From ed8f3d07a93e7925645fac1514775559397b89ac Mon Sep 17 00:00:00 2001 From: Claudia Meadows Date: Tue, 10 Sep 2024 04:42:09 -0700 Subject: [PATCH] Finish initial repo draft --- .github/workflows/deploy-gh-pages.yml | 15 - .../workflows/{deploy-npm.yml => deploy.yml} | 8 +- .github/workflows/remind.yml | 3 +- eslint.config.js | 260 ++-- .../deploy-npm}/action.yml | 2 +- internal/deploy/action.yml | 9 + .../remind}/action.yml | 2 +- lib/api-client.js | 242 ++-- lib/config.js | 2 +- lib/deploy/github-pages.js | 259 ++++ lib/deploy/npm.js | 111 ++ lib/entry/deploy-gh-pages.js | 39 +- lib/entry/deploy-npm.js | 39 +- lib/entry/handle-deploy-gh-pages.js | 37 - lib/entry/handle-deploy-npm.js | 105 -- lib/entry/handle-deploy.js | 19 + lib/entry/handle-remind.js | 6 +- lib/oidc-get-token.js | 369 +++--- lib/util.js | 69 +- package-lock.json | 1093 ++++++++++++----- package.json | 21 +- 21 files changed, 1568 insertions(+), 1142 deletions(-) delete mode 100644 .github/workflows/deploy-gh-pages.yml rename .github/workflows/{deploy-npm.yml => deploy.yml} (51%) rename {internal-deploy-npm => internal/deploy-npm}/action.yml (69%) create mode 100644 internal/deploy/action.yml rename {internal-remind => internal/remind}/action.yml (57%) create mode 100644 lib/deploy/github-pages.js create mode 100644 lib/deploy/npm.js delete mode 100644 lib/entry/handle-deploy-gh-pages.js delete mode 100644 lib/entry/handle-deploy-npm.js create mode 100644 lib/entry/handle-deploy.js diff --git a/.github/workflows/deploy-gh-pages.yml b/.github/workflows/deploy-gh-pages.yml deleted file mode 100644 index 354ef8e..0000000 --- a/.github/workflows/deploy-gh-pages.yml +++ /dev/null @@ -1,15 +0,0 @@ -on: - repository_dispatch: - types: - - deploy-npm - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - repository: ${{ github.workflow_ref }} - - uses: ./internal-deploy-npm - with: - npm_token: ${{ secrets.GH_PAGES_TOKEN }} diff --git a/.github/workflows/deploy-npm.yml b/.github/workflows/deploy.yml similarity index 51% rename from .github/workflows/deploy-npm.yml rename to .github/workflows/deploy.yml index 5f8b617..daa930b 100644 --- a/.github/workflows/deploy-npm.yml +++ b/.github/workflows/deploy.yml @@ -1,15 +1,13 @@ on: repository_dispatch: types: - - deploy-npm + - deploy jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - with: - repository: ${{ github.workflow_ref }} - - uses: ./internal-deploy-npm + - uses: MithrilJS/infra/internal/deploy with: npm_token: ${{ secrets.NPM_TOKEN }} + github_pages_token: ${{ secrets.GITHUB_PAGES_TOKEN }} diff --git a/.github/workflows/remind.yml b/.github/workflows/remind.yml index 059d259..75ffc08 100644 --- a/.github/workflows/remind.yml +++ b/.github/workflows/remind.yml @@ -12,5 +12,4 @@ jobs: - windows-latest - macos-latest steps: - - uses: actions/checkout@v4 - - uses: ./internal-remind + - uses: MithrilJS/infra/internal/remind diff --git a/eslint.config.js b/eslint.config.js index 6ab0377..feab296 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,10 +1,17 @@ import globals from "globals" import js from "@eslint/js" +import n from "eslint-plugin-n" +import stylistic from "@stylistic/eslint-plugin" export default [ js.configs.recommended, + n.configs["flat/recommended"], { name: "root config", + plugins: { + "@stylistic": stylistic, + "n": n, + }, languageOptions: { ecmaVersion: 2022, globals: { @@ -14,222 +21,121 @@ export default [ }, rules: { "accessor-pairs": "error", - "array-bracket-spacing": [ + "@stylistic/array-bracket-spacing": [ "error", "never" ], "array-callback-return": "error", "arrow-body-style": "error", - "arrow-parens": "error", - "arrow-spacing": "error", - "block-scoped-var": "off", - "block-spacing": "off", - "brace-style": "off", - "callback-return": "off", - "camelcase": [ - "error", - { - "properties": "never" - } - ], - "comma-dangle": [ - "error", - "only-multiline" - ], - "comma-spacing": "off", - "comma-style": [ - "error", - "last" - ], - "complexity": "off", - "computed-property-spacing": [ - "error", - "never" - ], - "consistent-return": "off", - "consistent-this": "off", - "curly": "off", - "default-case": "off", - "dot-location": [ - "error", - "property" - ], - "dot-notation": "off", - "eol-last": "off", - "eqeqeq": "off", - "func-names": "off", - "func-style": "off", - "generator-star-spacing": "error", - "global-require": "error", - "guard-for-in": "off", - "handle-callback-err": "error", - "id-blacklist": "error", - "id-length": "off", - "id-match": "error", - "indent": [ - "warn", - 4, - { - "outerIIFEBody": 0, - "SwitchCase": 1 - } - ], - "init-declarations": "off", - "jsx-quotes": "error", - "key-spacing": "off", - "keyword-spacing": "off", - "linebreak-style": "off", - "lines-around-comment": "off", - "max-depth": "off", - "max-len": "off", - "max-nested-callbacks": "error", - "max-params": "off", - "max-statements": "off", - "max-statements-per-line": "off", - "new-parens": "off", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "off", + "@stylistic/arrow-parens": "error", + "@stylistic/arrow-spacing": "error", + "@stylistic/block-spacing": ["error", "always"], + "@stylistic/brace-style": ["error", "1tbs"], + "@stylistic/comma-dangle": ["error", "only-multiline"], + "@stylistic/comma-spacing": ["error", {before: false, after: true}], + "@stylistic/comma-style": ["error", "last"], + "@stylistic/computed-property-spacing": ["error", "never"], + "curly": ["error", "multi-line", "consistent"], + "@stylistic/dot-location": ["error", "property"], + "dot-notation": "error", + "@stylistic/eol-last": "error", + "eqeqeq": ["error", "always", {null: "ignore"}], + "@stylistic/function-call-spacing": "error", + "@stylistic/generator-star-spacing": "error", + "n/handle-callback-err": "error", + "id-match": ["error", "^_?(?:[A-Za-z$][\\w$]*|[$A-Z][$A-Z0-9]*(?:_[$A-Z0-9]+)*)$", {properties: false}], + "@stylistic/indent": ["error", 4, {outerIIFEBody: 0, SwitchCase: 1}], + "@stylistic/implicit-arrow-linebreak": ["error", "beside"], + "@stylistic/key-spacing": "error", + "@stylistic/keyword-spacing": "error", + "@stylistic/linebreak-style": ["error", "unix"], + "@stylistic/lines-around-comment": ["error", { + beforeBlockComment: true, + allowBlockStart: true, + allowBlockEnd: true, + allowObjectStart: true, + allowObjectEnd: true, + allowArrayStart: true, + allowArrayEnd: true, + allowClassStart: true, + allowClassEnd: true, + }], + "@stylistic/multiline-comment-style": ["error", "bare-block"], + "@stylistic/new-parens": "error", + "@stylistic/newline-per-chained-call": "error", "no-alert": "error", "no-array-constructor": "error", "no-bitwise": "error", "no-caller": "error", - "no-catch-shadow": "off", - "no-cond-assign": "off", - "no-confusing-arrow": "error", - "no-console": "off", - "no-continue": "off", + "@stylistic/no-confusing-arrow": "error", "no-div-regex": "error", "no-duplicate-imports": "error", - "no-else-return": "off", - "no-empty-function": "off", - "no-eq-null": "off", "no-eval": "error", - "no-extend-native": "off", "no-extra-bind": "error", "no-extra-label": "error", - "no-extra-parens": "off", - "no-floating-decimal": "error", + "@stylistic/no-extra-semi": "error", + "@stylistic/no-floating-decimal": "error", "no-implicit-coercion": "error", "no-implicit-globals": "error", "no-implied-eval": "error", - "no-inline-comments": "off", - "no-invalid-this": "off", "no-iterator": "error", - "no-label-var": "off", - "no-labels": "off", "no-lone-blocks": "error", - "no-lonely-if": "off", - "no-loop-func": "off", - "no-magic-numbers": "off", - "no-mixed-requires": "error", - "no-multi-spaces": "error", + "n/no-mixed-requires": "error", + "@stylistic/no-multi-spaces": "error", "no-multi-str": "error", - "no-multiple-empty-lines": "error", + "@stylistic/no-multiple-empty-lines": "error", "no-native-reassign": "error", - "no-negated-condition": "off", - "no-nested-ternary": "off", - "no-new": "off", - "no-new-func": "off", + "no-new": "error", + "no-new-func": "error", "no-new-object": "error", - "no-new-require": "error", + "n/no-new-require": "error", "no-new-wrappers": "error", "no-octal-escape": "error", - "no-param-reassign": "off", - "no-path-concat": "off", - "no-plusplus": "off", - "no-process-env": "off", - "no-process-exit": "error", + "n/no-process-exit": "error", "no-proto": "error", - "no-redeclare": "off", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-modules": "error", - "no-restricted-syntax": "error", - "no-return-assign": "off", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "off", - "no-shadow": "off", - "no-shadow-restricted-names": "error", - "no-spaced-func": "error", - "no-sync": "off", - "no-ternary": "off", - "no-throw-literal": "off", - "no-trailing-spaces": [ + "no-redeclare": "error", + "no-restricted-syntax": [ "error", - { - "skipBlankLines": true - } + {selector: "ForInStatement", message: "Use `for (const key of Object.keys(object))` instead."} ], + "no-script-url": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow": "error", + "@stylistic/no-tabs": "error", + "no-throw-literal": "error", + "@stylistic/no-trailing-spaces": ["error", {skipBlankLines: true}], "no-undef-init": "error", - "no-undefined": "off", - "no-underscore-dangle": "off", "no-unmodified-loop-condition": "error", + "no-unreachable-loop": "error", "no-unneeded-ternary": "error", - "no-unused-expressions": "off", - "no-use-before-define": "off", + "no-useless-assignment": "error", "no-useless-call": "error", "no-useless-concat": "error", "no-useless-constructor": "error", - "no-useless-escape": "off", - "no-var": "off", - "no-void": "off", - "no-warning-comments": "off", - "no-whitespace-before-property": "error", - "no-with": "error", - "object-curly-spacing": [ - "error", - "never" - ], + "no-var": "error", + "@stylistic/no-whitespace-before-property": "error", + "object-curly-spacing": ["error", "never"], "object-shorthand": "off", - "one-var": "off", + "one-var": ["error", {initialized: "never"}], "one-var-declaration-per-line": "off", - "operator-assignment": [ - "error", - "always" - ], - "operator-linebreak": "off", - "padded-blocks": "off", - "prefer-arrow-callback": "off", + "operator-assignment": ["error", "always"], + "@stylistic/operator-linebreak": "off", + "@stylistic/padded-blocks": ["error", "never"], "prefer-const": "error", - "prefer-reflect": "off", - "prefer-rest-params": "off", - "prefer-spread": "off", - "prefer-template": "off", - "quote-props": "off", - "quotes": [ - "error", - "double", - {"avoidEscape": true} - ], - "radix": [ - "error", - "always" - ], - "require-jsdoc": "off", + "@stylistic/quotes": ["error", "double", {avoidEscape: true}], + "radix": ["error", "always"], "require-yield": "error", - "semi": "off", - "semi-spacing": "off", + "@stylistic/semi": ["error", "never"], "sort-imports": ["error", {allowSeparatedGroups: true}], - "sort-vars": "off", - "space-before-blocks": "off", - "space-before-function-paren": "off", - "space-in-parens": [ - "error", - "never" - ], - "space-infix-ops": "off", - "space-unary-ops": "error", - "spaced-comment": "off", + "@stylistic/space-before-blocks": ["error", "always"], + "@stylistic/space-before-function-paren": ["error", "never"], + "@stylistic/space-in-parens": ["error", "never"], + "@stylistic/space-unary-ops": "error", "strict": ["error", "global"], - "template-curly-spacing": "error", - "valid-jsdoc": "off", - "vars-on-top": "off", - "wrap-iife": "off", - "wrap-regex": "error", - "yield-star-spacing": "error", - "yoda": "off" + "@stylistic/template-curly-spacing": "error", + "@stylistic/wrap-regex": "error", + "@stylistic/yield-star-spacing": "error", } } ] diff --git a/internal-deploy-npm/action.yml b/internal/deploy-npm/action.yml similarity index 69% rename from internal-deploy-npm/action.yml rename to internal/deploy-npm/action.yml index d08dbb2..519b91b 100644 --- a/internal-deploy-npm/action.yml +++ b/internal/deploy-npm/action.yml @@ -6,4 +6,4 @@ inputs: runs: using: node20 - main: ../lib/entry/handle-deploy.js + main: ../../lib/entry/handle-deploy.js diff --git a/internal/deploy/action.yml b/internal/deploy/action.yml new file mode 100644 index 0000000..059a248 --- /dev/null +++ b/internal/deploy/action.yml @@ -0,0 +1,9 @@ +name: Do internal deployment + +inputs: + github_pages_token: + required: true + +runs: + using: node20 + main: ../../lib/entry/handle-deploy.js diff --git a/internal-remind/action.yml b/internal/remind/action.yml similarity index 57% rename from internal-remind/action.yml rename to internal/remind/action.yml index 4e0896b..60c99d3 100644 --- a/internal-remind/action.yml +++ b/internal/remind/action.yml @@ -2,4 +2,4 @@ name: Generate internal reminder runs: using: node20 - main: ../lib/entry/handle-remind.js + main: ../../lib/entry/handle-remind.js diff --git a/lib/api-client.js b/lib/api-client.js index 4ec4ba4..0435f51 100644 --- a/lib/api-client.js +++ b/lib/api-client.js @@ -1,167 +1,149 @@ import * as http from "node:http" import * as https from "node:https" +import * as tunnel from "tunnel" import {ProxyAgent, fetch} from "undici" +import {request} from "@octokit/request" -import * as tunnel from "tunnel" -import {Octokit} from "@octokit/core" - -const isLoopbackAddress = /^LOCALHOST$|^127\.|^\[::1]|^\[0:0:0:0:0:0:0:1]/ - -/** @param {URL} reqUrl */ -function checkBypass(reqUrl) { - const hostnameUpper = reqUrl.hostname.toUpperCase() - if (!hostnameUpper) return false - if (isLoopbackAddress.test(hostnameUpper)) return true - - const noProxy = process.env["no_proxy"] || process.env["NO_PROXY"] - if (!noProxy) return false - - // Determine the request port - let reqPort - - if (reqUrl.port) { - reqPort = Number(reqUrl.port) - } else if (reqUrl.protocol === "http:") { - reqPort = 80 - } else if (reqUrl.protocol === "https:") { - reqPort = 443 - } else{ - return false - } +import {fail, validateDeployPayload} from "./util.js" - // Compare request host against noproxy - // Check both hostname and hostname + port - const hostPortUpper = `${hostnameUpper}:${reqPort}` - - for (let part of noProxy.split(",")) { - part = part.trim() - if (!part) continue - if (part === "*") return true - part = part.toUpperCase() - if (hostnameUpper === part) return true - if (hostPortUpper === part) return true - if (!part.startsWith(".")) part = `.${part}` - if (hostnameUpper.endsWith(part)) return true - if (hostPortUpper.endsWith(part)) return true - } +// Check both hostname and hostname + port +const proxyHasGitHubApi = /(?:^|,)\s*(?:\*|(?:(?:(?:(?:api)?\.)?github)?\.)?com(?::443)?)\s*(?:$|,)/i - return false -} +// Note: this assumes the public `https://api.github.com` URL is being used for the API. -let context +// This sets up any potential internal proxying Github might use in its runtime. +const maxSockets = http.globalAgent.maxSockets +let proxyUrl2 -/** - * @returns {{ - * agent: TunnelingAgent | http.Agent; - * dispatcher: undefined | import("undici").Dispatcher - * baseUrl: string; - * }} - */ -export function getRequestContext() { - if (context) return context - - // This sets up any potential internal proxying Github might use in its runtime. - const parsedUrl = new URL(process.env.GITHUB_API_URL || "https://api.github.com") - const usingSsl = parsedUrl.protocol === "https:" - const maxSockets = http.globalAgent.maxSockets - let proxyUrl2 - - if (!checkBypass(parsedUrl)) { - let proxyVar - - if (usingSsl) { - proxyVar = process.env["https_proxy"] || process.env["HTTPS_PROXY"] - } else { - proxyVar = process.env["http_proxy"] || process.env["HTTP_PROXY"] - } +const noProxy = process.env["no_proxy"] || process.env["NO_PROXY"] +if (!noProxy || !proxyHasGitHubApi.test(noProxy)) { + const proxyVar = process.env["https_proxy"] || process.env["HTTPS_PROXY"] - if (proxyVar) { - try { - proxyUrl2 = new URL(proxyVar) - } catch { - if (!proxyVar.startsWith("http://") && !proxyVar.startsWith("https://")) { - proxyUrl2 = new URL(`http://${proxyVar}`) - } + if (proxyVar) { + try { + proxyUrl2 = new URL(proxyVar) + } catch { + if (!proxyVar.startsWith("http://") && !proxyVar.startsWith("https://")) { + proxyUrl2 = new URL(`http://${proxyVar}`) } } } +} + +const useProxy = proxyUrl2 && proxyUrl2.hostname +let agent, dispatcher + +// This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. +if (proxyUrl2 && proxyUrl2.hostname) { + const agentOptions = { + maxSockets, + keepAlive: false, + proxy: { + host: proxyUrl2.hostname, + port: proxyUrl2.port, + }, + } + + const proxyUsername = decodeURIComponent(proxyUrl2.username) + const proxyPassword = decodeURIComponent(proxyUrl2.password) - const useProxy = proxyUrl2 && proxyUrl2.hostname - let agent, dispatcher + if (proxyUsername || proxyPassword) { + agentOptions.proxy.proxyAuth = `${proxyUsername}:${proxyPassword}` + } - // This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. - if (proxyUrl2 && proxyUrl2.hostname) { + const overHttps = proxyUrl2.protocol === "https:" + const tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp + + agent = dispatcher = tunnelAgent(agentOptions) +} else { + const options = {keepAlive: false, maxSockets} + agent = new https.Agent(options) + if (useProxy) { const agentOptions = { - maxSockets, - keepAlive: false, - proxy: { - host: proxyUrl2.hostname, - port: proxyUrl2.port, - }, + url: proxyUrl2.href, + pipelining: 0, } const proxyUsername = decodeURIComponent(proxyUrl2.username) const proxyPassword = decodeURIComponent(proxyUrl2.password) if (proxyUsername || proxyPassword) { - agentOptions.proxy.proxyAuth = `${proxyUsername}:${proxyPassword}` + const userPass = `${proxyUsername}:${proxyPassword}` + agentOptions.token = `Basic ${Buffer.from(userPass).toString("base64")}` } - let tunnelAgent - const overHttps = proxyUrl2.protocol === "https:" - if (usingSsl) { - tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp - } else { - tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp - } - - agent = dispatcher = tunnelAgent(agentOptions) - } else { - const options = {keepAlive: false, maxSockets} - agent = usingSsl ? new https.Agent(options) : new http.Agent(options) - if (useProxy) { - const agentOptions = { - url: proxyUrl2.href, - pipelining: 0, - } + dispatcher = new ProxyAgent(options) + } +} - const proxyUsername = decodeURIComponent(proxyUrl2.username) - const proxyPassword = decodeURIComponent(proxyUrl2.password) +export {agent as apiAgent} - if (proxyUsername || proxyPassword) { - const userPass = `${proxyUsername}:${proxyPassword}` - agentOptions.token = `Basic ${Buffer.from(userPass).toString("base64")}` - } +/** + * @param {string} token + */ +export function getRequest(token) { + return request.defaults({ + headers: { + "X-GitHub-Api-Version": "2022-11-28", + authorization: `token ${token}` + }, + request: { + agent, + fetch: (url, opts) => fetch(url, {dispatcher, ...opts}), + }, + }) +} - dispatcher = new ProxyAgent(options) - } +/** + * @param {string} artifactFile + * @param {import("./util.js").DeployPayload["type"]} type + * @param {import("./util.js").DeployPayload["artifactName"]} artifactName + * @param {import("./util.js").DeployPayload["tarballName"]} tarballName + */ +export async function performDeployment(artifactFile, type, artifactName, tarballName) { + if (!process.env.INPUT_TOKEN) { + throw new TypeError("Deploy token must be present and non-empty") } - return context = {agent, dispatcher, baseUrl: parsedUrl.href} -} + console.log(`Uploading ${artifactFile} as artifact ${artifactName}`) -const octokitCache = new Map() + const {default: artifact} = await import("@actions/artifact") + const uploadResponse = await artifact.uploadArtifact(artifactName, [tarballName], process.env.RUNNER_TEMP) -export function getOctokit(token) { - if (!token) { - throw new Error("Token is required") + if (uploadResponse.id === undefined) { + fail("Artifact upload failed to yield an ID") } - const found = octokitCache.get(token) - if (found) return found + const [thisOwner, thisRepo] = process.env.GITHUB_ACTION_REPOSITORY.split("/") - const {agent, dispatcher, baseUrl} = getRequestContext() + console.log(`Issuing dispatch event to ${thisOwner}/${thisRepo}`) - const octokit = new Octokit({ - auth: `token ${token}`, - baseUrl, - request: { - agent, - fetch: (url, opts) => fetch(url, {dispatcher, ...opts}), - } - }) - octokitCache.set(found, octokit) - return octokit + try { + await request("POST /repos/{owner}/{repo}/dispatches", { + headers: { + "X-GitHub-Api-Version": "2022-11-28", + authorization: `token ${process.env.INPUT_TOKEN}` + }, + request: { + agent, + fetch: (url, opts) => fetch(url, {dispatcher, ...opts}), + }, + owner: thisOwner, + repo: thisRepo, + event_type: "deploy", + client_payload: validateDeployPayload({ + repo: process.env.GITHUB_REPOSITORY, + type, + artifactName, + tarballName, + artifactId: uploadResponse.id, + workflowRunId: process.env.GITHUB_RUN_ID, + buildVersion: process.env.GITHUB_SHA, + }), + }) + } catch (e) { + fail(`::error title=Failed to create dispatch event::${e.message}`) + } } diff --git a/lib/config.js b/lib/config.js index 680fb51..09a436f 100644 --- a/lib/config.js +++ b/lib/config.js @@ -30,7 +30,7 @@ export const projects = { export const localTokenExpiryDates = { NPM_TOKEN: d(1970, 1, 1), - GH_PAGES_TOKEN: d(1970, 1, 1), + GITHUB_PAGES_TOKEN: d(1970, 1, 1), } function d(year, month, day) { diff --git a/lib/deploy/github-pages.js b/lib/deploy/github-pages.js new file mode 100644 index 0000000..831aa96 --- /dev/null +++ b/lib/deploy/github-pages.js @@ -0,0 +1,259 @@ +import {checkSecretExpiration, fail, reportRunError, run} from "../util.js" +import {getIDToken} from "../oidc-get-token.js" +import {getRequest} from "../api-client.js" + +const DEPLOYMENT_TIMEOUT = 60_000 +const SIZE_LIMIT_BYTES = 2 ** 30 +const SIZE_LIMIT_DESCRIPTION = "1 GB" + +const MIN_REPORTING_INTERVAL = 5000 +const MAX_REPORTING_INTERVAL = 15000 +const MAX_ERROR_COUNT = 10 + +/** + * @type {Map< + * import("@octokit/openapi-types").components["schemas"]["pages-deployment-status"], + * {fatal: boolean, message: string}, + * >} + */ +const deploymentErrorMessageMap = new Map([ + ["unknown_status", { + fatal: false, + message: "Unable to get deployment status.", + }], + ["not_found", { + fatal: false, + message: "Deployment not found.", + }], + ["deployment_attempt_error", { + fatal: false, + message: "Deployment temporarily failed, a retry will be automatically scheduled...", + }], + ["deployment_failed", { + fatal: true, + message: "Deployment failed, try again later.", + }], + ["deployment_content_failed", { + fatal: true, + message: "Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB.", + }], + ["deployment_cancelled", { + fatal: true, + message: "Deployment cancelled.", + }], + ["deployment_lost", { + fatal: true, + message: "Deployment failed to report final status.", + }], +]) + +/** + * @param {string} idToken + * @param {import("../util.js").DeployPayload} payload + * @param {AbortSignal} signal + */ +export async function deployToGitHubPages(payload) { + const idToken = await getIDToken() + + checkSecretExpiration("GITHUB_PAGES_TOKEN") + + const request = getRequest(process.env.INPUT_GITHUB_PAGES_TOKEN) + + const buildActor = process.env.GITHUB_ACTOR + let startTime = 0 + let deploymentPending = false + + /** @type {string | number} */ + let deploymentId = "" + + /** @type {undefined | import("@octokit/openapi-types").components["schemas"]["page-deployment"]} */ + let deploymentInfo + + async function cancelDeployment() { + // Don't attempt to cancel if no deployment was created + if (!deploymentPending) { + console.log("::debug::No deployment to cancel") + return + } + + // Cancel the deployment + console.log("Canceling Pages deployment...") + try { + await request("POST /repos/{owner}/{repo}/pages/deployments/{deploymentId}/cancel", { + owner, + repo, + deploymentId, + }) + console.log(`Canceled deployment with ID ${deploymentId}`) + + deploymentPending = false + } catch (error) { + fail([ + "Canceling Pages deployment failed", + error, + ...(error.response?.data ? [JSON.stringify(error.response.data)] : []), + ]) + } + } + + let errorReportingIntervalTimer + + // Handlers aren't registered until they're actually needed. + const onCancel = () => { + process.off("SIGINT", onCancel) + process.off("SIGTERM", onCancel) + clearTimeout(errorReportingIntervalTimer) + // Let it wait indefinitely until the action runner either shuts down or times out. + run(cancelDeployment) + } + + const registerCancel = () => { + process.on("SIGINT", onCancel) + process.on("SIGTERM", onCancel) + } + + const [owner, repo] = payload.repo.split("/") + + console.log("::group::Deploy to GitHub Pages") + + try { + try { + console.log(`::debug::Actor: ${buildActor}`) + console.log(`::debug::Actions Workflow Run ID: ${payload.workflowRunId}`) + + const {data: artifactData} = await request("GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}", { + owner, + repo, + artifact_id: payload.artifactId, + }) + + if (artifactData.size_in_bytes > SIZE_LIMIT_BYTES) { + // I'm failing this. If it's too big to fit, I don't want to chance it bugging out. + fail( + `::warning::Uploaded artifact size of ${artifactData.size_in_bytes + } bytes exceeds the allowed size of ${SIZE_LIMIT_DESCRIPTION}.` + ) + } + + // It's at this point where the termination handlers need to be active. + registerCancel() + + const response = await request("POST /repos/{owner}/{repo}/pages/deployments", { + owner, + repo, + artifact_id: payload.artifactId, + oidc_token: idToken, + }) + + deploymentInfo = response.data + deploymentId = response.data.id || payload.buildVersion + deploymentPending = true + startTime = Date.now() + + console.log(`Created deployment for ${deploymentId}, ID: ${deploymentInfo.id}`) + } catch (e) { + // build customized error message based on server response + if (!e.response) { + fail(e) + } + + let message = `Failed to create deployment (status: ${e.status}) with build version ${payload.buildVersion}.` + + if (e.response.headers["x-github-request-id"]) { + message += ` Request ID ${e.response.headers["x-github-request-id"]}` + } + + if (e.status >= 500) { + message += " Server error. Check https://githubstatus.com for a possible GitHub Pages outage and re-run the deployment at a later time." + } else if (e.status === 400) { + message += ` Responded with: ${e.message}` + } else if (e.status === 403) { + message += ' Ensure GITHUB_TOKEN has permission "pages: write".' + } else if (e.status === 404) { + message += ` Ensure GitHub Pages has been enabled: https://github.com/${payload.repo}/settings/pages` + } + + fail(message) + } + + // Don't attempt to check status if no deployment was created + if (!deploymentInfo) { + fail(deploymentErrorMessageMap.get("not_found")) + } + + if (deploymentPending) { + fail(deploymentErrorMessageMap.get("unknown_status")) + } + + let errorCount = 0 + let errorBurstCount = 0 + let errorStatus = 0 + + for (;;) { + await new Promise((resolve) => { + const ms = Math.min(MAX_REPORTING_INTERVAL, MIN_REPORTING_INTERVAL + 2 ** errorBurstCount) + errorReportingIntervalTimer = setTimeout(resolve, ms) + }) + errorReportingIntervalTimer = undefined + + // Check status + try { + console.log("Getting Pages deployment status...") + const {data: deploymentStatus} = await request("GET /repos/{owner}/{repo}/pages/deployments/{pages_deployment_id}", { + owner, + repo, + pages_deployment_id: deploymentId, + }) + + + const entry = deploymentErrorMessageMap.get(deploymentStatus.status) + + if (entry !== undefined) { + if (entry.fatal) { + fail(entry.message) + } + + console.log(`::warning::${entry.message}`) + } else if (deploymentStatus.status === "succeed") { + break + } else { + console.log(`Current status: ${deploymentStatus.status}`) + } + + // reset the error reporting interval once get the proper status back. + errorBurstCount = 0 + } catch (e) { + console.log("::error::Getting Pages deployment status failed") + reportRunError(e) + + // build customized error message based on server response + if (e.response) { + errorStatus = e.status || e.response.status + errorCount++ + errorBurstCount++ + } + } + + if (errorCount >= MAX_ERROR_COUNT) { + // Explicitly cancel the deployment + onCancel() + fail([ + "Too many errors, aborting!", + `Failed with status code: ${errorStatus}`, + ]) + } + + // Handle timeout + if (Date.now() - startTime >= DEPLOYMENT_TIMEOUT) { + // Explicitly cancel the deployment + onCancel() + fail("Timeout reached, aborting!") + } + } + } finally { + console.log("::endgroup::") + deploymentPending = false + } + + console.log("Deployment successful!") +} diff --git a/lib/deploy/npm.js b/lib/deploy/npm.js new file mode 100644 index 0000000..8a3e836 --- /dev/null +++ b/lib/deploy/npm.js @@ -0,0 +1,111 @@ +import * as fs from "node:fs/promises" +import * as path from "node:path" +import {once} from "node:events" +import {spawn} from "node:child_process" + +import artifact from "@actions/artifact" + +import { + checkSecretExpiration, + fail, + getAllowedPackages, + parsePackageInfo, +} from "../util.js" +import {projects} from "../config.js" + +/** + * @param {import("../util.js").DeployPayload} payload + * @param {AbortSignal} signal + */ +export async function deployToNpm(payload) { + checkSecretExpiration("NPM_TOKEN") + const artifactFile = path.join(process.env.GITHUB_WORKSPACE, payload.tarballName) + let packageName + + console.log("::group::Validating file") + + try { + console.log(`Downloading artifact from ${payload.repo}`) + const allowedPackages = getAllowedPackages(payload.repo, projects.npm) + + const [repositoryOwner, repositoryName] = payload.repo.split("/") + + await artifact.downloadArtifact(payload.artifactId, { + findBy: { + token: process.env.GITHUB_TOKEN, + repositoryOwner, + repositoryName, + workflowRunId: payload.workflowRunId, + }, + }) + + console.log("Extracting package info for validation") + let stdout + + const tarProcess = spawn("tar", [ + "--extract", + "--to-stdout", + `--file=${artifactFile}`, + "package/package.json", + ], { + stdio: [null, "pipe", null], + }) + + tarProcess.stdout.setEncoding("utf-8") + tarProcess.stdout.on("data", (chunk) => { + stdout += chunk + }) + + await Promise.all([ + once(tarProcess.stdout, "close"), + once(tarProcess, "exit"), + ]) + + if (tarProcess.exitCode) { + fail(`\`tar\` extraction failed with code ${tarProcess.exitCode}.`) + } + + if (tarProcess.signalCode) { + fail(`\`tar\` extraction failed with signal ${tarProcess.signalCode}.`) + } + + const packageInfo = await parsePackageInfo(artifactFile, stdout.received) + packageName = packageInfo.name + console.log(`Package ${packageName} detected`) + + if (!allowedPackages.has(packageName)) { + fail(`Refusing to publish ${packageName} as it is not allowlisted`) + } + } finally { + console.log("::endgroup::") + } + + console.log("::group::Deploy to npm") + + try { + // Don't use `npm config set` here, so the token can remain secret. + console.log("Registering authorization token secret") + await fs.appendFile( + path.join(process.env.HOME, ".npmrc"), + `\n//registry.npmjs.org/:_authToken=${process.env.INPUT_NPM_TOKEN}\n`, + {encoding: "utf-8"}, + ) + + console.log(`Publishing package ${packageName}`) + + const publishProcess = spawn("npm", ["publish", artifactFile]) + await once(publishProcess, "exit") + + if (publishProcess.exitCode) { + fail(`\`npm publish\` failed with code ${publishProcess.exitCode}.`) + } + + if (publishProcess.signalCode) { + fail(`\`npm publish\` failed with signal ${publishProcess.signalCode}.`) + } + } finally { + console.log("::endgroup::") + } + + console.log(`Package ${packageName} published successfully`) +} diff --git a/lib/entry/deploy-gh-pages.js b/lib/entry/deploy-gh-pages.js index 54de008..c66218b 100644 --- a/lib/entry/deploy-gh-pages.js +++ b/lib/entry/deploy-gh-pages.js @@ -3,22 +3,17 @@ import {once} from "node:events" import {spawn} from "node:child_process" import {fail, run} from "../util.js" -import {getOctokit} from "../api-client.js" - -const artifactP = import("@actions/artifact") +import {performDeployment} from "../api-client.js" run(async () => { - const [thisOwner, thisRepo] = process.env.GITHUB_ACTION_REPOSITORY.split("/") - const sourceRepository = process.env.GITHUB_REPOSITORY - - const deployToken = process.env.INPUT_TOKEN + const artifactFile = path.join(process.env.RUNNER_TEMP, "artifact.tar") // Switch to GNU tar instead of the default bsdtar so I can use for `--hard-dereference`. const tarCmd = process.platform === "darwin" ? "gtar" : "tar" const tarArgs = [ "--dereference", "--hard-dereference", "--directory", process.env.INPUT_PATH, - "-cvf", path.join(process.env.RUNNER_TEMP, "artifact.tar"), + "-cvf", artifactFile, "--exclude=.git", "--exclude=.github", ...(process.platform === "win32" ? ["--force-local"] : []), @@ -39,31 +34,5 @@ run(async () => { console.log("::endgroup::") - console.log("Uploading artifact") - const {default: artifact} = await artifactP - const uploadResponse = await artifact.uploadArtifact("github-pages", ["artifact.tar"], process.env.RUNNER_TEMP) - - if (uploadResponse.id === undefined) { - fail("Artifact upload failed to yield an ID") - } - - console.log(`Issuing dispatch event to ${thisOwner}/${thisRepo}`) - - const octokit = getOctokit(deployToken) - - try { - await octokit.request("POST /repos/{owner}/{repo}/dispatches", { - owner: thisOwner, - repo: thisRepo, - event_type: "deploy-gh-pages", - client_payload: { - repo: sourceRepository, - artifactName: "github-pages", - artifactId: uploadResponse.id, - workflowRunId: process.env.GITHUB_RUN_ID, - }, - }) - } catch (e) { - fail(`::error title=Failed to create dispatch event::${e.message}`) - } + await performDeployment(artifactFile, "github-pages", "github-pages", "artifact.tar") }) diff --git a/lib/entry/deploy-npm.js b/lib/entry/deploy-npm.js index e60a9f6..423aebe 100644 --- a/lib/entry/deploy-npm.js +++ b/lib/entry/deploy-npm.js @@ -3,17 +3,10 @@ import {once} from "node:events" import {spawn} from "node:child_process" import {fail, getPackageInfo, run} from "../util.js" -import {getOctokit} from "../api-client.js" +import {performDeployment} from "../api-client.js" import {projects} from "../config.js" -const artifactP = import("@actions/artifact") - run(async () => { - const [thisOwner, thisRepo] = process.env.GITHUB_ACTION_REPOSITORY.split("/") - const sourceRepository = process.env.GITHUB_REPOSITORY - - const deployToken = process.env.INPUT_TOKEN - const packageDir = path.resolve( process.env.INPUT_PACKAGE_DIR || process.env.GITHUB_WORKSPACE || @@ -64,33 +57,5 @@ run(async () => { console.log(`Uploading tarball ${tarballName} as artifact`) - const {default: artifact} = await artifactP - const uploadResponse = await artifact.uploadArtifact(artifactName, [tarballName], packageDir, { - compressionLevel: 0, - }) - - if (uploadResponse.id === undefined) { - fail("Artifact upload failed to yield an ID") - } - - console.log(`Issuing dispatch event to ${thisOwner}/${thisRepo}`) - - const octokit = getOctokit(deployToken) - - try { - await octokit.request("POST /repos/{owner}/{repo}/dispatches", { - owner: thisOwner, - repo: thisRepo, - event_type: "deploy-npm", - client_payload: { - repo: sourceRepository, - artifactName, - tarballName, - artifactId: uploadResponse.id, - workflowRunId: process.env.GITHUB_RUN_ID, - }, - }) - } catch (e) { - fail(`::error title=Failed to create dispatch event::${e.message}`) - } + await performDeployment(artifactFile, "npm", artifactName, tarballName) }) diff --git a/lib/entry/handle-deploy-gh-pages.js b/lib/entry/handle-deploy-gh-pages.js deleted file mode 100644 index b04e189..0000000 --- a/lib/entry/handle-deploy-gh-pages.js +++ /dev/null @@ -1,37 +0,0 @@ -// This package assumes a site has already been built and the files exist in the current workspace -// If there's an artifact named `artifact.tar`, it can upload that to actions on its own, -// without the user having to do the tar process themselves. - -import {getIDToken} from "@actions/core" - -import {fail, run} from "../util.js" -import {Deployment} from "./internal/deployment" - - -const deployment = new Deployment() - -async function cancelHandler(evtOrExitCodeOrError) { - await deployment.cancel() - // eslint-disable-next-line no-process-exit - process.exit(Number.isNaN(evtOrExitCodeOrError) ? 1 : evtOrExitCodeOrError) -} - -// Register signal handlers for workflow cancellation -process.on("SIGINT", cancelHandler) -process.on("SIGTERM", cancelHandler) - -// Main -run(async () => { - let idToken = "" - try { - idToken = await getIDToken() - } catch (error) { - fail([ - error, - "Ensure GITHUB_TOKEN has permission \"id-token: write\".", - ]) - } - - await deployment.create(idToken) - await deployment.check() -}) diff --git a/lib/entry/handle-deploy-npm.js b/lib/entry/handle-deploy-npm.js deleted file mode 100644 index 1c94488..0000000 --- a/lib/entry/handle-deploy-npm.js +++ /dev/null @@ -1,105 +0,0 @@ -import * as fs from "node:fs/promises" -import * as path from "node:path" -import {once} from "node:events" -import {spawn} from "node:child_process" - -import artifact from "@actions/artifact" - -import { - checkSecretExpiration, - fail, - getAllowedPackages, - getDeployPayload, - parsePackageInfo, - run, -} from "./util.js" -import {projects} from "./config.js" - -run(async () => { - if (!process.env.GITHUB_EVENT_PATH) { - fail("::error::`GITHUB_EVENT_PATH` environment variable not set") - } - - checkSecretExpiration("NPM_TOKEN") - - const registry = "registry.npmjs.org" - const npmToken = process.env.INPUT_NPM_TOKEN - - const payload = await getDeployPayload() - - const allowedPackages = getAllowedPackages(payload.repo, projects.npm) - - console.log(`Downloading artifact from ${payload.repo}`) - const artifactFile = path.join(process.env.GITHUB_WORKSPACE, payload.tarballName) - - const [repositoryOwner, repositoryName] = payload.repo.split("/") - - await artifact.downloadArtifact(payload.artifactId, { - findBy: { - token: process.env.GITHUB_TOKEN, - repositoryOwner, - repositoryName, - workflowRunId: payload.workflowRunId, - }, - }) - - console.log("Extracting package info for validation") - let stdout - - const tarProcess = spawn("tar", [ - "--extract", - "--to-stdout", - `--file=${artifactFile}`, - "package/package.json", - ], { - stdio: [null, "pipe", null], - }) - - tarProcess.stdout.setEncoding("utf-8") - tarProcess.stdout.on("data", (chunk) => { - stdout += chunk - }) - - await Promise.all([ - once(tarProcess.stdout, "close"), - once(tarProcess, "exit"), - ]) - - if (tarProcess.exitCode) { - fail(`\`tar\` extraction failed with code ${tarProcess.exitCode}.`) - } - - if (tarProcess.signalCode) { - fail(`\`tar\` extraction failed with signal ${tarProcess.signalCode}.`) - } - - const packageInfo = await parsePackageInfo(artifactFile, stdout.received) - console.log(`Package ${packageInfo.name} detected`) - - if (!allowedPackages.has(packageInfo.name)) { - fail(`Refusing to publish ${packageInfo.name} as it is not allowlisted`) - } - - // Don't use `npm config set` here, so the token can remain secret. - console.log("Registering authorization token secret") - await fs.appendFile( - path.join(process.env.HOME, ".npmrc"), - `\n//${registry}/:_authToken=${npmToken}\n`, - {encoding: "utf-8"}, - ) - - console.log(`Publishing package ${packageInfo.name}`) - - const publishProcess = spawn("npm", ["publish", artifactFile]) - await once(publishProcess, "exit") - - if (publishProcess.exitCode) { - fail(`\`npm publish\` failed with code ${publishProcess.exitCode}.`) - } - - if (publishProcess.signalCode) { - fail(`\`npm publish\` failed with signal ${publishProcess.signalCode}.`) - } - - console.log(`Package ${packageInfo.name} published successfully`) -}) diff --git a/lib/entry/handle-deploy.js b/lib/entry/handle-deploy.js new file mode 100644 index 0000000..97c7efc --- /dev/null +++ b/lib/entry/handle-deploy.js @@ -0,0 +1,19 @@ +// This package assumes a site has already been built and the files exist in the current workspace +// If there's an artifact named `artifact.tar`, it can upload that to actions on its own, +// without the user having to do the tar process themselves. + +import {getDeployPayload, run} from "../util.js" + +run(async () => { + const payload = await getDeployPayload() + + if (payload.type === "npm") { + const {deployToNpm} = await import("../deploy/npm.js") + await deployToNpm(payload) + } else if (payload.type === "github-pages") { + const {deployToGitHubPages} = await import("../deploy/github-pages.js") + await deployToGitHubPages(payload) + } else { + throw new Error(`Unimplemented payload type: ${payload.type}`) + } +}) diff --git a/lib/entry/handle-remind.js b/lib/entry/handle-remind.js index a5ddc1b..6a5d8a7 100644 --- a/lib/entry/handle-remind.js +++ b/lib/entry/handle-remind.js @@ -1,6 +1,6 @@ import {localTokenExpiryDates, projects} from "../config.js" -import {getOctokit} from "../client.js" +import {getRequest} from "../client.js" const thresholdDays = 30 @@ -49,8 +49,8 @@ if (localNeedsRotated.length || repoNeedsRotated.length) { } } - const octokit = getOctokit(process.env.GITHUB_TOKEN) - await octokit.request("POST /repos/{owner}/{repo}/issues", { + const request = getRequest(process.env.GITHUB_TOKEN) + await request("POST /repos/{owner}/{repo}/issues", { owner: thisOwner, repo: thisRepo, title: "Secrets need rotated", diff --git a/lib/oidc-get-token.js b/lib/oidc-get-token.js index 1afc955..5094651 100644 --- a/lib/oidc-get-token.js +++ b/lib/oidc-get-token.js @@ -1,273 +1,198 @@ -export async function getIDToken(audience) { - // New ID Token is requested from action service - let idTokenUrl = process.env["ACTIONS_ID_TOKEN_REQUEST_URL"] - if (!idTokenUrl) { - throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable") - } +import * as http from "node:http" +import * as https from "node:https" +import {StringDecoder} from "node:string_decoder" +import {once} from "node:events" +import {setTimeout} from "node:timers/promises" - if (audience) { - const encodedAudience = encodeURIComponent(audience) - idTokenUrl = `${idTokenUrl}&audience=${encodedAudience}` - } +import {apiAgent} from "./api-client.js" +import {fail} from "./util.js" - console.log(`::debug::ID token url is ${idTokenUrl}`) +const MAX_TRIES = 10 +const EXPONENTIAL_BACKOFF_SCALE = 5 - let res +export async function getIDToken() { + let statusCode = 0 + let contents = "" + let result try { - res = await processResponse(await request(idTokenUrl)) - } catch (error) { - throw new Error( - `Failed to get ID Token. - Error Code: ${error.statusCode} - Error Message: ${error.message}` - ) - } - - const idToken = res.result?.value - if (!idToken) { - throw new Error("Response json body do not have ID Token field") - } + if (!process.env.ACTIONS_ID_TOKEN_REQUEST_URL) { + fail("Unable to get `ACTIONS_ID_TOKEN_REQUEST_URL` env variable") + } - console.log(`::add-mask::${idToken}`) + console.log(`::debug::ID token url is ${process.env.ACTIONS_ID_TOKEN_REQUEST_URL}`) - return idToken -} + if (!process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN) { + fail("Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable") + } -import * as http from "node:http" -import * as https from "node:https" + const idTokenUrl = new URL(process.env.ACTIONS_ID_TOKEN_REQUEST_URL) -import {getRequestContext} from "./api-client.js" + let numTries = 0 + let response -const HttpRedirectCodes = [301, 302, 303, 307, 308] -const HttpResponseRetryCodes = [502, 503, 504] -const ExponentialBackoffCeiling = 10 -const ExponentialBackoffTimeSlice = 5 -const maxTries = 10 + responseIsFinal: + for (;;) { + response = await requestRaw(idTokenUrl) -class HttpClientError extends Error { - name = "HttpClientError" - constructor(message, statusCode, result) { - super(message) - this.statusCode = statusCode - this.result = result - } -} + let redirectsRemaining = 50 + while ( + isRedirectCode(response.statusCode) && + redirectsRemaining > 0 && + // Don't redirect without a location. + response.headers.location + ) { + redirectsRemaining-- -async function readBody(message) { - return new Promise((resolve) => { - const chunks = [] + const redirectUrl = new URL(response.headers.location) - message.on("data", (chunk) => { - chunks.push(chunk) - }) + if (idTokenUrl.protocol === "https:" && redirectUrl.protocol !== "https:") { + fail([ + "Blocked HTTPS to HTTP redirect downgrade.", + `Source URL: ${idTokenUrl}`, + `Target URL: ${redirectUrl}`, + ]) + } - message.on("end", () => { - resolve(Buffer.concat(chunks).toString()) - }) - }) -} + // Drain the response before reassigning, so the old socket won't leak. + await dropResponse(response) -async function request(requestUrl) { - const requestToken = process.env["ACTIONS_ID_TOKEN_REQUEST_TOKEN"] - if (!requestToken) { - throw new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable") - } + // strip authorization header if redirected to a different hostname + if (redirectUrl.hostname !== idTokenUrl.hostname) { + break responseIsFinal + } - const {agent} = getRequestContext() - - const parsedUrl = new URL(requestUrl) - - const usingSsl = requestUrl.protocol === "https:" - const defaultPort = usingSsl ? 443 : 80 - - let info = { - httpModule: usingSsl ? https : http, - options: { - port: requestUrl.port ? parseInt(requestUrl.port, 10) : defaultPort, - path: `${requestUrl.pathname || ""}${requestUrl.search || ""}`, - method: "GET", - headers: { - "accept": "application/json", - "user-agent": "actions/oidc-client", - "authorization": `Bearer ${requestToken}`, - }, - agent, - }, - } + // let's make the request with the new redirectUrl + response = await requestRaw(redirectUrl) + } - // Only perform retries on reads since writes may not be idempotent. - let numTries = 0 + // If a successful response or not a retryable failure, return immediately + if (!isRetryableCode(response.statusCode)) { + break + } - let response - for (;;) { - response = await requestRaw(info) + // Check for retries. + if (++numTries > MAX_TRIES) { + break + } - // Check if it's an authentication challenge - if (response && response.statusCode === 403) { - return response + await dropResponse(response) + await setTimeout(EXPONENTIAL_BACKOFF_SCALE * 2 ** numTries) } - let redirectsRemaining = 50 - while ( - response.statusCode && - HttpRedirectCodes.includes(response.statusCode) && - redirectsRemaining > 0 - ) { - const redirectUrl = response.headers["location"] - if (!redirectUrl) { - // if there's no location to redirect to, we won't - break - } - const parsedRedirectUrl = new URL(redirectUrl) - if ( - parsedUrl.protocol === "https:" && - parsedUrl.protocol !== parsedRedirectUrl.protocol - ) { - throw new Error( - "Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true." - ) - } + statusCode = response.statusCode - // we need to finish reading the response before reassigning response - // which will leak the open socket. - await readBody(response) + if (statusCode !== 404) { + const decoder = new StringDecoder("utf-8") - // strip authorization header if redirected to a different hostname - if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { - return response + for await (const chunk of response) { + contents = `${contents}${decoder.write(chunk)}` } - // let's make the request with the new redirectUrl - const {agent} = getRequestContext() - - const usingSsl = parsedRedirectUrl.protocol === "https:" - const defaultPort = usingSsl ? 443 : 80 - info = { - httpModule: usingSsl ? https : http, - options: { - port: parsedRedirectUrl.port ? parseInt(parsedRedirectUrl.port, 10) : defaultPort, - path: `${parsedRedirectUrl.pathname || ""}${parsedRedirectUrl.search || ""}`, - method: "GET", - headers: info.options.headers, - agent, - }, - } - response = await requestRaw(info) - redirectsRemaining-- - } + contents = `${contents}${decoder.end()}` - if ( - !response.message.statusCode || - !HttpResponseRetryCodes.includes(response.message.statusCode) - ) { - // If not a retry code, return immediately instead of retrying - return response + try { + result = JSON.parse(contents) + } catch { + // Leave result as `undefined` if the response isn't valid JSON. + } } + } catch (error) { + fail([ + `Failed to get ID token (status code: ${statusCode})`, + error, + "Ensure GITHUB_TOKEN has permission \"id-token: write\".", + ]) + } - numTries += 1 + // 3xx redirects are handled by the http layer, so they don't need handled here. + if (statusCode > 299) { + fail(result?.message || [ + `Failed to get ID token (status code: ${statusCode})`, + // The error message might be in the response, so try that as a fallback. + ...(contents ? [contents] : []), + "Ensure GITHUB_TOKEN has permission \"id-token: write\".", + ]) + } - if (numTries > maxTries) break + if (result === null || typeof result !== "object") { + fail("Response body is not an object.") + } - await readBody(response) - await performExponentialBackoff(numTries) + if (!Object.hasOwn(result, "value")) { + fail("Response body lacks a \"value\" field, and thus an ID token.") } - return response -} + const idToken = result.value -async function requestRaw(info) { - return new Promise((resolve, reject) => { - let callbackCalled = false + if (typeof idToken !== "string") { + fail("Returned ID token is not a string") + } - const req = info.httpModule.request( - info.options, - (msg) => { - if (callbackCalled) return - callbackCalled = true - resolve(msg) - } - ) + console.log(`::add-mask::${idToken}`) + + return idToken +} - let socket - req.on("socket", (sock) => { - socket = sock - }) +function isRedirectCode(code) { + return code === 301 || code === 302 || code === 303 || code === 307 || code === 308 +} - // If we ever get disconnected, we want the socket to timeout eventually - req.setTimeout(3 * 60000, () => { - if (socket) { - socket.end() - } - if (callbackCalled) return - callbackCalled = true - reject(new Error(`Request timeout: ${info.options.path}`)) - }) - - // err has statusCode property - // res should have headers - req.on("error", (err) => { - if (callbackCalled) return - callbackCalled = true - reject(err) - }) - - req.end() - }) +function isRetryableCode(code) { + return code === 502 || code === 503 || code === 504 } -async function performExponentialBackoff(retryNumber) { - retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber) - const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber) - return new Promise((resolve) => setTimeout(() => resolve(), ms)) +function dropResponse(response) { + const endP = once(response, "end") + response.resume() + return endP } -async function processResponse(res) { - const statusCode = res.message.statusCode || 0 +async function requestRaw(targetUrl) { + const requestPath = `${targetUrl.pathname || ""}${targetUrl.search || ""}` + let requestPort = 443 + let httpModule = https - const response = { - statusCode, - result: null, - headers: {} + if (targetUrl.protocol === "http:") { + requestPort = 80 + httpModule = http } - // not found leads to null obj returned - if (statusCode === 404) { - return response + if (targetUrl.port) { + requestPort = Number.parseInt(targetUrl.port, 10) } - // get the result from the body - let obj - let contents + const req = httpModule.request({ + method: "GET", + agent: apiAgent, + headers: { + "accept": "application/json", + "user-agent": "actions/oidc-client", + "authorization": `Bearer ${process.env.ACTIONS_ID_TOKEN_REQUEST_TOKEN}`, + }, + port: requestPort, + path: requestPath, + // Time out after 3 minutes so it doesn't take forever. + timeout: 3 * 60_000, + }) - try { - contents = await readBody(res.message) - if (contents && contents.length > 0) { - obj = JSON.parse(contents) - response.result = obj - } + const ctrl = new AbortController() - response.headers = res.message.headers - } catch { - // Invalid resource (contents not json); leaving result obj null - } + /** @type {Promise<[undefined | http.IncomingMessage]>} */ + const responseP = Promise.race([ + once(req, "response", {signal: ctrl.signal}), + once(req, "timeout", {signal: ctrl.signal}), + ]).finally(() => ctrl.abort()) - // note that 3xx redirects are handled by the http layer. - if (statusCode > 299) { - let msg - - // if exception/error in body, attempt to get better error - if (obj && obj.message) { - msg = obj.message - } else if (contents && contents.length > 0) { - // it may be the case that the exception is in the body message as string - msg = contents - } else { - msg = `Failed request: (${statusCode})` - } + req.end() - throw new HttpClientError(msg, statusCode, response.result) - } else { - return response + /** @type {[undefined | http.IncomingMessage]} */ + const [response] = await responseP + + if (!response) { + req.socket?.end() + fail(`Request timeout: ${requestPath}`) } + + return response } diff --git a/lib/util.js b/lib/util.js index 62d6668..e330b70 100644 --- a/lib/util.js +++ b/lib/util.js @@ -44,18 +44,21 @@ export async function run(init) { await init() } catch (e) { process.exitCode = 1 + reportErrorOrFail(e) + } +} - if (e instanceof Fail) { - for (const detail of e.details) { - reportRunError(detail) - } - } else { - reportRunError(e) +function reportErrorOrFail(e) { + if (e instanceof Fail) { + for (const detail of e.details) { + reportErrorOrFail(detail) } + } else { + reportRunError(e) } } -function reportRunError(detail) { +export function reportRunError(detail) { if (detail instanceof Error) { detail = detail.stack.replace(/^/gm, "::error::") } else { @@ -77,28 +80,46 @@ export function checkSecretExpiration(name) { } /** - * @returns {Promise<{ - * owner: string; - * repo: string; - * type: string; - * artifactName: string; - * tarballName: string; - * artifactId: number; - * workflowRunId: number; - * }>} + * @typedef DeployPayload + * @property {string} repo + * @property {"npm" | "github-pages"} type + * @property {string} artifactName + * @property {string} tarballName + * @property {number} artifactId + * @property {number} workflowRunId + * @property {number} buildVersion */ -export async function getDeployPayload() { - const eventData = await fs.readFile(process.env.GITHUB_EVENT_PATH, "utf-8") - const payload = JSON.parse(eventData) - return extractJsonFields(undefined, "Payload", payload, { - owner: "string", + +/** + * @param {DeployPayload} payload + * @returns {DeployPayload} + */ +export function validateDeployPayload(payload) { + payload = extractJsonFields(undefined, "Payload", payload, { repo: "string", type: "string", artifactName: "string", tarballName: "string", artifactId: "number", workflowRunId: "number", + buildVersion: "number", }) + if (!(/^(?:npm|github-pages)$/).test(payload.type)) { + throw new Error(`Unknown payload type: ${payload.type}`) + } + return payload +} + +/** + * @returns {Promise} + */ +export async function getDeployPayload() { + if (!process.env.GITHUB_EVENT_PATH) { + fail("::error::`GITHUB_EVENT_PATH` environment variable not set") + } + + const eventData = await fs.readFile(process.env.GITHUB_EVENT_PATH, "utf-8") + return validateDeployPayload(JSON.parse(eventData)) } export function getAllowedPackages(repository, expectedMap) { @@ -152,10 +173,10 @@ function jsonFieldMatchesStringType(host, key, type) { * @param {unknown} object * @param {Spec} spec * @returns {{[P in keyof Spec]: JsonType[ - * Spec[P] extends unknown[] ? Spec[P][number] : Spec[P] + * Spec[P] extends Array ? Spec[P][number] : Spec[P] * ]}} */ -export function extractJsonFields(file, thing, object, spec) { +function extractJsonFields(file, thing, object, spec) { if (object === null || typeof object !== "object") { fail(`${thing} is not an object`, {file}) } @@ -164,7 +185,7 @@ export function extractJsonFields(file, thing, object, spec) { const errors = [] for (const [name, type] of Object.entries(spec)) { - let typeString = "" + let typeString if (Array.isArray(type)) { if (type.length === 0) { diff --git a/package-lock.json b/package-lock.json index 0c5af94..1261d1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,15 +7,20 @@ "name": "infra", "dependencies": { "@actions/artifact": "2.1.9", - "@actions/http-client": "^2.2.3", - "@octokit/core": "^6.1.2", - "tunnel": "^0.0.6", - "undici": "^6.19.8" + "@octokit/core": "6.1.2", + "@octokit/request": "9.1.3", + "@octokit/request-error": "6.1.4", + "tunnel": "0.0.6", + "undici": "6.19.8" }, "devDependencies": { - "@eslint/js": "^9.9.1", + "@eslint/js": "^9.10.0", + "@octokit/openapi-types": "22.2.0", + "@stylistic/eslint-plugin": "^2.8.0", "@types/node": "^22.5.4", - "eslint": "^9.9.1", + "@types/tunnel": "^0.0.7", + "eslint": "^9.10.0", + "eslint-plugin-n": "^17.10.2", "globals": "^15.9.0" } }, @@ -48,6 +53,14 @@ "@octokit/types": "^6.0.3" } }, + "node_modules/@actions/artifact/node_modules/@octokit/auth-token/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@actions/artifact/node_modules/@octokit/core": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", @@ -72,6 +85,14 @@ "once": "^1.4.0" } }, + "node_modules/@actions/artifact/node_modules/@octokit/core/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@actions/artifact/node_modules/@octokit/endpoint": { "version": "6.0.12", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", @@ -82,6 +103,14 @@ "universal-user-agent": "^6.0.0" } }, + "node_modules/@actions/artifact/node_modules/@octokit/endpoint/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@actions/artifact/node_modules/@octokit/graphql": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", @@ -92,6 +121,19 @@ "universal-user-agent": "^6.0.0" } }, + "node_modules/@actions/artifact/node_modules/@octokit/graphql/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@actions/artifact/node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, "node_modules/@actions/artifact/node_modules/@octokit/request": { "version": "5.6.3", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", @@ -105,6 +147,19 @@ "universal-user-agent": "^6.0.0" } }, + "node_modules/@actions/artifact/node_modules/@octokit/request-error": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", + "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/@actions/artifact/node_modules/@octokit/request/node_modules/@octokit/request-error": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", @@ -115,6 +170,14 @@ "once": "^1.4.0" } }, + "node_modules/@actions/artifact/node_modules/@octokit/request/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@actions/artifact/node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -187,17 +250,10 @@ "universal-user-agent": "^6.0.0" } }, - "node_modules/@actions/github/node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.16.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", - "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", - "dependencies": { - "@octokit/types": "^6.39.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } + "node_modules/@actions/github/node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" }, "node_modules/@actions/github/node_modules/@octokit/request": { "version": "5.6.3", @@ -222,6 +278,14 @@ "once": "^1.4.0" } }, + "node_modules/@actions/github/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@actions/github/node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", @@ -534,28 +598,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/eslintrc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", @@ -579,16 +621,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -601,22 +633,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/@eslint/js": { - "version": "9.9.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.9.1.tgz", - "integrity": "sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -631,6 +651,18 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/plugin-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", + "dev": true, + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@fastify/busboy": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", @@ -681,6 +713,31 @@ "node": ">=12" } }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -741,30 +798,6 @@ "node": ">= 18" } }, - "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", - "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" - }, - "node_modules/@octokit/core/node_modules/@octokit/request-error": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", - "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", - "dependencies": { - "@octokit/types": "^13.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", - "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", - "dependencies": { - "@octokit/openapi-types": "^22.2.0" - } - }, "node_modules/@octokit/endpoint": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", @@ -777,19 +810,6 @@ "node": ">= 18" } }, - "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", - "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" - }, - "node_modules/@octokit/endpoint/node_modules/@octokit/types": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", - "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", - "dependencies": { - "@octokit/openapi-types": "^22.2.0" - } - }, "node_modules/@octokit/graphql": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", @@ -803,24 +823,11 @@ "node": ">= 18" } }, - "node_modules/@octokit/graphql/node_modules/@octokit/openapi-types": { + "node_modules/@octokit/openapi-types": { "version": "22.2.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" }, - "node_modules/@octokit/graphql/node_modules/@octokit/types": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", - "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", - "dependencies": { - "@octokit/openapi-types": "^22.2.0" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "12.11.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", - "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" - }, "node_modules/@octokit/plugin-paginate-rest": { "version": "2.21.3", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", @@ -832,6 +839,19 @@ "@octokit/core": ">=2" } }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@octokit/plugin-request-log": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", @@ -840,6 +860,31 @@ "@octokit/core": ">=3" } }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@octokit/plugin-retry": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", @@ -849,6 +894,19 @@ "bottleneck": "^2.15.3" } }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==" + }, + "node_modules/@octokit/plugin-retry/node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, "node_modules/@octokit/request": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", @@ -864,24 +922,17 @@ } }, "node_modules/@octokit/request-error": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", - "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", + "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", "dependencies": { - "@octokit/types": "^13.1.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^13.0.0" }, "engines": { "node": ">= 18" } }, - "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", - "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" - }, - "node_modules/@octokit/request-error/node_modules/@octokit/types": { + "node_modules/@octokit/types": { "version": "13.5.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", @@ -889,46 +940,14 @@ "@octokit/openapi-types": "^22.2.0" } }, - "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { - "version": "22.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", - "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" - }, - "node_modules/@octokit/request/node_modules/@octokit/request-error": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.4.tgz", - "integrity": "sha512-VpAhIUxwhWZQImo/dWAN/NpPqqojR6PSLgLYAituLM6U+ddx9hCioFGwBr5Mi+oi5CLeJkcAs3gJ0PYYzU6wUg==", - "dependencies": { - "@octokit/types": "^13.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/request/node_modules/@octokit/types": { - "version": "13.5.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.5.0.tgz", - "integrity": "sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==", - "dependencies": { - "@octokit/openapi-types": "^22.2.0" - } - }, - "node_modules/@octokit/types": { - "version": "6.41.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", - "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", - "dependencies": { - "@octokit/openapi-types": "^12.11.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, - "engines": { - "node": ">=14" - } + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } }, "node_modules/@protobuf-ts/plugin": { "version": "2.9.4", @@ -976,6 +995,25 @@ "@protobuf-ts/runtime": "^2.9.4" } }, + "node_modules/@stylistic/eslint-plugin": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.8.0.tgz", + "integrity": "sha512-Ufvk7hP+bf+pD35R/QfunF793XlSRIC7USr3/EdgduK9j13i2JjmsM0LUz3/foS+jDYp2fzyWZA9N44CPur0Ow==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^8.4.0", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, "node_modules/@types/node": { "version": "22.5.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz", @@ -985,6 +1023,174 @@ "undici-types": "~6.19.2" } }, + "node_modules/@types/tunnel": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.7.tgz", + "integrity": "sha512-VYKjZSmb2PvUwXoux4Gy4LAk7kzOB1ktkjyL4lxvpkqL2adgR+Qrh/yFyWluvJgIXWFicqs7XuzPI2NbTO/r3Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.5.0.tgz", + "integrity": "sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.5.0.tgz", + "integrity": "sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.5.0.tgz", + "integrity": "sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/visitor-keys": "8.5.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.5.0.tgz", + "integrity": "sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.5.0", + "@typescript-eslint/types": "8.5.0", + "@typescript-eslint/typescript-estree": "8.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.5.0.tgz", + "integrity": "sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.5.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -1045,22 +1251,22 @@ } }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=8" } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -1169,11 +1375,24 @@ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==" }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, "node_modules/buffer": { @@ -1260,21 +1479,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1367,11 +1571,11 @@ "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1405,15 +1609,6 @@ "dot-object": "bin/dot-object" } }, - "node_modules/dot-object/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, "node_modules/dot-object/node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -1434,17 +1629,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/dot-object/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1455,6 +1639,19 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1468,16 +1665,17 @@ } }, "node_modules/eslint": { - "version": "9.9.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.9.1.tgz", - "integrity": "sha512-dHvhrbfr4xFQ9/dq+jcVneZMyRYLjggWjk6RVsIiHsP8Rz6yZ8LvZ//iU4TrZF+SXWG+JkNF2OyiZRvzgRDqMg==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", "@eslint/config-array": "^0.18.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.9.1", + "@eslint/js": "9.10.0", + "@eslint/plugin-kit": "^0.1.0", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.3.0", "@nodelib/fs.walk": "^1.2.8", @@ -1500,7 +1698,6 @@ "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", @@ -1512,18 +1709,103 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-compat-utils": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz", + "integrity": "sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==", + "dev": true, + "dependencies": { + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/eslint-plugin-es-x": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz", + "integrity": "sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/ota-meshi", + "https://opencollective.com/eslint" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.1.2", + "@eslint-community/regexpp": "^4.11.0", + "eslint-compat-utils": "^0.5.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": ">=8" + } + }, + "node_modules/eslint-plugin-n": { + "version": "17.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.10.2.tgz", + "integrity": "sha512-e+s4eAf5NtJaxPhTNu3qMO0Iz40WANS93w9LQgYcvuljgvDmWi/a3rh+OrNyMHeng6aOWGJO0rCg5lH4zi8yTw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "enhanced-resolve": "^5.17.0", + "eslint-plugin-es-x": "^7.5.0", + "get-tsconfig": "^4.7.0", + "globals": "^15.8.0", + "ignore": "^5.2.4", + "minimatch": "^9.0.5", + "semver": "^7.5.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": ">=8.23.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/eslint-plugin-n/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/eslint-scope": { @@ -1554,49 +1836,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/espree": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", @@ -1683,6 +1922,34 @@ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==" }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1737,6 +2004,18 @@ "node": ">=16.0.0" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1792,6 +2071,18 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/get-tsconfig": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.0.tgz", + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -1823,6 +2114,28 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "15.9.0", "resolved": "https://registry.npmjs.org/globals/-/globals-15.9.0.tgz", @@ -1970,6 +2283,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", @@ -2156,18 +2478,49 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/minimist": { @@ -2198,9 +2551,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -2370,6 +2723,18 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==" }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2463,6 +2828,14 @@ "minimatch": "^5.1.0" } }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, "node_modules/readdir-glob/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", @@ -2483,6 +2856,15 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -2535,6 +2917,18 @@ } ] }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2616,31 +3010,23 @@ "node": ">=8" } }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/strip-ansi": { + "node_modules/string-width/node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", @@ -2654,8 +3040,7 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", @@ -2666,10 +3051,14 @@ "node": ">=8" } }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } @@ -2703,6 +3092,15 @@ "node": ">=8" } }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/tar-stream": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", @@ -2727,6 +3125,18 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -2934,28 +3344,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2974,15 +3362,40 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dependencies": { - "ansi-regex": "^5.0.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/wrappy": { diff --git a/package.json b/package.json index 9e0debd..820e2af 100644 --- a/package.json +++ b/package.json @@ -2,18 +2,25 @@ "name": "infra", "private": true, "type": "module", - "main": "index.js", "dependencies": { "@actions/artifact": "2.1.9", - "@actions/http-client": "^2.2.3", - "@octokit/core": "^6.1.2", - "tunnel": "^0.0.6", - "undici": "^6.19.8" + "@octokit/core": "6.1.2", + "@octokit/request": "9.1.3", + "@octokit/request-error": "6.1.4", + "tunnel": "0.0.6", + "undici": "6.19.8" }, "devDependencies": { - "@eslint/js": "^9.9.1", + "@eslint/js": "^9.10.0", + "@octokit/openapi-types": "22.2.0", + "@stylistic/eslint-plugin": "^2.8.0", "@types/node": "^22.5.4", - "eslint": "^9.9.1", + "@types/tunnel": "^0.0.7", + "eslint": "^9.10.0", + "eslint-plugin-n": "^17.10.2", "globals": "^15.9.0" + }, + "engines": { + "node": ">=20.0.0" } }