forked from mirrors/action-gh-release
node_modules
This commit is contained in:
parent
0e414c630a
commit
78c309ef59
555 changed files with 103819 additions and 1 deletions
128
node_modules/@octokit/plugin-throttling/lib/index.js
generated
vendored
Normal file
128
node_modules/@octokit/plugin-throttling/lib/index.js
generated
vendored
Normal file
|
@ -0,0 +1,128 @@
|
|||
module.exports = throttlingPlugin
|
||||
|
||||
const BottleneckLight = require('bottleneck/light')
|
||||
const wrapRequest = require('./wrap-request')
|
||||
const triggersNotificationPaths = require('./triggers-notification-paths')
|
||||
const routeMatcher = require('./route-matcher')(triggersNotificationPaths)
|
||||
|
||||
// Workaround to allow tests to directly access the triggersNotification function.
|
||||
const triggersNotification = throttlingPlugin.triggersNotification =
|
||||
routeMatcher.test.bind(routeMatcher)
|
||||
|
||||
const groups = {}
|
||||
|
||||
const createGroups = function (Bottleneck, common) {
|
||||
groups.global = new Bottleneck.Group({
|
||||
id: 'octokit-global',
|
||||
maxConcurrent: 10,
|
||||
...common
|
||||
})
|
||||
groups.search = new Bottleneck.Group({
|
||||
id: 'octokit-search',
|
||||
maxConcurrent: 1,
|
||||
minTime: 2000,
|
||||
...common
|
||||
})
|
||||
groups.write = new Bottleneck.Group({
|
||||
id: 'octokit-write',
|
||||
maxConcurrent: 1,
|
||||
minTime: 1000,
|
||||
...common
|
||||
})
|
||||
groups.notifications = new Bottleneck.Group({
|
||||
id: 'octokit-notifications',
|
||||
maxConcurrent: 1,
|
||||
minTime: 3000,
|
||||
...common
|
||||
})
|
||||
}
|
||||
|
||||
function throttlingPlugin (octokit, octokitOptions = {}) {
|
||||
const {
|
||||
enabled = true,
|
||||
Bottleneck = BottleneckLight,
|
||||
id = 'no-id',
|
||||
timeout = 1000 * 60 * 2, // Redis TTL: 2 minutes
|
||||
connection
|
||||
} = octokitOptions.throttle || {}
|
||||
if (!enabled) {
|
||||
return
|
||||
}
|
||||
const common = { connection, timeout }
|
||||
|
||||
if (groups.global == null) {
|
||||
createGroups(Bottleneck, common)
|
||||
}
|
||||
|
||||
const state = Object.assign({
|
||||
clustering: connection != null,
|
||||
triggersNotification,
|
||||
minimumAbuseRetryAfter: 5,
|
||||
retryAfterBaseValue: 1000,
|
||||
retryLimiter: new Bottleneck(),
|
||||
id,
|
||||
...groups
|
||||
}, octokitOptions.throttle)
|
||||
|
||||
if (typeof state.onAbuseLimit !== 'function' || typeof state.onRateLimit !== 'function') {
|
||||
throw new Error(`octokit/plugin-throttling error:
|
||||
You must pass the onAbuseLimit and onRateLimit error handlers.
|
||||
See https://github.com/octokit/rest.js#throttling
|
||||
|
||||
const octokit = new Octokit({
|
||||
throttle: {
|
||||
onAbuseLimit: (error, options) => {/* ... */},
|
||||
onRateLimit: (error, options) => {/* ... */}
|
||||
}
|
||||
})
|
||||
`)
|
||||
}
|
||||
|
||||
const events = {}
|
||||
const emitter = new Bottleneck.Events(events)
|
||||
events.on('abuse-limit', state.onAbuseLimit)
|
||||
events.on('rate-limit', state.onRateLimit)
|
||||
events.on('error', e => console.warn('Error in throttling-plugin limit handler', e))
|
||||
|
||||
state.retryLimiter.on('failed', async function (error, info) {
|
||||
const options = info.args[info.args.length - 1]
|
||||
const isGraphQL = options.url.startsWith('/graphql')
|
||||
|
||||
if (!(isGraphQL || error.status === 403)) {
|
||||
return
|
||||
}
|
||||
|
||||
const retryCount = ~~options.request.retryCount
|
||||
options.request.retryCount = retryCount
|
||||
|
||||
const { wantRetry, retryAfter } = await (async function () {
|
||||
if (/\babuse\b/i.test(error.message)) {
|
||||
// The user has hit the abuse rate limit. (REST only)
|
||||
// https://developer.github.com/v3/#abuse-rate-limits
|
||||
|
||||
// The Retry-After header can sometimes be blank when hitting an abuse limit,
|
||||
// but is always present after 2-3s, so make sure to set `retryAfter` to at least 5s by default.
|
||||
const retryAfter = Math.max(~~error.headers['retry-after'], state.minimumAbuseRetryAfter)
|
||||
const wantRetry = await emitter.trigger('abuse-limit', retryAfter, options)
|
||||
return { wantRetry, retryAfter }
|
||||
}
|
||||
if (error.headers != null && error.headers['x-ratelimit-remaining'] === '0') {
|
||||
// The user has used all their allowed calls for the current time period (REST and GraphQL)
|
||||
// https://developer.github.com/v3/#rate-limiting
|
||||
|
||||
const rateLimitReset = new Date(~~error.headers['x-ratelimit-reset'] * 1000).getTime()
|
||||
const retryAfter = Math.max(Math.ceil((rateLimitReset - Date.now()) / 1000), 0)
|
||||
const wantRetry = await emitter.trigger('rate-limit', retryAfter, options)
|
||||
return { wantRetry, retryAfter }
|
||||
}
|
||||
return {}
|
||||
})()
|
||||
|
||||
if (wantRetry) {
|
||||
options.request.retryCount++
|
||||
return retryAfter * state.retryAfterBaseValue
|
||||
}
|
||||
})
|
||||
|
||||
octokit.hook.wrap('request', wrapRequest.bind(null, state))
|
||||
}
|
31
node_modules/@octokit/plugin-throttling/lib/route-matcher.js
generated
vendored
Normal file
31
node_modules/@octokit/plugin-throttling/lib/route-matcher.js
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
module.exports = routeMatcher
|
||||
|
||||
function routeMatcher (paths) {
|
||||
// EXAMPLE. For the following paths:
|
||||
/* [
|
||||
"/orgs/:org/invitations",
|
||||
"/repos/:owner/:repo/collaborators/:username"
|
||||
] */
|
||||
|
||||
const regexes = paths.map(p =>
|
||||
p.split('/')
|
||||
.map(c => c.startsWith(':') ? '(?:.+?)' : c)
|
||||
.join('/')
|
||||
)
|
||||
// 'regexes' would contain:
|
||||
/* [
|
||||
'/orgs/(?:.+?)/invitations',
|
||||
'/repos/(?:.+?)/(?:.+?)/collaborators/(?:.+?)'
|
||||
] */
|
||||
|
||||
const regex = `^(?:${regexes.map(r => `(?:${r})`).join('|')})[^/]*$`
|
||||
// 'regex' would contain:
|
||||
/*
|
||||
^(?:(?:\/orgs\/(?:.+?)\/invitations)|(?:\/repos\/(?:.+?)\/(?:.+?)\/collaborators\/(?:.+?)))[^\/]*$
|
||||
|
||||
It may look scary, but paste it into https://www.debuggex.com/
|
||||
and it will make a lot more sense!
|
||||
*/
|
||||
|
||||
return new RegExp(regex, 'i')
|
||||
}
|
15
node_modules/@octokit/plugin-throttling/lib/triggers-notification-paths.json
generated
vendored
Normal file
15
node_modules/@octokit/plugin-throttling/lib/triggers-notification-paths.json
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
[
|
||||
"/orgs/:org/invitations",
|
||||
"/repos/:owner/:repo/collaborators/:username",
|
||||
"/repos/:owner/:repo/commits/:sha/comments",
|
||||
"/repos/:owner/:repo/issues",
|
||||
"/repos/:owner/:repo/issues/:issue_number/comments",
|
||||
"/repos/:owner/:repo/pulls",
|
||||
"/repos/:owner/:repo/pulls/:pull_number/comments",
|
||||
"/repos/:owner/:repo/pulls/:pull_number/merge",
|
||||
"/repos/:owner/:repo/pulls/:pull_number/requested_reviewers",
|
||||
"/repos/:owner/:repo/pulls/:pull_number/reviews",
|
||||
"/repos/:owner/:repo/releases",
|
||||
"/teams/:team_id/discussions",
|
||||
"/teams/:team_id/discussions/:discussion_number/comments"
|
||||
]
|
49
node_modules/@octokit/plugin-throttling/lib/wrap-request.js
generated
vendored
Normal file
49
node_modules/@octokit/plugin-throttling/lib/wrap-request.js
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
module.exports = wrapRequest
|
||||
|
||||
const noop = () => Promise.resolve()
|
||||
|
||||
function wrapRequest (state, request, options) {
|
||||
return state.retryLimiter.schedule(doRequest, state, request, options)
|
||||
}
|
||||
|
||||
async function doRequest (state, request, options) {
|
||||
const isWrite = options.method !== 'GET' && options.method !== 'HEAD'
|
||||
const isSearch = options.method === 'GET' && options.url.startsWith('/search/')
|
||||
const isGraphQL = options.url.startsWith('/graphql')
|
||||
|
||||
const retryCount = ~~options.request.retryCount
|
||||
const jobOptions = retryCount > 0 ? { priority: 0, weight: 0 } : {}
|
||||
if (state.clustering) {
|
||||
// Remove a job from Redis if it has not completed or failed within 60s
|
||||
// Examples: Node process terminated, client disconnected, etc.
|
||||
jobOptions.expiration = 1000 * 60
|
||||
}
|
||||
|
||||
// Guarantee at least 1000ms between writes
|
||||
// GraphQL can also trigger writes
|
||||
if (isWrite || isGraphQL) {
|
||||
await state.write.key(state.id).schedule(jobOptions, noop)
|
||||
}
|
||||
|
||||
// Guarantee at least 3000ms between requests that trigger notifications
|
||||
if (isWrite && state.triggersNotification(options.url)) {
|
||||
await state.notifications.key(state.id).schedule(jobOptions, noop)
|
||||
}
|
||||
|
||||
// Guarantee at least 2000ms between search requests
|
||||
if (isSearch) {
|
||||
await state.search.key(state.id).schedule(jobOptions, noop)
|
||||
}
|
||||
|
||||
const req = state.global.key(state.id).schedule(jobOptions, request, options)
|
||||
if (isGraphQL) {
|
||||
const res = await req
|
||||
if (res.data.errors != null && res.data.errors.some((err) => err.type === 'RATE_LIMITED')) {
|
||||
const err = new Error('GraphQL Rate Limit Exceeded')
|
||||
err.headers = res.headers
|
||||
err.data = res.data
|
||||
throw err
|
||||
}
|
||||
}
|
||||
return req
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue