mirror of
https://github.com/actions/stale.git
synced 2026-10-21 22:03:07 +01:00
Compare commits
4 Commits
dependabot
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4f635963e | ||
|
|
ba64a8c4cf | ||
|
|
3467b6485b | ||
|
|
f01e4de246 |
2
.licenses/npm/undici.dep.yml
generated
2
.licenses/npm/undici.dep.yml
generated
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: undici
|
||||
version: 6.28.0
|
||||
version: 6.29.0
|
||||
type: npm
|
||||
summary: An HTTP/1.1 client, written from scratch for Node.js
|
||||
homepage: https://undici.nodejs.org
|
||||
|
||||
12
README.md
12
README.md
@@ -110,6 +110,7 @@ Every argument is optional.
|
||||
| [ignore-pr-updates](#ignore-pr-updates) | Override [ignore-updates](#ignore-updates) for PRs only | |
|
||||
| [include-only-assigned](#include-only-assigned) | Process only assigned issues | `false` |
|
||||
| [sort-by](#sort-by) | What to sort issues and PRs by | `created` |
|
||||
| [exempt-issue-types](#exempt-issue-types) | Issue types on issues exempted from stale/closed. | |
|
||||
| [only-issue-types](#only-issue-types) | Only issues with a matching type are processed as stale/closed. | |
|
||||
|
||||
### List of output options
|
||||
@@ -567,6 +568,17 @@ Useful to sort the issues and PRs by the specified field. It accepts `created`,
|
||||
|
||||
Default value: `created`
|
||||
|
||||
#### exempt-issue-types
|
||||
|
||||
A comma separated list of issue types that can be assigned to issues to exclude them from being marked as stale
|
||||
(e.g: `Bug,Feature`)
|
||||
|
||||
If unset (or an empty string), this option will not alter the stale workflow.
|
||||
|
||||
If a type is listed in both `exempt-issue-types` and `only-issue-types`, `exempt-issue-types` takes precedence.
|
||||
|
||||
Default value: unset
|
||||
|
||||
#### only-issue-types
|
||||
|
||||
A comma separated list of allowed issue types. Only issues with a matching type will be processed (e.g.: `bug,question`).
|
||||
|
||||
203
__tests__/exempt-issue-types.spec.ts
Normal file
203
__tests__/exempt-issue-types.spec.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import {Issue} from '../src/classes/issue';
|
||||
import {IIssuesProcessorOptions} from '../src/interfaces/issues-processor-options';
|
||||
import {IssuesProcessorMock} from './classes/issues-processor-mock';
|
||||
import {DefaultProcessorOptions} from './constants/default-processor-options';
|
||||
import {generateIssue} from './functions/generate-issue';
|
||||
import {alwaysFalseStateMock} from './classes/state-mock';
|
||||
|
||||
describe('exempt-issue-types option', () => {
|
||||
test('should skip issues with an exempt type', async () => {
|
||||
const opts: IIssuesProcessorOptions = {
|
||||
...DefaultProcessorOptions,
|
||||
exemptIssueTypes: 'question,discussion'
|
||||
};
|
||||
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'A bug',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
'bug'
|
||||
),
|
||||
generateIssue(
|
||||
opts,
|
||||
2,
|
||||
'A question',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
'question'
|
||||
),
|
||||
generateIssue(
|
||||
opts,
|
||||
3,
|
||||
'A discussion',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
'discussion'
|
||||
)
|
||||
];
|
||||
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
await processor.processIssues(1);
|
||||
|
||||
// The exempt types should not be processed/marked stale
|
||||
expect(processor.staleIssues.map(i => i.title)).toEqual(['A bug']);
|
||||
});
|
||||
|
||||
test('should process all issues if exemptIssueTypes is unset', async () => {
|
||||
const opts: IIssuesProcessorOptions = {
|
||||
...DefaultProcessorOptions,
|
||||
exemptIssueTypes: ''
|
||||
};
|
||||
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'A bug',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
'bug'
|
||||
),
|
||||
generateIssue(
|
||||
opts,
|
||||
2,
|
||||
'A feature',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
'feature'
|
||||
)
|
||||
];
|
||||
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
await processor.processIssues(1);
|
||||
expect(processor.staleIssues.map(i => i.title)).toEqual([
|
||||
'A bug',
|
||||
'A feature'
|
||||
]);
|
||||
});
|
||||
|
||||
test('should take precedence over onlyIssueTypes', async () => {
|
||||
const opts: IIssuesProcessorOptions = {
|
||||
...DefaultProcessorOptions,
|
||||
exemptIssueTypes: 'bug',
|
||||
onlyIssueTypes: 'bug'
|
||||
};
|
||||
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'A bug',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
false,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
'bug'
|
||||
)
|
||||
];
|
||||
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
await processor.processIssues(1);
|
||||
expect(processor.staleIssues).toEqual([]);
|
||||
});
|
||||
|
||||
test('should ignore exemptIssueTypes filter when item is a pull request', async () => {
|
||||
const opts: IIssuesProcessorOptions = {
|
||||
...DefaultProcessorOptions,
|
||||
exemptIssueTypes: 'bug'
|
||||
};
|
||||
|
||||
const TestIssueList: Issue[] = [
|
||||
generateIssue(
|
||||
opts,
|
||||
1,
|
||||
'A pull request',
|
||||
'2020-01-01T17:00:00Z',
|
||||
'2020-01-01T17:00:00Z',
|
||||
false,
|
||||
true,
|
||||
[],
|
||||
false,
|
||||
false,
|
||||
undefined,
|
||||
[],
|
||||
'bug'
|
||||
)
|
||||
];
|
||||
|
||||
const processor = new IssuesProcessorMock(
|
||||
opts,
|
||||
alwaysFalseStateMock,
|
||||
async p => (p === 1 ? TestIssueList : []),
|
||||
async () => [],
|
||||
async () => new Date().toDateString()
|
||||
);
|
||||
|
||||
await processor.processIssues(1);
|
||||
expect(processor.staleIssues.map(i => i.title)).toEqual(['A pull request']);
|
||||
});
|
||||
});
|
||||
@@ -208,6 +208,10 @@ inputs:
|
||||
description: 'Only the issues or the pull requests with an assignee will be marked as stale automatically.'
|
||||
default: 'false'
|
||||
required: false
|
||||
exempt-issue-types:
|
||||
description: 'Issues with a matching type are exempt from being processed as stale/closed. Defaults to `[]` (disabled) and can be a comma-separated list of issue types.'
|
||||
default: ''
|
||||
required: false
|
||||
only-issue-types:
|
||||
description: 'Only issues with a matching type are processed as stale/closed. Defaults to `[]` (disabled) and can be a comma-separated list of issue types.'
|
||||
default: ''
|
||||
|
||||
657
dist/index.js
vendored
657
dist/index.js
vendored
@@ -16925,11 +16925,77 @@ class Request {
|
||||
}
|
||||
}
|
||||
|
||||
onUpgrade (statusCode, headers, socket) {
|
||||
/**
|
||||
* @param {number|null} statusCode
|
||||
* @param {Buffer[]|null} headers
|
||||
* @param {import('node:stream').Duplex} socket
|
||||
* @param {string} [statusText]
|
||||
*/
|
||||
onUpgrade (statusCode, headers, socket, statusText = '') {
|
||||
this.onFinally()
|
||||
|
||||
assert(!this.aborted)
|
||||
assert(!this.completed)
|
||||
|
||||
return this[kHandler].onUpgrade(statusCode, headers, socket)
|
||||
if (statusCode !== null) {
|
||||
this.#publishUpgradeHeaders(statusCode, headers, statusText)
|
||||
}
|
||||
|
||||
const result = this[kHandler].onUpgrade(statusCode, headers, socket)
|
||||
|
||||
if (!this.aborted) {
|
||||
this.completed = true
|
||||
if (statusCode !== null) {
|
||||
this.#publishUpgradeTrailers()
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} statusCode
|
||||
* @param {import('node:http2').IncomingHttpHeaders} headers
|
||||
* @param {(headers: import('node:http2').IncomingHttpHeaders) => Buffer[]} parseHeaders
|
||||
* @param {string} [statusText]
|
||||
*/
|
||||
onUpgradeResponse (statusCode, headers, parseHeaders, statusText = '') {
|
||||
assert(!this.aborted)
|
||||
assert(this.completed)
|
||||
|
||||
if (channels.headers.hasSubscribers) {
|
||||
this.#publishUpgradeHeaders(statusCode, parseHeaders(headers), statusText)
|
||||
}
|
||||
this.#publishUpgradeTrailers()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Error} error
|
||||
*/
|
||||
onUpgradeError (error) {
|
||||
assert(!this.aborted)
|
||||
assert(this.completed)
|
||||
|
||||
if (channels.error.hasSubscribers) {
|
||||
channels.error.publish({ request: this, error })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} statusCode
|
||||
* @param {Buffer[]} headers
|
||||
* @param {string} statusText
|
||||
*/
|
||||
#publishUpgradeHeaders (statusCode, headers, statusText) {
|
||||
if (channels.headers.hasSubscribers) {
|
||||
channels.headers.publish({ request: this, response: { statusCode, headers, statusText } })
|
||||
}
|
||||
}
|
||||
|
||||
#publishUpgradeTrailers () {
|
||||
if (channels.trailers.hasSubscribers) {
|
||||
channels.trailers.publish({ request: this, trailers: [] })
|
||||
}
|
||||
}
|
||||
|
||||
onComplete (trailers) {
|
||||
@@ -18828,7 +18894,7 @@ class Parser {
|
||||
}
|
||||
|
||||
onUpgrade (head) {
|
||||
const { upgrade, client, socket, headers, statusCode } = this
|
||||
const { upgrade, client, socket, headers, statusCode, statusText } = this
|
||||
|
||||
assert(upgrade)
|
||||
assert(client[kSocket] === socket)
|
||||
@@ -18863,9 +18929,10 @@ class Parser {
|
||||
client.emit('disconnect', client[kUrl], [client], new InformationalError('upgrade'))
|
||||
|
||||
try {
|
||||
request.onUpgrade(statusCode, headers, socket)
|
||||
} catch (err) {
|
||||
util.destroy(socket, err)
|
||||
request.onUpgrade(statusCode, headers, socket, statusText)
|
||||
} catch (error) {
|
||||
util.errorRequest(client, request, error)
|
||||
util.destroy(socket, error)
|
||||
}
|
||||
|
||||
client[kResume]()
|
||||
@@ -19272,7 +19339,7 @@ async function connectH1 (client, socket) {
|
||||
|
||||
function clearIdleSocketValidation (socket) {
|
||||
if (socket[kIdleSocketValidationTimeout]) {
|
||||
clearTimeout(socket[kIdleSocketValidationTimeout])
|
||||
clearImmediate(socket[kIdleSocketValidationTimeout])
|
||||
socket[kIdleSocketValidationTimeout] = null
|
||||
}
|
||||
|
||||
@@ -19281,15 +19348,23 @@ function clearIdleSocketValidation (socket) {
|
||||
|
||||
function scheduleIdleSocketValidation (client, socket) {
|
||||
socket[kIdleSocketValidation] = 1
|
||||
socket[kIdleSocketValidationTimeout] = setTimeout(() => {
|
||||
// Yield to the check phase (after poll) so unsolicited bytes / FIN / RST
|
||||
// already pending on this idle keep-alive socket are processed before the
|
||||
// next request is written (GHSA-35p6-xmwp-9g52).
|
||||
//
|
||||
// setTimeout(0) pays Node's ~1ms timer floor on every sequential reuse
|
||||
// (#5493). setImmediate avoids that, but an *unref'd* Immediate lets poll
|
||||
// block for ~500ms when the event loop is otherwise idle (#5600 / #5606).
|
||||
// A ref'd Immediate both keeps the pending request alive and makes poll
|
||||
// return immediately — the hybrid those issues asked for.
|
||||
socket[kIdleSocketValidationTimeout] = setImmediate(() => {
|
||||
socket[kIdleSocketValidationTimeout] = null
|
||||
socket[kIdleSocketValidation] = 2
|
||||
|
||||
if (client[kSocket] === socket && !socket.destroyed) {
|
||||
client[kResume]()
|
||||
}
|
||||
}, 0)
|
||||
socket[kIdleSocketValidationTimeout].unref?.()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -19438,12 +19513,22 @@ function writeH1 (client, request) {
|
||||
const socket = client[kSocket]
|
||||
clearIdleSocketValidation(socket)
|
||||
|
||||
const abort = (err) => {
|
||||
if (request.aborted || request.completed) {
|
||||
/**
|
||||
* @param {Error} [error]
|
||||
*/
|
||||
const abort = (error) => {
|
||||
if (request.aborted) {
|
||||
return
|
||||
}
|
||||
|
||||
util.errorRequest(client, request, err || new RequestAbortedError())
|
||||
if (request.completed) {
|
||||
if (request.upgrade || request.method === 'CONNECT') {
|
||||
util.destroy(socket, new InformationalError('aborted'))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
util.errorRequest(client, request, error || new RequestAbortedError())
|
||||
|
||||
util.destroy(body)
|
||||
util.destroy(socket, new InformationalError('aborted'))
|
||||
@@ -19900,6 +19985,7 @@ module.exports = connectH1
|
||||
|
||||
|
||||
const assert = __nccwpck_require__(4589)
|
||||
const { errorMonitor } = __nccwpck_require__(8474)
|
||||
const { pipeline } = __nccwpck_require__(7075)
|
||||
const util = __nccwpck_require__(3440)
|
||||
const {
|
||||
@@ -19976,6 +20062,15 @@ function parseH2Headers (headers) {
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('node:http2').IncomingHttpHeaders} headers
|
||||
* @returns {Buffer[]}
|
||||
*/
|
||||
function parseH2ResponseHeaders (headers) {
|
||||
const { [HTTP2_HEADER_STATUS]: _statusCode, ...realHeaders } = headers
|
||||
return parseH2Headers(realHeaders)
|
||||
}
|
||||
|
||||
async function connectH2 (client, socket) {
|
||||
client[kSocket] = socket
|
||||
|
||||
@@ -20196,22 +20291,32 @@ function writeH2 (client, request) {
|
||||
headers[HTTP2_HEADER_AUTHORITY] = host || `${hostname}${port ? `:${port}` : ''}`
|
||||
headers[HTTP2_HEADER_METHOD] = method
|
||||
|
||||
const abort = (err) => {
|
||||
if (request.aborted || request.completed) {
|
||||
/**
|
||||
* @param {Error} [error]
|
||||
*/
|
||||
const abort = (error) => {
|
||||
if (request.aborted) {
|
||||
return
|
||||
}
|
||||
|
||||
err = err || new RequestAbortedError()
|
||||
if (request.completed) {
|
||||
if (method === 'CONNECT' && stream != null) {
|
||||
util.destroy(stream, error || new RequestAbortedError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
util.errorRequest(client, request, err)
|
||||
error = error || new RequestAbortedError()
|
||||
|
||||
util.errorRequest(client, request, error)
|
||||
|
||||
if (stream != null) {
|
||||
util.destroy(stream, err)
|
||||
util.destroy(stream, error)
|
||||
}
|
||||
|
||||
// We do not destroy the socket as we can continue using the session
|
||||
// the stream get's destroyed and the session remains to create new streams
|
||||
util.destroy(body, err)
|
||||
util.destroy(body, error)
|
||||
client[kQueue][client[kRunningIdx]++] = null
|
||||
client[kResume]()
|
||||
}
|
||||
@@ -20230,25 +20335,57 @@ function writeH2 (client, request) {
|
||||
|
||||
if (method === 'CONNECT') {
|
||||
session.ref()
|
||||
// We are already connected, streams are pending, first request
|
||||
// will create a new stream. We trigger a request to create the stream and wait until
|
||||
// `ready` event is triggered
|
||||
// We disabled endStream to allow the user to write to the stream
|
||||
stream = session.request(headers, { endStream: false, signal })
|
||||
let upgradeResponseFinished = false
|
||||
|
||||
if (stream.id && !stream.pending) {
|
||||
request.onUpgrade(null, null, stream)
|
||||
++session[kOpenStreams]
|
||||
client[kQueue][client[kRunningIdx]++] = null
|
||||
} else {
|
||||
stream.once('ready', () => {
|
||||
request.onUpgrade(null, null, stream)
|
||||
++session[kOpenStreams]
|
||||
client[kQueue][client[kRunningIdx]++] = null
|
||||
})
|
||||
/**
|
||||
* @param {import('node:http2').IncomingHttpHeaders} headers
|
||||
*/
|
||||
const onResponse = (headers) => {
|
||||
upgradeResponseFinished = true
|
||||
stream.off(errorMonitor, onUpgradeError)
|
||||
request.onUpgradeResponse(Number(headers[HTTP2_HEADER_STATUS]), headers, parseH2ResponseHeaders)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Error} error
|
||||
*/
|
||||
const onUpgradeError = (error) => {
|
||||
upgradeResponseFinished = true
|
||||
stream.off('response', onResponse)
|
||||
request.onUpgradeError(error)
|
||||
}
|
||||
|
||||
const onReady = () => {
|
||||
try {
|
||||
request.onUpgrade(null, null, stream)
|
||||
} catch (error) {
|
||||
stream.off('response', onResponse)
|
||||
abort(error)
|
||||
return
|
||||
}
|
||||
|
||||
if (request.aborted) {
|
||||
return
|
||||
}
|
||||
|
||||
stream.off('error', abort)
|
||||
stream.once(errorMonitor, onUpgradeError)
|
||||
client[kQueue][client[kRunningIdx]++] = null
|
||||
}
|
||||
|
||||
stream.once('response', onResponse)
|
||||
stream.once('error', abort)
|
||||
++session[kOpenStreams]
|
||||
onReady()
|
||||
|
||||
stream.once('close', () => {
|
||||
if (!upgradeResponseFinished && request.completed) {
|
||||
stream.off('response', onResponse)
|
||||
stream.off(errorMonitor, onUpgradeError)
|
||||
request.onUpgradeError(new InformationalError(`HTTP/2: "stream error" received - code ${stream.rstCode}`))
|
||||
}
|
||||
session[kOpenStreams] -= 1
|
||||
if (session[kOpenStreams] === 0) session.unref()
|
||||
})
|
||||
@@ -22947,6 +23084,7 @@ class RetryHandler {
|
||||
this.end = null
|
||||
this.etag = null
|
||||
this.resume = null
|
||||
this.headersSent = false
|
||||
|
||||
// Handle possible onConnect duplication
|
||||
this.handler.onConnect(reason => {
|
||||
@@ -22959,6 +23097,20 @@ class RetryHandler {
|
||||
})
|
||||
}
|
||||
|
||||
checkpointResponseEnd (headers, resume) {
|
||||
if (this.end == null && this.opts.method !== 'HEAD') {
|
||||
const contentLength = headers['content-length']
|
||||
this.end = contentLength != null ? Number(contentLength) - 1 : null
|
||||
|
||||
assert(
|
||||
this.end == null || Number.isFinite(this.end),
|
||||
'invalid content-length'
|
||||
)
|
||||
}
|
||||
|
||||
this.resume = this.end != null ? resume : null
|
||||
}
|
||||
|
||||
onRequestSent () {
|
||||
if (this.handler.onRequestSent) {
|
||||
this.handler.onRequestSent()
|
||||
@@ -23047,7 +23199,12 @@ class RetryHandler {
|
||||
this.retryCount += 1
|
||||
|
||||
if (statusCode >= 300) {
|
||||
if (this.retryOpts.statusCodes.includes(statusCode) === false) {
|
||||
// Only expose a response if no earlier attempt has reached the caller.
|
||||
// Otherwise abort this attempt so the error settles the existing body
|
||||
// instead of replacing it with a new response.
|
||||
if (!this.headersSent && this.retryOpts.statusCodes.includes(statusCode) === false) {
|
||||
this.headersSent = true
|
||||
this.checkpointResponseEnd(headers, resume)
|
||||
return this.handler.onHeaders(
|
||||
statusCode,
|
||||
rawHeaders,
|
||||
@@ -23116,8 +23273,15 @@ class RetryHandler {
|
||||
|
||||
const { start, size, end = size - 1 } = contentRange
|
||||
|
||||
assert(this.start === start, 'content-range mismatch')
|
||||
assert(this.end == null || this.end === end, 'content-range mismatch')
|
||||
if (this.start !== start || (this.end != null && this.end !== end)) {
|
||||
this.abort(
|
||||
new RequestRetryError('Content-Range mismatch', statusCode, {
|
||||
headers,
|
||||
data: { count: this.retryCount }
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
this.resume = resume
|
||||
return true
|
||||
@@ -23129,6 +23293,7 @@ class RetryHandler {
|
||||
const range = parseRangeHeader(headers['content-range'])
|
||||
|
||||
if (range == null) {
|
||||
this.headersSent = true
|
||||
return this.handler.onHeaders(
|
||||
statusCode,
|
||||
rawHeaders,
|
||||
@@ -23167,6 +23332,7 @@ class RetryHandler {
|
||||
)
|
||||
|
||||
this.resume = resume
|
||||
this.headersSent = true
|
||||
this.etag = headers.etag != null ? headers.etag : null
|
||||
|
||||
// Weak etags are not useful for comparison nor cache
|
||||
@@ -23206,7 +23372,7 @@ class RetryHandler {
|
||||
}
|
||||
|
||||
onError (err) {
|
||||
if (this.aborted || isDisturbed(this.opts.body)) {
|
||||
if (this.aborted || isDisturbed(this.opts.body) || (this.headersSent && this.resume == null)) {
|
||||
return this.handler.onError(err)
|
||||
}
|
||||
|
||||
@@ -27664,6 +27830,49 @@ const COLON = 0x3A
|
||||
*/
|
||||
const SPACE = 0x20
|
||||
|
||||
const DATA = Buffer.from('data')
|
||||
const EVENT = Buffer.from('event')
|
||||
const ID = Buffer.from('id')
|
||||
const RETRY = Buffer.from('retry')
|
||||
|
||||
function isASCIINumberBytes (buffer, start) {
|
||||
if (start >= buffer.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (let i = start; i < buffer.length; i++) {
|
||||
if (buffer[i] < 0x30 || buffer[i] > 0x39) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function isValidLastEventIdBytes (buffer, start) {
|
||||
for (let i = start; i < buffer.length; i++) {
|
||||
if (buffer[i] === 0x00) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function isFieldName (line, length, field) {
|
||||
if (length !== field.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (line[i] !== field[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} EventSourceStreamEvent
|
||||
* @type {object}
|
||||
@@ -27704,11 +27913,14 @@ class EventSourceStream extends Transform {
|
||||
eventEndCheck = false
|
||||
|
||||
/**
|
||||
* @type {Buffer}
|
||||
* @type {Buffer[]}
|
||||
*/
|
||||
buffer = null
|
||||
chunks = []
|
||||
|
||||
chunkIndex = 0
|
||||
pos = 0
|
||||
lineChunkIndex = 0
|
||||
linePos = 0
|
||||
|
||||
event = {
|
||||
data: undefined,
|
||||
@@ -27747,92 +27959,20 @@ class EventSourceStream extends Transform {
|
||||
return
|
||||
}
|
||||
|
||||
// Cache the chunk in the buffer, as the data might not be complete while
|
||||
// processing it
|
||||
// TODO: Investigate if there is a more performant way to handle
|
||||
// incoming chunks
|
||||
// see: https://github.com/nodejs/undici/issues/2630
|
||||
if (this.buffer) {
|
||||
this.buffer = Buffer.concat([this.buffer, chunk])
|
||||
} else {
|
||||
this.buffer = chunk
|
||||
}
|
||||
this.chunks.push(chunk)
|
||||
|
||||
// Strip leading byte-order-mark if we opened the stream and started
|
||||
// the processing of the incoming data
|
||||
if (this.checkBOM) {
|
||||
switch (this.buffer.length) {
|
||||
case 1:
|
||||
// Check if the first byte is the same as the first byte of the BOM
|
||||
if (this.buffer[0] === BOM[0]) {
|
||||
// If it is, we need to wait for more data
|
||||
callback()
|
||||
return
|
||||
}
|
||||
// Set the checkBOM flag to false as we don't need to check for the
|
||||
// BOM anymore
|
||||
this.checkBOM = false
|
||||
|
||||
// The buffer only contains one byte so we need to wait for more data
|
||||
callback()
|
||||
return
|
||||
case 2:
|
||||
// Check if the first two bytes are the same as the first two bytes
|
||||
// of the BOM
|
||||
if (
|
||||
this.buffer[0] === BOM[0] &&
|
||||
this.buffer[1] === BOM[1]
|
||||
) {
|
||||
// If it is, we need to wait for more data, because the third byte
|
||||
// is needed to determine if it is the BOM or not
|
||||
callback()
|
||||
return
|
||||
}
|
||||
|
||||
// Set the checkBOM flag to false as we don't need to check for the
|
||||
// BOM anymore
|
||||
this.checkBOM = false
|
||||
break
|
||||
case 3:
|
||||
// Check if the first three bytes are the same as the first three
|
||||
// bytes of the BOM
|
||||
if (
|
||||
this.buffer[0] === BOM[0] &&
|
||||
this.buffer[1] === BOM[1] &&
|
||||
this.buffer[2] === BOM[2]
|
||||
) {
|
||||
// If it is, we can drop the buffered data, as it is only the BOM
|
||||
this.buffer = Buffer.alloc(0)
|
||||
// Set the checkBOM flag to false as we don't need to check for the
|
||||
// BOM anymore
|
||||
this.checkBOM = false
|
||||
|
||||
// Await more data
|
||||
callback()
|
||||
return
|
||||
}
|
||||
// If it is not the BOM, we can start processing the data
|
||||
this.checkBOM = false
|
||||
break
|
||||
default:
|
||||
// The buffer is longer than 3 bytes, so we can drop the BOM if it is
|
||||
// present
|
||||
if (
|
||||
this.buffer[0] === BOM[0] &&
|
||||
this.buffer[1] === BOM[1] &&
|
||||
this.buffer[2] === BOM[2]
|
||||
) {
|
||||
// Remove the BOM from the buffer
|
||||
this.buffer = this.buffer.subarray(3)
|
||||
}
|
||||
|
||||
// Set the checkBOM flag to false as we don't need to check for the
|
||||
this.checkBOM = false
|
||||
break
|
||||
if (this.handleBOM()) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
while (this.pos < this.buffer.length) {
|
||||
while (this.hasCurrentByte()) {
|
||||
const byte = this.currentByte()
|
||||
|
||||
// If the previous line ended with an end-of-line, we need to check
|
||||
// if the next character is also an end-of-line.
|
||||
if (this.eventEndCheck) {
|
||||
@@ -27845,10 +27985,9 @@ class EventSourceStream extends Transform {
|
||||
if (this.crlfCheck) {
|
||||
// If the current character is a line feed, we can remove it
|
||||
// from the buffer and reset the crlfCheck flag
|
||||
if (this.buffer[this.pos] === LF) {
|
||||
this.buffer = this.buffer.subarray(this.pos + 1)
|
||||
this.pos = 0
|
||||
if (byte === LF) {
|
||||
this.crlfCheck = false
|
||||
this.consumeCurrentByte()
|
||||
|
||||
// It is possible that the line feed is not the end of the
|
||||
// event. We need to check if the next character is an
|
||||
@@ -27864,19 +28003,17 @@ class EventSourceStream extends Transform {
|
||||
this.crlfCheck = false
|
||||
}
|
||||
|
||||
if (this.buffer[this.pos] === LF || this.buffer[this.pos] === CR) {
|
||||
if (byte === LF || byte === CR) {
|
||||
// If the current character is a carriage return, we need to
|
||||
// set the crlfCheck flag to true, as we need to check if the
|
||||
// next character is a line feed so we can remove it from the
|
||||
// buffer
|
||||
if (this.buffer[this.pos] === CR) {
|
||||
if (byte === CR) {
|
||||
this.crlfCheck = true
|
||||
}
|
||||
|
||||
this.buffer = this.buffer.subarray(this.pos + 1)
|
||||
this.pos = 0
|
||||
if (
|
||||
this.event.data !== undefined || this.event.event || this.event.id || this.event.retry) {
|
||||
this.consumeCurrentByte()
|
||||
if (this.hasPendingEvent()) {
|
||||
this.processEvent(this.event)
|
||||
}
|
||||
this.clearEvent()
|
||||
@@ -27890,22 +28027,18 @@ class EventSourceStream extends Transform {
|
||||
|
||||
// If the current character is an end-of-line, we can process the
|
||||
// line
|
||||
if (this.buffer[this.pos] === LF || this.buffer[this.pos] === CR) {
|
||||
if (byte === LF || byte === CR) {
|
||||
// If the current character is a carriage return, we need to
|
||||
// set the crlfCheck flag to true, as we need to check if the
|
||||
// next character is a line feed
|
||||
if (this.buffer[this.pos] === CR) {
|
||||
if (byte === CR) {
|
||||
this.crlfCheck = true
|
||||
}
|
||||
|
||||
// In any case, we can process the line as we reached an
|
||||
// end-of-line character
|
||||
this.parseLine(this.buffer.subarray(0, this.pos), this.event)
|
||||
|
||||
// Remove the processed line from the buffer
|
||||
this.buffer = this.buffer.subarray(this.pos + 1)
|
||||
// Reset the position as we removed the processed line from the buffer
|
||||
this.pos = 0
|
||||
this.parseLine(this.readLine(), this.event)
|
||||
this.consumeCurrentByte()
|
||||
// A line was processed and this could be the end of the event. We need
|
||||
// to check if the next line is empty to determine if the event is
|
||||
// finished.
|
||||
@@ -27913,7 +28046,7 @@ class EventSourceStream extends Transform {
|
||||
continue
|
||||
}
|
||||
|
||||
this.pos++
|
||||
this.advanceCursor()
|
||||
}
|
||||
|
||||
callback()
|
||||
@@ -27938,64 +28071,53 @@ class EventSourceStream extends Transform {
|
||||
return
|
||||
}
|
||||
|
||||
let field = ''
|
||||
let value = ''
|
||||
let fieldLength = line.length
|
||||
let valueStart = line.length
|
||||
|
||||
// If the line contains a U+003A COLON character (:)
|
||||
if (colonPosition !== -1) {
|
||||
// Collect the characters on the line before the first U+003A COLON
|
||||
// character (:), and let field be that string.
|
||||
// TODO: Investigate if there is a more performant way to extract the
|
||||
// field
|
||||
// see: https://github.com/nodejs/undici/issues/2630
|
||||
field = line.subarray(0, colonPosition).toString('utf8')
|
||||
fieldLength = colonPosition
|
||||
|
||||
// Collect the characters on the line after the first U+003A COLON
|
||||
// character (:), and let value be that string.
|
||||
// If value starts with a U+0020 SPACE character, remove it from value.
|
||||
let valueStart = colonPosition + 1
|
||||
valueStart = colonPosition + 1
|
||||
if (line[valueStart] === SPACE) {
|
||||
++valueStart
|
||||
}
|
||||
// TODO: Investigate if there is a more performant way to extract the
|
||||
// value
|
||||
// see: https://github.com/nodejs/undici/issues/2630
|
||||
value = line.subarray(valueStart).toString('utf8')
|
||||
|
||||
// Otherwise, the string is not empty but does not contain a U+003A COLON
|
||||
// character (:)
|
||||
} else {
|
||||
// Process the field using the steps described below, using the whole
|
||||
// line as the field name, and the empty string as the field value.
|
||||
field = line.toString('utf8')
|
||||
value = ''
|
||||
}
|
||||
|
||||
// Modify the event with the field name and value. The value is also
|
||||
// decoded as UTF-8
|
||||
switch (field) {
|
||||
case 'data':
|
||||
if (event[field] === undefined) {
|
||||
event[field] = value
|
||||
} else {
|
||||
event[field] += `\n${value}`
|
||||
}
|
||||
break
|
||||
case 'retry':
|
||||
if (isASCIINumber(value)) {
|
||||
event[field] = value
|
||||
}
|
||||
break
|
||||
case 'id':
|
||||
if (isValidLastEventId(value)) {
|
||||
event[field] = value
|
||||
}
|
||||
break
|
||||
case 'event':
|
||||
if (value.length > 0) {
|
||||
event[field] = value
|
||||
}
|
||||
break
|
||||
if (isFieldName(line, fieldLength, DATA)) {
|
||||
const value = line.toString('utf8', valueStart)
|
||||
|
||||
if (event.data === undefined) {
|
||||
event.data = value
|
||||
} else {
|
||||
event.data += `\n${value}`
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (isFieldName(line, fieldLength, RETRY)) {
|
||||
if (isASCIINumberBytes(line, valueStart)) {
|
||||
event.retry = line.toString('utf8', valueStart)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (isFieldName(line, fieldLength, ID)) {
|
||||
if (isValidLastEventIdBytes(line, valueStart)) {
|
||||
event.id = line.toString('utf8', valueStart)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (isFieldName(line, fieldLength, EVENT)) {
|
||||
const value = line.toString('utf8', valueStart)
|
||||
|
||||
if (value.length > 0) {
|
||||
event.event = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28025,13 +28147,152 @@ class EventSourceStream extends Transform {
|
||||
}
|
||||
|
||||
clearEvent () {
|
||||
this.event = {
|
||||
data: undefined,
|
||||
event: undefined,
|
||||
id: undefined,
|
||||
retry: undefined
|
||||
this.event.data = undefined
|
||||
this.event.event = undefined
|
||||
this.event.id = undefined
|
||||
this.event.retry = undefined
|
||||
}
|
||||
|
||||
hasPendingEvent () {
|
||||
return this.event.data !== undefined ||
|
||||
this.event.event !== undefined ||
|
||||
this.event.id !== undefined ||
|
||||
this.event.retry !== undefined
|
||||
}
|
||||
|
||||
hasCurrentByte () {
|
||||
return this.chunkIndex < this.chunks.length &&
|
||||
this.pos < this.chunks[this.chunkIndex].length
|
||||
}
|
||||
|
||||
currentByte () {
|
||||
return this.chunks[this.chunkIndex][this.pos]
|
||||
}
|
||||
|
||||
consumeCurrentByte () {
|
||||
this.advanceCursor()
|
||||
this.syncLineStartToCursor()
|
||||
}
|
||||
|
||||
advanceCursor () {
|
||||
this.pos++
|
||||
|
||||
while (this.chunkIndex < this.chunks.length && this.pos >= this.chunks[this.chunkIndex].length) {
|
||||
this.chunkIndex++
|
||||
this.pos = 0
|
||||
}
|
||||
}
|
||||
|
||||
syncLineStartToCursor () {
|
||||
this.lineChunkIndex = this.chunkIndex
|
||||
this.linePos = this.pos
|
||||
this.dropConsumedChunks()
|
||||
}
|
||||
|
||||
dropConsumedChunks () {
|
||||
while (this.lineChunkIndex > 0) {
|
||||
this.chunks.shift()
|
||||
this.lineChunkIndex--
|
||||
this.chunkIndex--
|
||||
}
|
||||
|
||||
if (this.chunkIndex === this.chunks.length) {
|
||||
this.chunks.length = 0
|
||||
this.chunkIndex = 0
|
||||
this.pos = 0
|
||||
this.lineChunkIndex = 0
|
||||
this.linePos = 0
|
||||
}
|
||||
}
|
||||
|
||||
readLine () {
|
||||
if (this.lineChunkIndex === this.chunkIndex) {
|
||||
return this.chunks[this.chunkIndex].subarray(this.linePos, this.pos)
|
||||
}
|
||||
|
||||
const chunks = []
|
||||
let length = 0
|
||||
|
||||
for (let i = this.lineChunkIndex; i <= this.chunkIndex; i++) {
|
||||
const chunk = this.chunks[i]
|
||||
const start = i === this.lineChunkIndex ? this.linePos : 0
|
||||
const end = i === this.chunkIndex ? this.pos : chunk.length
|
||||
const slice = chunk.subarray(start, end)
|
||||
length += slice.length
|
||||
chunks.push(slice)
|
||||
}
|
||||
|
||||
return Buffer.concat(chunks, length)
|
||||
}
|
||||
|
||||
peekBufferedByte (offset) {
|
||||
let chunkIndex = this.lineChunkIndex
|
||||
let pos = this.linePos
|
||||
|
||||
while (chunkIndex < this.chunks.length) {
|
||||
const chunk = this.chunks[chunkIndex]
|
||||
const remaining = chunk.length - pos
|
||||
|
||||
if (offset < remaining) {
|
||||
return chunk[pos + offset]
|
||||
}
|
||||
|
||||
offset -= remaining
|
||||
chunkIndex++
|
||||
pos = 0
|
||||
}
|
||||
}
|
||||
|
||||
discardLeadingBytes (count) {
|
||||
while (count > 0 && this.lineChunkIndex < this.chunks.length) {
|
||||
const chunk = this.chunks[this.lineChunkIndex]
|
||||
const remaining = chunk.length - this.linePos
|
||||
|
||||
if (count < remaining) {
|
||||
this.linePos += count
|
||||
count = 0
|
||||
} else {
|
||||
count -= remaining
|
||||
this.lineChunkIndex++
|
||||
this.linePos = 0
|
||||
}
|
||||
}
|
||||
|
||||
this.chunkIndex = this.lineChunkIndex
|
||||
this.pos = this.linePos
|
||||
this.dropConsumedChunks()
|
||||
}
|
||||
|
||||
handleBOM () {
|
||||
const first = this.peekBufferedByte(0)
|
||||
const second = this.peekBufferedByte(1)
|
||||
const third = this.peekBufferedByte(2)
|
||||
|
||||
if (second === undefined) {
|
||||
if (first === BOM[0]) {
|
||||
return true
|
||||
}
|
||||
|
||||
this.checkBOM = false
|
||||
return true
|
||||
}
|
||||
|
||||
if (third === undefined) {
|
||||
if (first === BOM[0] && second === BOM[1]) {
|
||||
return true
|
||||
}
|
||||
|
||||
this.checkBOM = false
|
||||
return false
|
||||
}
|
||||
|
||||
if (first === BOM[0] && second === BOM[1] && third === BOM[2]) {
|
||||
this.discardLeadingBytes(3)
|
||||
}
|
||||
|
||||
this.checkBOM = false
|
||||
return !this.hasCurrentByte()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -39299,7 +39560,7 @@ function establishWebSocketConnection (url, protocols, client, ws, onEstablish,
|
||||
// is specified, the server needs to include the same field and one of
|
||||
// the selected subprotocol values in its response for the connection to
|
||||
// be established.
|
||||
if (!requestProtocols.includes(secProtocol)) {
|
||||
if (requestProtocols === null || !requestProtocols.includes(secProtocol)) {
|
||||
failWebsocketConnection(ws, 'Protocol was not set in the opening handshake.')
|
||||
return
|
||||
}
|
||||
@@ -40060,7 +40321,12 @@ class PerMessageDeflate {
|
||||
|
||||
if (this.#maxPayloadSize > 0 && this.#inflate[kLength] > this.#maxPayloadSize) {
|
||||
callback(new MessageSizeExceededError())
|
||||
// The inflater may still hold buffered input that can emit a late
|
||||
// zlib error. Remove the data listener, then deterministically stop
|
||||
// the stream so a subsequent 'error' cannot fire without a listener
|
||||
// (which would terminate the process as an unhandled error event).
|
||||
this.#inflate.removeAllListeners()
|
||||
this.#inflate.destroy()
|
||||
this.#inflate = null
|
||||
return
|
||||
}
|
||||
@@ -49743,6 +50009,7 @@ var Option;
|
||||
Option["IgnorePrUpdates"] = "ignore-pr-updates";
|
||||
Option["ExemptDraftPr"] = "exempt-draft-pr";
|
||||
Option["CloseIssueReason"] = "close-issue-reason";
|
||||
Option["ExemptIssueTypes"] = "exempt-issue-types";
|
||||
Option["OnlyIssueTypes"] = "only-issue-types";
|
||||
})(Option || (Option = {}));
|
||||
|
||||
@@ -51978,6 +52245,19 @@ class IssuesProcessor {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// exemptIssueTypes wins if both it and onlyIssueTypes are specified
|
||||
if (this.options.exemptIssueTypes && !issue.isPullRequest) {
|
||||
const exemptTypes = this.options.exemptIssueTypes
|
||||
.split(',')
|
||||
.map(t => t.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
const issueType = (issue.issue_type || '').toLowerCase();
|
||||
if (exemptTypes.includes(issueType)) {
|
||||
issueLogger.info(`Skipping this $$type because its type ('${issue.issue_type}') is in exemptIssueTypes (${exemptTypes.join(', ')})`);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const onlyLabels = wordsToList(this._getOnlyLabels(issue));
|
||||
if (onlyLabels.length > 0) {
|
||||
issueLogger.info(`The option ${issueLogger.createOptionLink(Option.OnlyLabels)} was specified to only process issues and pull requests with all those labels (${LoggerService.cyan(onlyLabels.length)})`);
|
||||
@@ -106904,7 +107184,8 @@ function _getAndValidateArgs() {
|
||||
exemptDraftPr: getInput('exempt-draft-pr') === 'true',
|
||||
closeIssueReason: getInput('close-issue-reason'),
|
||||
includeOnlyAssigned: getInput('include-only-assigned') === 'true',
|
||||
onlyIssueTypes: getInput('only-issue-types')
|
||||
onlyIssueTypes: getInput('only-issue-types'),
|
||||
exemptIssueTypes: getInput('exempt-issue-types')
|
||||
};
|
||||
for (const numberInput of ['days-before-stale']) {
|
||||
if (isNaN(parseFloat(getInput(numberInput)))) {
|
||||
|
||||
66
package-lock.json
generated
66
package-lock.json
generated
@@ -29,7 +29,7 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jest": "^29.15.4",
|
||||
"eslint-plugin-n": "^18.2.1",
|
||||
"globals": "^17.11.0",
|
||||
"globals": "^17.7.0",
|
||||
"jest": "^30.4.2",
|
||||
"jest-silent-reporter": "^0.6.0",
|
||||
"prettier": "^3.9.4",
|
||||
@@ -3679,9 +3679,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/baseline-browser-mapping": {
|
||||
"version": "2.10.41",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.41.tgz",
|
||||
"integrity": "sha512-WwS7MHhqGHHlaVsqRZnhvCEMS0owDX+SxRlve7JkuH7My1Ara3ZriTmCQupPfYjxMZ8I/tgxtJYr2t7taHaH4A==",
|
||||
"version": "2.11.25",
|
||||
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.25.tgz",
|
||||
"integrity": "sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@@ -3729,9 +3729,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.28.4",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz",
|
||||
"integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==",
|
||||
"version": "4.29.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.0.tgz",
|
||||
"integrity": "sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3749,11 +3749,11 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.10.38",
|
||||
"caniuse-lite": "^1.0.30001799",
|
||||
"electron-to-chromium": "^1.5.376",
|
||||
"node-releases": "^2.0.48",
|
||||
"update-browserslist-db": "^1.2.3"
|
||||
"baseline-browser-mapping": "^2.11.23",
|
||||
"caniuse-lite": "^1.0.30001810",
|
||||
"electron-to-chromium": "^1.5.427",
|
||||
"node-releases": "^2.0.55",
|
||||
"update-browserslist-db": "^1.3.3"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
@@ -3827,9 +3827,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001800",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz",
|
||||
"integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==",
|
||||
"version": "1.0.30001810",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
|
||||
"integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4521,9 +4521,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.385",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.385.tgz",
|
||||
"integrity": "sha512-78sa/M08MNAYHQfjoWMvOlKQqZ0ElhSm/L5HNUc96VZ3b+KvDVnngFm8sYQy0XrhTRgAhggHr5abA7yTvRdo4Q==",
|
||||
"version": "1.5.433",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.433.tgz",
|
||||
"integrity": "sha512-5lCAbyZBjtmUt/RAGHRqrL2q0oEFRThDAsZHHDn9XHa89Qw7gMYOeSicBTy+AHfvo0r6vwsZvqNJTQIQy1BLzA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
@@ -5491,9 +5491,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "17.11.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.11.0.tgz",
|
||||
"integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==",
|
||||
"version": "17.7.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz",
|
||||
"integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -7600,9 +7600,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "3.15.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
|
||||
"integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
|
||||
"version": "3.15.2",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz",
|
||||
"integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -8222,9 +8222,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.50",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz",
|
||||
"integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==",
|
||||
"version": "2.0.56",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.56.tgz",
|
||||
"integrity": "sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
@@ -9754,9 +9754,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "6.28.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz",
|
||||
"integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==",
|
||||
"version": "6.29.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.29.0.tgz",
|
||||
"integrity": "sha512-R+RODBqp6i2pPflGdq+xIOUkl+RNfGgHwoinecKu/JCuf2uO06cOKoDbI2P7Dn6KcswdKwrczbU6IYJ6K8X+wg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18.17"
|
||||
@@ -9814,9 +9814,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz",
|
||||
"integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-jest": "^29.15.4",
|
||||
"eslint-plugin-n": "^18.2.1",
|
||||
"globals": "^17.11.0",
|
||||
"globals": "^17.7.0",
|
||||
"jest": "^30.4.2",
|
||||
"jest-silent-reporter": "^0.6.0",
|
||||
"prettier": "^3.9.4",
|
||||
|
||||
@@ -268,6 +268,24 @@ export class IssuesProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
// exemptIssueTypes wins if both it and onlyIssueTypes are specified
|
||||
if (this.options.exemptIssueTypes && !issue.isPullRequest) {
|
||||
const exemptTypes = this.options.exemptIssueTypes
|
||||
.split(',')
|
||||
.map(t => t.trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
const issueType = (issue.issue_type || '').toLowerCase();
|
||||
if (exemptTypes.includes(issueType)) {
|
||||
issueLogger.info(
|
||||
`Skipping this $$type because its type ('${
|
||||
issue.issue_type
|
||||
}') is in exemptIssueTypes (${exemptTypes.join(', ')})`
|
||||
);
|
||||
IssuesProcessor._endIssueProcessing(issue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const onlyLabels: string[] = wordsToList(this._getOnlyLabels(issue));
|
||||
|
||||
if (onlyLabels.length > 0) {
|
||||
|
||||
@@ -50,5 +50,6 @@ export enum Option {
|
||||
IgnorePrUpdates = 'ignore-pr-updates',
|
||||
ExemptDraftPr = 'exempt-draft-pr',
|
||||
CloseIssueReason = 'close-issue-reason',
|
||||
ExemptIssueTypes = 'exempt-issue-types',
|
||||
OnlyIssueTypes = 'only-issue-types'
|
||||
}
|
||||
|
||||
@@ -55,5 +55,6 @@ export interface IIssuesProcessorOptions {
|
||||
exemptDraftPr: boolean;
|
||||
closeIssueReason: string;
|
||||
includeOnlyAssigned: boolean;
|
||||
exemptIssueTypes?: string;
|
||||
onlyIssueTypes?: string;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,8 @@ function _getAndValidateArgs(): IIssuesProcessorOptions {
|
||||
exemptDraftPr: core.getInput('exempt-draft-pr') === 'true',
|
||||
closeIssueReason: core.getInput('close-issue-reason'),
|
||||
includeOnlyAssigned: core.getInput('include-only-assigned') === 'true',
|
||||
onlyIssueTypes: core.getInput('only-issue-types')
|
||||
onlyIssueTypes: core.getInput('only-issue-types'),
|
||||
exemptIssueTypes: core.getInput('exempt-issue-types')
|
||||
};
|
||||
|
||||
for (const numberInput of ['days-before-stale']) {
|
||||
|
||||
Reference in New Issue
Block a user