From 0dd535dc3a18f5442fae3b693235f1c481633c24 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Mon, 6 Jan 2025 11:00:43 -0500 Subject: [PATCH 1/8] Rename stub files --- __tests__/command.test.ts | 4 ++-- __tests__/commands/run.test.ts | 4 ++-- __tests__/index.test.ts | 4 ++-- __tests__/stubs/core-stubs.test.ts | 4 ++-- __tests__/stubs/env-stubs.test.ts | 4 ++-- __tests__/stubs/summary-stubs.test.ts | 4 ++-- __tests__/utils/output.test.ts | 4 ++-- src/command.ts | 2 +- src/commands/run.ts | 4 ++-- src/stubs/{core-stubs.ts => core.ts} | 4 ++-- src/stubs/{env-stubs.ts => env.ts} | 0 src/stubs/{summary-stubs.ts => summary.ts} | 2 +- src/utils/package.ts | 2 +- 13 files changed, 21 insertions(+), 21 deletions(-) rename src/stubs/{core-stubs.ts => core.ts} (99%) rename src/stubs/{env-stubs.ts => env.ts} (100%) rename src/stubs/{summary-stubs.ts => summary.ts} (99%) diff --git a/__tests__/command.test.ts b/__tests__/command.test.ts index c97c12b..cd318bc 100644 --- a/__tests__/command.test.ts +++ b/__tests__/command.test.ts @@ -1,7 +1,7 @@ import { jest } from '@jest/globals' import { Command } from 'commander' -import { ResetCoreMetadata } from '../src/stubs/core-stubs.js' -import { ResetEnvMetadata } from '../src/stubs/env-stubs.js' +import { ResetCoreMetadata } from '../src/stubs/core.js' +import { ResetEnvMetadata } from '../src/stubs/env.js' const action = jest.fn() diff --git a/__tests__/commands/run.test.ts b/__tests__/commands/run.test.ts index 48ef01f..c4ef3a1 100644 --- a/__tests__/commands/run.test.ts +++ b/__tests__/commands/run.test.ts @@ -1,7 +1,7 @@ import { jest } from '@jest/globals' import * as core from '../../__fixtures__/core.js' -import { ResetCoreMetadata } from '../../src/stubs/core-stubs.js' -import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs.js' +import { ResetCoreMetadata } from '../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env.js' const quibbleEsm = jest.fn().mockImplementation(() => {}) const quibbleDefault = jest.fn().mockImplementation(() => {}) diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index 71054b2..8fd6e38 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -1,6 +1,6 @@ import { jest } from '@jest/globals' -import { ResetCoreMetadata } from '../src/stubs/core-stubs.js' -import { ResetEnvMetadata } from '../src/stubs/env-stubs.js' +import { ResetCoreMetadata } from '../src/stubs/core.js' +import { ResetEnvMetadata } from '../src/stubs/env.js' const makeProgram = jest.fn().mockResolvedValue({ parse: jest.fn() diff --git a/__tests__/stubs/core-stubs.test.ts b/__tests__/stubs/core-stubs.test.ts index aba859b..f206f32 100644 --- a/__tests__/stubs/core-stubs.test.ts +++ b/__tests__/stubs/core-stubs.test.ts @@ -28,8 +28,8 @@ import { toPosixPath, toWin32Path, warning -} from '../../src/stubs/core-stubs.js' -import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs.js' +} from '../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env.js' import type { CoreMetadata } from '../../src/types.js' /** Empty CoreMetadata Object */ diff --git a/__tests__/stubs/env-stubs.test.ts b/__tests__/stubs/env-stubs.test.ts index 681affb..15aac69 100644 --- a/__tests__/stubs/env-stubs.test.ts +++ b/__tests__/stubs/env-stubs.test.ts @@ -1,6 +1,6 @@ import { jest } from '@jest/globals' -import { ResetCoreMetadata } from '../../src/stubs/core-stubs.js' -import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env-stubs.js' +import { ResetCoreMetadata } from '../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../src/stubs/env.js' import type { EnvMetadata } from '../../src/types.js' /** Empty EnvMetadata Object */ diff --git a/__tests__/stubs/summary-stubs.test.ts b/__tests__/stubs/summary-stubs.test.ts index 09c4576..72abc45 100644 --- a/__tests__/stubs/summary-stubs.test.ts +++ b/__tests__/stubs/summary-stubs.test.ts @@ -2,8 +2,8 @@ import { jest } from '@jest/globals' import fs from 'fs' import { EOL } from 'os' import path from 'path' -import { CoreMeta, ResetCoreMetadata } from '../../src/stubs/core-stubs.js' -import { Summary } from '../../src/stubs/summary-stubs.js' +import { CoreMeta, ResetCoreMetadata } from '../../src/stubs/core.js' +import { Summary } from '../../src/stubs/summary.js' let summary: Summary = new Summary() diff --git a/__tests__/utils/output.test.ts b/__tests__/utils/output.test.ts index 8dd030a..449138e 100644 --- a/__tests__/utils/output.test.ts +++ b/__tests__/utils/output.test.ts @@ -1,6 +1,6 @@ import { jest } from '@jest/globals' -import { ResetCoreMetadata } from '../../src/stubs/core-stubs.js' -import { ResetEnvMetadata } from '../../src/stubs/env-stubs.js' +import { ResetCoreMetadata } from '../../src/stubs/core.js' +import { ResetEnvMetadata } from '../../src/stubs/env.js' import { printTitle } from '../../src/utils/output.js' // Prevent output during tests diff --git a/src/command.ts b/src/command.ts index 8a8c48a..75d6923 100644 --- a/src/command.ts +++ b/src/command.ts @@ -2,7 +2,7 @@ import { Command, InvalidArgumentError } from 'commander' import { dirname } from 'node:path' import { fileURLToPath } from 'node:url' import { action } from './commands/run.js' -import { EnvMeta } from './stubs/env-stubs.js' +import { EnvMeta } from './stubs/env.js' /** * Creates the program for the CLI diff --git a/src/commands/run.ts b/src/commands/run.ts index 9deb991..33f34d3 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -1,8 +1,8 @@ import { config } from 'dotenv' import { createRequire } from 'module' import quibble from 'quibble' -import { CORE_STUBS, CoreMeta } from '../stubs/core-stubs.js' -import { EnvMeta } from '../stubs/env-stubs.js' +import { CORE_STUBS, CoreMeta } from '../stubs/core.js' +import { EnvMeta } from '../stubs/env.js' import type { Action } from '../types.js' import { printTitle } from '../utils/output.js' import { isESM } from '../utils/package.js' diff --git a/src/stubs/core-stubs.ts b/src/stubs/core.ts similarity index 99% rename from src/stubs/core-stubs.ts rename to src/stubs/core.ts index f4b4b10..0f173e3 100644 --- a/src/stubs/core-stubs.ts +++ b/src/stubs/core.ts @@ -4,8 +4,8 @@ import type { CoreMetadata, InputOptions } from '../types.js' -import { EnvMeta } from './env-stubs.js' -import { Summary } from './summary-stubs.js' +import { EnvMeta } from './env.js' +import { Summary } from './summary.js' export const CORE_STUBS = { addPath, diff --git a/src/stubs/env-stubs.ts b/src/stubs/env.ts similarity index 100% rename from src/stubs/env-stubs.ts rename to src/stubs/env.ts diff --git a/src/stubs/summary-stubs.ts b/src/stubs/summary.ts similarity index 99% rename from src/stubs/summary-stubs.ts rename to src/stubs/summary.ts index 662ba18..b54681a 100644 --- a/src/stubs/summary-stubs.ts +++ b/src/stubs/summary.ts @@ -6,7 +6,7 @@ import type { SummaryTableRow, SummaryWriteOptions } from '../types.js' -import { CoreMeta } from './core-stubs.js' +import { CoreMeta } from './core.js' /** * A class for creating and writing job step summaries. diff --git a/src/utils/package.ts b/src/utils/package.ts index e538353..37723ce 100644 --- a/src/utils/package.ts +++ b/src/utils/package.ts @@ -1,6 +1,6 @@ import fs from 'fs' import * as path from 'path' -import { EnvMeta } from '../stubs/env-stubs.js' +import { EnvMeta } from '../stubs/env.js' /** * Checks if the JavaScript/TypeScript project is an ESM module. From 70af942894522de756b72ad3c724c4e96ae61658 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Tue, 7 Jan 2025 12:34:27 -0500 Subject: [PATCH 2/8] Initial @actions/artifact stub --- .env.example | 7 + docs/supported-functionality.md | 28 +- package-lock.json | 1855 ++++++++++++++++- package.json | 10 +- src/commands/run.ts | 23 + src/stubs/artifact/artifact.ts | 16 + src/stubs/artifact/internal/client.ts | 312 +++ .../internal/delete/delete-artifact.ts | 108 + .../internal/download/download-artifact.ts | 223 ++ .../artifact/internal/find/get-artifact.ts | 115 + .../artifact/internal/find/list-artifacts.ts | 146 ++ .../artifact/internal/find/retry-options.ts | 48 + src/stubs/artifact/internal/shared/config.ts | 34 + src/stubs/artifact/internal/shared/errors.ts | 49 + .../artifact/internal/shared/interfaces.ts | 94 + .../artifact/internal/shared/user-agent.ts | 6 + .../path-and-artifact-name-validation.ts | 86 + .../internal/upload/upload-artifact.ts | 111 + .../upload/upload-zip-specification.ts | 124 ++ src/stubs/artifact/internal/upload/zip.ts | 111 + src/stubs/env.ts | 2 + src/types.ts | 5 + 22 files changed, 3445 insertions(+), 68 deletions(-) create mode 100644 src/stubs/artifact/artifact.ts create mode 100644 src/stubs/artifact/internal/client.ts create mode 100644 src/stubs/artifact/internal/delete/delete-artifact.ts create mode 100644 src/stubs/artifact/internal/download/download-artifact.ts create mode 100644 src/stubs/artifact/internal/find/get-artifact.ts create mode 100644 src/stubs/artifact/internal/find/list-artifacts.ts create mode 100644 src/stubs/artifact/internal/find/retry-options.ts create mode 100644 src/stubs/artifact/internal/shared/config.ts create mode 100644 src/stubs/artifact/internal/shared/errors.ts create mode 100644 src/stubs/artifact/internal/shared/interfaces.ts create mode 100644 src/stubs/artifact/internal/shared/user-agent.ts create mode 100644 src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts create mode 100644 src/stubs/artifact/internal/upload/upload-artifact.ts create mode 100644 src/stubs/artifact/internal/upload/upload-zip-specification.ts create mode 100644 src/stubs/artifact/internal/upload/zip.ts diff --git a/.env.example b/.env.example index fddaa98..d667ed1 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,13 @@ ACTIONS_STEP_DEBUG=true # Hyphens should not be converted to underscores! INPUT_MILLISECONDS=2400 +# Environment variables specific to the @github/local-action tool. +# +# LOCAL_ACTION_ARTIFACT_PATH: Local path where any artifacts will be saved. Will +# throw an error if the action attempts to use the +# @actions/artifact package without setting this. +LOCAL_ACTION_ARTIFACT_PATH="" + # GitHub Actions default environment variables. These are set for every run of a # workflow and can be used in your actions. Setting the value here will override # any value set by the local-action tool. diff --git a/docs/supported-functionality.md b/docs/supported-functionality.md index 27449b5..7317276 100644 --- a/docs/supported-functionality.md +++ b/docs/supported-functionality.md @@ -10,6 +10,33 @@ whether or not they are currently supported by `local-action`. > JavaScript/TypeScript actions, it is assumed that they are using the > [GitHub Actions Toolkit](https://github.com/actions/toolkit). +## [`@actions/artifact`](https://github.com/actions/toolkit/blob/main/packages/artifact/README.md) + +The stubbed version of `@actions/artifact` functions similarly to the real +package. However, any artifacts that are created as part of a `local-action` run +will be stored on your local workstation. The specific path must be set using +the `LOCAL_ACTION_ARTIFACT_PATH` environment variable in the `.env` file passed +to the `local-action` command. + +> [!NOTE] +> +> If this variable is not set, and you attempt to interact with +> `@actions/artifact`, you will receive an error message. + +| Feature | Supported | Notes | +| -------------------- | ------------------ | ------------------------------ | +| `deleteArtifact()` | :white_check_mark: | | +| `downloadArtifact()` | :white_check_mark: | | +| `getArtifact()` | :white_check_mark: | | +| `listArtifacts()` | :white_check_mark: | | +| `uploadArtifact()` | :white_check_mark: | Retention settings are ignored | + +> [!IMPORTANT] +> +> When working with artifacts that were created as part of actual GitHub Actions +> workflow runs (e.g. if you try to download an artifact from a different +> repository), these requests **will be passed to the GitHub API**. + ## [`@actions/core`](https://github.com/actions/toolkit/blob/main/packages/core/README.md) | Feature | Supported | Notes | @@ -46,7 +73,6 @@ whether or not they are currently supported by `local-action`. The following packages are under investigation for how to integrate with `local-action`. Make sure to check back later! -- [`@actions/artifact`](https://github.com/actions/toolkit/tree/main/packages/artifact) - [`@actions/attest`](https://github.com/actions/toolkit/tree/main/packages/attest) - [`@actions/cache`](https://github.com/actions/toolkit/tree/main/packages/cache) diff --git a/package-lock.json b/package-lock.json index 08d4d07..c5170b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,13 @@ "version": "2.2.1", "license": "MIT", "dependencies": { + "@actions/artifact": "^2.2.0", "@actions/core": "^1.11.1", - "@octokit/core": "^6.1.2", + "@actions/github": "^6.0.0", + "@actions/http-client": "^2.2.3", + "@octokit/core": "^6.1.3", + "@octokit/plugin-retry": "^7.1.2", + "archiver": "^7.0.1", "chalk": "^5.3.0", "commander": "^13.0.0", "dotenv": "^16.4.5", @@ -19,6 +24,7 @@ "tsconfig-paths": "^4.2.0", "tsx": "^4.19.2", "typescript": "^5.6.3", + "unzip-stream": "^0.3.4", "yaml": "^2.6.1" }, "bin": { @@ -27,9 +33,11 @@ "devDependencies": { "@eslint/compat": "^1.2.3", "@jest/globals": "^29.7.0", + "@types/archiver": "^6.0.3", "@types/figlet": "^1.7.0", "@types/jest": "^29.5.14", "@types/node": "^22.9.1", + "@types/unzip-stream": "^0.3.4", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "eslint": "^9.15.0", @@ -51,6 +59,217 @@ "node": ">=20" } }, + "node_modules/@actions/artifact": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-2.2.0.tgz", + "integrity": "sha512-nDEyBsphN148zHe6ihq1a/UX92MDgC2GS9XmeFx2xs/wztZxzARYllviiP5U1nTDp2n9dEhnUig9RP9eSDcU5g==", + "license": "MIT", + "dependencies": { + "@actions/core": "^1.10.0", + "@actions/github": "^5.1.1", + "@actions/http-client": "^2.1.0", + "@azure/storage-blob": "^12.15.0", + "@octokit/core": "^3.5.1", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-retry": "^3.0.9", + "@octokit/request-error": "^5.0.0", + "@protobuf-ts/plugin": "^2.2.3-alpha.1", + "archiver": "^7.0.1", + "jwt-decode": "^3.1.2", + "twirp-ts": "^2.5.0", + "unzip-stream": "^0.3.1" + } + }, + "node_modules/@actions/artifact/node_modules/@actions/github": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz", + "integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==", + "license": "MIT", + "dependencies": { + "@actions/http-client": "^2.0.1", + "@octokit/core": "^3.6.0", + "@octokit/plugin-paginate-rest": "^2.17.0", + "@octokit/plugin-rest-endpoint-methods": "^5.13.0" + } + }, + "node_modules/@actions/artifact/node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "license": "MIT", + "dependencies": { + "@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==", + "license": "MIT", + "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", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@actions/artifact/node_modules/@octokit/core/node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "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==", + "license": "MIT", + "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", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "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==", + "license": "MIT", + "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", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "node_modules/@actions/artifact/node_modules/@octokit/plugin-retry": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-3.0.9.tgz", + "integrity": "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3", + "bottleneck": "^2.15.3" + } + }, + "node_modules/@actions/artifact/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==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@actions/artifact/node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "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==", + "license": "MIT", + "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", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "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==", + "license": "MIT", + "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", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "license": "Apache-2.0" + }, + "node_modules/@actions/artifact/node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "license": "ISC" + }, "node_modules/@actions/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", @@ -70,6 +289,167 @@ "@actions/io": "^1.0.1" } }, + "node_modules/@actions/github": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.0.tgz", + "integrity": "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==", + "license": "MIT", + "dependencies": { + "@actions/http-client": "^2.2.0", + "@octokit/core": "^5.0.1", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0" + } + }, + "node_modules/@actions/github/node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@actions/github/node_modules/@octokit/core": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", + "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@actions/github/node_modules/@octokit/endpoint": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.5.tgz", + "integrity": "sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@actions/github/node_modules/@octokit/graphql": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", + "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.3.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@actions/github/node_modules/@octokit/openapi-types": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", + "license": "MIT" + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.1.tgz", + "integrity": "sha512-wfGhE/TAkXZRLjksFXuDZdmGnJQHvtU/joFQdweXUgzo1XwvBCD4o4+75NtFfjfLK5IwLf9vHTfSiU3sLRYpRw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@actions/github/node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { + "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "node_modules/@actions/github/node_modules/@octokit/request": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.0.tgz", + "integrity": "sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@actions/github/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==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", + "license": "Apache-2.0" + }, + "node_modules/@actions/github/node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", + "license": "ISC" + }, "node_modules/@actions/http-client": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", @@ -100,6 +480,184 @@ "node": ">=6.0.0" } }, + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", + "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-http-compat": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz", + "integrity": "sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.3.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.1.tgz", + "integrity": "sha512-/wS73UEDrxroUEVywEm7J0p2c+IIiVxyfigCGfsKvCxxCET4V/Hef2aURqltrXMRjNmdmt5IuOgIpl8f6xdO5A==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", + "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", + "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-xml": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.4.4.tgz", + "integrity": "sha512-J4FYAqakGXcbfeZjwjMzjNcpcH4E+JtEBv+xcV1yL0Ydn/6wbQfeFKTCHh9wttAi0lmajHw7yBbHPRG+YHckZQ==", + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.1.4.tgz", + "integrity": "sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.26.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.26.0.tgz", + "integrity": "sha512-SriLPKezypIsiZ+TtlFfE46uuBIap2HeaQVS78e1P7rz5OSbq0rsd52WE1mC5f7vAeLiXqv7I7oRhL3WFZEw3Q==", + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.4.0", + "@azure/core-client": "^1.6.2", + "@azure/core-http-compat": "^2.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-rest-pipeline": "^1.10.1", + "@azure/core-tracing": "^1.1.2", + "@azure/core-util": "^1.6.1", + "@azure/core-xml": "^1.4.3", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", @@ -1324,6 +1882,102 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "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==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/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==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -2009,16 +2663,16 @@ } }, "node_modules/@octokit/core": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", - "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.3.tgz", + "integrity": "sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==", "license": "MIT", "dependencies": { "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.0.0", - "@octokit/request": "^9.0.0", - "@octokit/request-error": "^6.0.1", - "@octokit/types": "^13.0.0", + "@octokit/graphql": "^8.1.2", + "@octokit/request": "^9.1.4", + "@octokit/request-error": "^6.1.6", + "@octokit/types": "^13.6.2", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" }, @@ -2027,12 +2681,12 @@ } }, "node_modules/@octokit/endpoint": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", - "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.2.tgz", + "integrity": "sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.0.0", + "@octokit/types": "^13.6.2", "universal-user-agent": "^7.0.2" }, "engines": { @@ -2040,13 +2694,13 @@ } }, "node_modules/@octokit/graphql": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", - "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.2.tgz", + "integrity": "sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==", "license": "MIT", "dependencies": { - "@octokit/request": "^9.0.0", - "@octokit/types": "^13.0.0", + "@octokit/request": "^9.1.4", + "@octokit/types": "^13.6.2", "universal-user-agent": "^7.0.0" }, "engines": { @@ -2059,15 +2713,97 @@ "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==", "license": "MIT" }, + "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", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "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", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "license": "MIT", + "peerDependencies": { + "@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==", + "license": "MIT", + "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==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@octokit/plugin-retry": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-7.1.2.tgz", + "integrity": "sha512-XOWnPpH2kJ5VTwozsxGurw+svB2e61aWlmk5EVIYZPwFK5F9h4cyPyj9CIKRyMXMHSwpIsI3mPOdpMmrRhe7UQ==", + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^6.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, "node_modules/@octokit/request": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", - "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "version": "9.1.4", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.4.tgz", + "integrity": "sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==", "license": "MIT", "dependencies": { "@octokit/endpoint": "^10.0.0", "@octokit/request-error": "^6.0.1", - "@octokit/types": "^13.1.0", + "@octokit/types": "^13.6.2", + "fast-content-type-parse": "^2.0.0", "universal-user-agent": "^7.0.2" }, "engines": { @@ -2075,26 +2811,36 @@ } }, "node_modules/@octokit/request-error": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz", - "integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.6.tgz", + "integrity": "sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.0.0" + "@octokit/types": "^13.6.2" }, "engines": { "node": ">= 18" } }, "node_modules/@octokit/types": { - "version": "13.6.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.1.tgz", - "integrity": "sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==", + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.2.tgz", + "integrity": "sha512-WpbZfZUcZU77DrSW4wbsSgTPfKcp286q3ItaIgvSbBpZJlu6mnYXAkjZz6LVZPXkEvLIM8McanyZejKTYUHipA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^22.2.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==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -2108,6 +2854,83 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@protobuf-ts/plugin": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.9.4.tgz", + "integrity": "sha512-Db5Laq5T3mc6ERZvhIhkj1rn57/p8gbWiCKxQWbZBBl20wMuqKoHbRw4tuD7FyXi+IkwTToaNVXymv5CY3E8Rw==", + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/plugin-framework": "^2.9.4", + "@protobuf-ts/protoc": "^2.9.4", + "@protobuf-ts/runtime": "^2.9.4", + "@protobuf-ts/runtime-rpc": "^2.9.4", + "typescript": "^3.9" + }, + "bin": { + "protoc-gen-dump": "bin/protoc-gen-dump", + "protoc-gen-ts": "bin/protoc-gen-ts" + } + }, + "node_modules/@protobuf-ts/plugin-framework": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin-framework/-/plugin-framework-2.9.4.tgz", + "integrity": "sha512-9nuX1kjdMliv+Pes8dQCKyVhjKgNNfwxVHg+tx3fLXSfZZRcUHMc1PMwB9/vTvc6gBKt9QGz5ERqSqZc0++E9A==", + "license": "(Apache-2.0 AND BSD-3-Clause)", + "dependencies": { + "@protobuf-ts/runtime": "^2.9.4", + "typescript": "^3.9" + } + }, + "node_modules/@protobuf-ts/plugin-framework/node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@protobuf-ts/plugin/node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/@protobuf-ts/protoc": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.9.4.tgz", + "integrity": "sha512-hQX+nOhFtrA+YdAXsXEDrLoGJqXHpgv4+BueYF0S9hy/Jq0VRTVlJS1Etmf4qlMt/WdigEes5LOd/LDzui4GIQ==", + "license": "Apache-2.0", + "bin": { + "protoc": "protoc.js" + } + }, + "node_modules/@protobuf-ts/runtime": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.9.4.tgz", + "integrity": "sha512-vHRFWtJJB/SiogWDF0ypoKfRIZ41Kq+G9cEFj6Qm1eQaAhJ1LDFvgZ7Ja4tb3iLOQhz0PaoPnnOijF1qmEqTxg==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@protobuf-ts/runtime-rpc": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.9.4.tgz", + "integrity": "sha512-y9L9JgnZxXFqH5vD4d7j9duWvIJ7AShyBRoNKJGhu9Q27qIbchfzli66H9RvrQNIFk5ER7z1Twe059WZGqERcA==", + "license": "Apache-2.0", + "dependencies": { + "@protobuf-ts/runtime": "^2.9.4" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -2170,6 +2993,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/archiver": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/archiver/-/archiver-6.0.3.tgz", + "integrity": "sha512-a6wUll6k3zX6qs5KlxIggs1P1JcYJaTCx2gnlr+f0S1yd2DoaEwoIK10HmBaLnZwWneBz+JBm0dwcZu0zECBcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/readdir-glob": "*" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2301,6 +3134,16 @@ "undici-types": "~6.20.0" } }, + "node_modules/@types/readdir-glob": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", + "integrity": "sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2308,6 +3151,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/unzip-stream": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@types/unzip-stream/-/unzip-stream-0.3.4.tgz", + "integrity": "sha512-ud0vtsNRF+joUCyvNMyo0j5DKX2Lh/im+xVgRzBEsfHhQYZ+i4fKTveova9XxLzt6Jl6G0e/0mM4aC0gqZYSnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -2525,6 +3378,18 @@ "dev": true, "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -2561,6 +3426,15 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2598,7 +3472,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2624,11 +3497,110 @@ "dev": true, "license": "ISC", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/archiver/node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver/node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver/node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 8" + "node": ">= 14" } }, "node_modules/arg": { @@ -2779,7 +3751,6 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, "license": "MIT" }, "node_modules/available-typed-arrays": { @@ -2798,6 +3769,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==", + "license": "Apache-2.0" + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -2961,7 +3938,33 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.2.tgz", + "integrity": "sha512-KSdMqLj1ZERZMP1PTmnLK7SqJu9z9/SbwUUPZly2puMtfVcytC+jl6mb/9XYiqq0PXcx1rNDS+Qvl1g54Lho6A==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, "node_modules/before-after-hook": { @@ -2970,11 +3973,29 @@ "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", "license": "Apache-2.0" }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "license": "MIT", + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", + "license": "MIT" + }, "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, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3049,6 +4070,39 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3056,6 +4110,14 @@ "dev": true, "license": "MIT" }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -3086,6 +4148,16 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -3117,6 +4189,18 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "license": "MIT/X11", + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/chalk": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", @@ -3199,7 +4283,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3212,7 +4295,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/commander": { @@ -3238,7 +4320,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { @@ -3248,6 +4329,24 @@ "dev": true, "license": "MIT" }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -3314,7 +4413,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3383,7 +4481,6 @@ "version": "4.3.7", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3465,6 +4562,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "license": "ISC" + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -3528,6 +4631,28 @@ "node": ">=6.0.0" } }, + "node_modules/dot-object": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/dot-object/-/dot-object-2.1.5.tgz", + "integrity": "sha512-xHF8EP4XH/Ba9fvAF2LDd5O3IITVolerVV6xvkxoM8zlGEiCUrggpAnHyOoKJKCrhvPcGATFAUwIujj7bRG5UA==", + "license": "MIT", + "dependencies": { + "commander": "^6.1.0", + "glob": "^7.1.6" + }, + "bin": { + "dot-object": "bin/dot-object" + } + }, + "node_modules/dot-object/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -3540,6 +4665,12 @@ "url": "https://dotenvx.com" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -3580,7 +4711,6 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/enhanced-resolve": { @@ -4343,6 +5473,24 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -4393,6 +5541,22 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4407,6 +5571,12 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -4451,6 +5621,28 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-xml-parser": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.1.tgz", + "integrity": "sha512-y655CeyUQ+jj7KBbYMc4FG01V8ZQqjN+gDYGJ50RtfsUB8iG9AmwmwoAgeKLJdmueKKMrH1RJ7yXHTSoczdv5w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -4580,11 +5772,38 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -4737,7 +5956,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -4771,7 +5989,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -4782,7 +5999,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -4859,7 +6075,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -4986,6 +6201,32 @@ "dev": true, "license": "MIT" }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -4996,6 +6237,26 @@ "node": ">=10.17.0" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5068,7 +6329,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -5079,7 +6339,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/internal-slot": { @@ -5235,7 +6494,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5313,6 +6571,15 @@ "node": ">=8" } }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -5350,7 +6617,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5431,7 +6697,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/istanbul-lib-coverage": { @@ -5505,6 +6770,21 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", @@ -6715,6 +7995,12 @@ "node": ">=6" } }, + "node_modules/jwt-decode": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==", + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6731,8 +8017,56 @@ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "engines": { + "node": ">=6" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" } }, "node_modules/leven": { @@ -6897,6 +8231,15 @@ "node": ">=0.8.0" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7002,7 +8345,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -7023,6 +8365,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -7050,7 +8401,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/natural-compare": { @@ -7060,6 +8410,36 @@ "dev": true, "license": "MIT" }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -7078,7 +8458,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7195,7 +8574,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -7277,6 +8655,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7309,6 +8693,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7323,7 +8717,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -7333,7 +8726,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7345,6 +8737,34 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -7942,6 +9362,21 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -8004,6 +9439,12 @@ ], "license": "MIT" }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "license": "MIT" + }, "node_modules/quibble": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/quibble/-/quibble-0.9.2.tgz", @@ -8024,6 +9465,43 @@ "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.6.0.tgz", + "integrity": "sha512-cbAdYt0VcnpN2Bekq7PU+k363ZRsPwJoEEJOEtSJQlJXzwaxt3FIo/uL+KeDSGIjJqtkwyge4KQgD2S2kd+CQw==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", @@ -8200,6 +9678,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-regex-test": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", @@ -8269,7 +9767,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -8282,7 +9779,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8389,6 +9885,29 @@ "node": ">=8" } }, + "node_modules/streamx": { + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", + "license": "MIT", + "dependencies": { + "fast-fifo": "^1.3.2", + "queue-tick": "^1.0.1", + "text-decoder": "^1.1.0" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -8407,7 +9926,21 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -8474,7 +10007,19 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -8516,6 +10061,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8568,6 +10119,17 @@ "node": ">=6" } }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -8607,6 +10169,15 @@ "node": "*" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -8634,6 +10205,21 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "license": "MIT/X11", + "engines": { + "node": "*" + } + }, "node_modules/ts-api-utils": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.0.tgz", @@ -8750,6 +10336,31 @@ } } }, + "node_modules/ts-poet": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/ts-poet/-/ts-poet-4.15.0.tgz", + "integrity": "sha512-sLLR8yQBvHzi9d4R1F4pd+AzQxBfzOSSjfxiJxQhkUoH5bL7RsAC6wgvtVUQdGqiCsyS9rT6/8X2FI7ipdir5g==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.15", + "prettier": "^2.5.1" + } + }, + "node_modules/ts-poet/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -8777,7 +10388,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD" }, "node_modules/tsx": { @@ -8808,6 +10418,44 @@ "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } }, + "node_modules/twirp-ts": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/twirp-ts/-/twirp-ts-2.5.0.tgz", + "integrity": "sha512-JTKIK5Pf/+3qCrmYDFlqcPPUx+ohEWKBaZy8GL8TmvV2VvC0SXVyNYILO39+GCRbqnuP6hBIF+BVr8ZxRz+6fw==", + "license": "MIT", + "dependencies": { + "@protobuf-ts/plugin-framework": "^2.0.7", + "camel-case": "^4.1.2", + "dot-object": "^2.1.4", + "path-to-regexp": "^6.2.0", + "ts-poet": "^4.5.0", + "yaml": "^1.10.2" + }, + "bin": { + "protoc-gen-twirp_ts": "protoc-gen-twirp_ts" + }, + "peerDependencies": { + "@protobuf-ts/plugin": "^2.5.0", + "ts-proto": "^1.81.3" + }, + "peerDependenciesMeta": { + "@protobuf-ts/plugin": { + "optional": true + }, + "ts-proto": { + "optional": true + } + } + }, + "node_modules/twirp-ts/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -8975,6 +10623,28 @@ "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", "license": "ISC" }, + "node_modules/unzip-stream": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/unzip-stream/-/unzip-stream-0.3.4.tgz", + "integrity": "sha512-PyofABPVv+d7fL7GOpusx7eRT9YETY2X04PhwbSipdj6bMxVCFJrr+nm0Mxqbf9hUiTin/UsnuFWBXlDZFy0Cw==", + "license": "MIT", + "dependencies": { + "binary": "^0.3.0", + "mkdirp": "^0.5.1" + } + }, + "node_modules/unzip-stream/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -9016,6 +10686,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -9118,11 +10794,26 @@ "makeerror": "1.0.12" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -9199,6 +10890,39 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -9219,7 +10943,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { diff --git a/package.json b/package.json index f26a357..57ab1b8 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,13 @@ "outputName": "jest-junit.xml" }, "dependencies": { + "@actions/artifact": "^2.2.0", "@actions/core": "^1.11.1", - "@octokit/core": "^6.1.2", + "@actions/github": "^6.0.0", + "@actions/http-client": "^2.2.3", + "@octokit/core": "^6.1.3", + "@octokit/plugin-retry": "^7.1.2", + "archiver": "^7.0.1", "chalk": "^5.3.0", "commander": "^13.0.0", "dotenv": "^16.4.5", @@ -52,14 +57,17 @@ "tsconfig-paths": "^4.2.0", "tsx": "^4.19.2", "typescript": "^5.6.3", + "unzip-stream": "^0.3.4", "yaml": "^2.6.1" }, "devDependencies": { "@eslint/compat": "^1.2.3", "@jest/globals": "^29.7.0", + "@types/archiver": "^6.0.3", "@types/figlet": "^1.7.0", "@types/jest": "^29.5.14", "@types/node": "^22.9.1", + "@types/unzip-stream": "^0.3.4", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "eslint": "^9.15.0", diff --git a/src/commands/run.ts b/src/commands/run.ts index 33f34d3..8ce65d8 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -1,6 +1,7 @@ import { config } from 'dotenv' import { createRequire } from 'module' import quibble from 'quibble' +import { ARTIFACT_STUBS } from '../stubs/artifact/artifact.js' import { CORE_STUBS, CoreMeta } from '../stubs/core.js' import { EnvMeta } from '../stubs/env.js' import type { Action } from '../types.js' @@ -134,6 +135,17 @@ export async function action(): Promise { ), CORE_STUBS ) + await quibble.esm( + path.resolve( + dirs.join(path.sep), + 'node_modules', + '@actions', + 'artifact', + 'lib', + 'artifact.js' + ), + ARTIFACT_STUBS + ) // ESM actions need to be imported, not required. const { run } = await import(path.resolve(EnvMeta.entrypoint)) @@ -157,6 +169,17 @@ export async function action(): Promise { ), CORE_STUBS ) + quibble( + path.resolve( + dirs.join(path.sep), + 'node_modules', + '@actions', + 'artifact', + 'lib', + 'artifact.js' + ), + ARTIFACT_STUBS + ) // CJS actions need to be required, not imported. const { run } = require(path.resolve(EnvMeta.entrypoint)) diff --git a/src/stubs/artifact/artifact.ts b/src/stubs/artifact/artifact.ts new file mode 100644 index 0000000..c160840 --- /dev/null +++ b/src/stubs/artifact/artifact.ts @@ -0,0 +1,16 @@ +/** + * @github/local-action Modified + */ + +import { + type ArtifactClient, + DefaultArtifactClient +} from './internal/client.js' + +const client: ArtifactClient = new DefaultArtifactClient() +export default client + +export const ARTIFACT_STUBS = { + DefaultArtifactClient, + client +} diff --git a/src/stubs/artifact/internal/client.ts b/src/stubs/artifact/internal/client.ts new file mode 100644 index 0000000..c6623b2 --- /dev/null +++ b/src/stubs/artifact/internal/client.ts @@ -0,0 +1,312 @@ +import { warning } from '../../core.js' +import { + deleteArtifactInternal, + deleteArtifactPublic +} from './delete/delete-artifact.js' +import { + downloadArtifactInternal, + downloadArtifactPublic +} from './download/download-artifact.js' +import { getArtifactInternal, getArtifactPublic } from './find/get-artifact.js' +import { + listArtifactsInternal, + listArtifactsPublic +} from './find/list-artifacts.js' +import { isGhes } from './shared/config.js' +import { GHESNotSupportedError } from './shared/errors.js' +import { + DeleteArtifactResponse, + DownloadArtifactOptions, + DownloadArtifactResponse, + FindOptions, + GetArtifactResponse, + ListArtifactsOptions, + ListArtifactsResponse, + UploadArtifactOptions, + UploadArtifactResponse +} from './shared/interfaces.js' +import { uploadArtifact } from './upload/upload-artifact.js' + +/** + * @github/local-action Unmodified + */ +export interface ArtifactClient { + /** + * Uploads an artifact. + * + * @param name The name of the artifact, required + * @param files A list of absolute or relative paths that denote what files should be uploaded + * @param rootDirectory An absolute or relative file path that denotes the root parent directory of the files being uploaded + * @param options Extra options for customizing the upload behavior + * @returns single UploadArtifactResponse object + */ + uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadArtifactOptions + ): Promise + + /** + * Lists all artifacts that are part of the current workflow run. + * This function will return at most 1000 artifacts per workflow run. + * + * If `options.findBy` is specified, this will call the public List-Artifacts API which can list from other runs. + * https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts + * + * @param options Extra options that allow for the customization of the list behavior + * @returns ListArtifactResponse object + */ + listArtifacts( + options?: ListArtifactsOptions & FindOptions + ): Promise + + /** + * Finds an artifact by name. + * If there are multiple artifacts with the same name in the same workflow run, this will return the latest. + * If the artifact is not found, it will throw. + * + * If `options.findBy` is specified, this will use the public List Artifacts API with a name filter which can get artifacts from other runs. + * https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#list-workflow-run-artifacts + * `@actions/artifact` v2+ does not allow for creating multiple artifacts with the same name in the same workflow run. + * It is possible to have multiple artifacts with the same name in the same workflow run by using old versions of upload-artifact (v1,v2 and v3), @actions/artifact < v2 or it is a rerun. + * If there are multiple artifacts with the same name in the same workflow run this function will return the first artifact that matches the name. + * + * @param artifactName The name of the artifact to find + * @param options Extra options that allow for the customization of the get behavior + */ + getArtifact( + artifactName: string, + options?: FindOptions + ): Promise + + /** + * Downloads an artifact and unzips the content. + * + * If `options.findBy` is specified, this will use the public Download Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#download-an-artifact + * + * @param artifactId The id of the artifact to download + * @param options Extra options that allow for the customization of the download behavior + * @returns single DownloadArtifactResponse object + */ + downloadArtifact( + artifactId: number, + options?: DownloadArtifactOptions & FindOptions + ): Promise + + /** + * Delete an Artifact + * + * If `options.findBy` is specified, this will use the public Delete Artifact API https://docs.github.com/en/rest/actions/artifacts?apiVersion=2022-11-28#delete-an-artifact + * + * @param artifactName The name of the artifact to delete + * @param options Extra options that allow for the customization of the delete behavior + * @returns single DeleteArtifactResponse object + */ + deleteArtifact( + artifactName: string, + options?: FindOptions + ): Promise +} + +/** + * @github/local-action Modified + */ +export class DefaultArtifactClient implements ArtifactClient { + async uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadArtifactOptions + ): Promise { + if (!process.env.LOCAL_ACTION_ARTIFACT_PATH) + throw new Error( + 'LOCAL_ACTION_ARTIFACT_PATH must be set when interacting with @actions/artifact!' + ) + + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + return uploadArtifact(name, files, rootDirectory, options) + } catch (error) { + warning( + `Artifact upload failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions is operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + + throw error + } + } + + async downloadArtifact( + artifactId: number, + options?: DownloadArtifactOptions & FindOptions + ): Promise { + if (!process.env.LOCAL_ACTION_ARTIFACT_PATH) + throw new Error( + 'LOCAL_ACTION_ARTIFACT_PATH must be set when interacting with @actions/artifact!' + ) + + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + if (options?.findBy) { + const { + findBy: { repositoryOwner, repositoryName, token }, + ...downloadOptions + } = options + + return downloadArtifactPublic( + artifactId, + repositoryOwner, + repositoryName, + token, + downloadOptions + ) + } + + return downloadArtifactInternal(artifactId, options) + } catch (error) { + warning( + `Download Artifact failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + + throw error + } + } + + async listArtifacts( + options?: ListArtifactsOptions & FindOptions + ): Promise { + if (!process.env.LOCAL_ACTION_ARTIFACT_PATH) + throw new Error( + 'LOCAL_ACTION_ARTIFACT_PATH must be set when interacting with @actions/artifact!' + ) + + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + if (options?.findBy) { + const { + findBy: { workflowRunId, repositoryOwner, repositoryName, token } + } = options + + return listArtifactsPublic( + workflowRunId, + repositoryOwner, + repositoryName, + token, + options?.latest + ) + } + + return listArtifactsInternal(options?.latest) + } catch (error: unknown) { + warning( + `Listing Artifacts failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + + throw error + } + } + + async getArtifact( + artifactName: string, + options?: FindOptions + ): Promise { + if (!process.env.LOCAL_ACTION_ARTIFACT_PATH) + throw new Error( + 'LOCAL_ACTION_ARTIFACT_PATH must be set when interacting with @actions/artifact!' + ) + + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + if (options?.findBy) { + const { + findBy: { workflowRunId, repositoryOwner, repositoryName, token } + } = options + + return getArtifactPublic( + artifactName, + workflowRunId, + repositoryOwner, + repositoryName, + token + ) + } + + return getArtifactInternal(artifactName) + } catch (error: unknown) { + warning( + `Get Artifact failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + throw error + } + } + + async deleteArtifact( + artifactName: string, + options?: FindOptions + ): Promise { + if (!process.env.LOCAL_ACTION_ARTIFACT_PATH) + throw new Error( + 'LOCAL_ACTION_ARTIFACT_PATH must be set when interacting with @actions/artifact!' + ) + + try { + if (isGhes()) { + throw new GHESNotSupportedError() + } + + if (options?.findBy) { + const { + findBy: { repositoryOwner, repositoryName, workflowRunId, token } + } = options + + return deleteArtifactPublic( + artifactName, + workflowRunId, + repositoryOwner, + repositoryName, + token + ) + } + + return deleteArtifactInternal(artifactName) + } catch (error) { + warning( + `Delete Artifact failed with error: ${error}. + +Errors can be temporary, so please try again and optionally run the action with debug mode enabled for more information. + +If the error persists, please check whether Actions and API requests are operating normally at [https://githubstatus.com](https://www.githubstatus.com).` + ) + + throw error + } + } +} diff --git a/src/stubs/artifact/internal/delete/delete-artifact.ts b/src/stubs/artifact/internal/delete/delete-artifact.ts new file mode 100644 index 0000000..6d0e4ad --- /dev/null +++ b/src/stubs/artifact/internal/delete/delete-artifact.ts @@ -0,0 +1,108 @@ +import { getOctokit } from '@actions/github' +import { defaults as defaultGitHubOptions } from '@actions/github/lib/utils.js' +import type { OctokitOptions } from '@octokit/core' +import { requestLog } from '@octokit/plugin-request-log' +import { retry } from '@octokit/plugin-retry' +import fs from 'fs' +import path from 'path' +import { EnvMeta } from '../../../../stubs/env.js' +import * as core from '../../../core.js' +import { getArtifactPublic } from '../find/get-artifact.js' +import { getRetryOptions } from '../find/retry-options.js' +import { + ArtifactNotFoundError, + InvalidResponseError +} from '../shared/errors.js' +import type { Artifact, DeleteArtifactResponse } from '../shared/interfaces.js' +import { getUserAgentString } from '../shared/user-agent.js' + +/** + * @github/local-action Unmodified + */ +export async function deleteArtifactPublic( + artifactName: string, + workflowRunId: number, + repositoryOwner: string, + repositoryName: string, + token: string +): Promise { + const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions) + + const opts: OctokitOptions = { + log: undefined, + userAgent: getUserAgentString(), + previews: undefined, + retry: retryOpts, + request: requestOpts + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const github = getOctokit(token, opts, retry as any, requestLog as any) + const getArtifactResp = await getArtifactPublic( + artifactName, + workflowRunId, + repositoryOwner, + repositoryName, + token + ) + + const deleteArtifactResp = await github.rest.actions.deleteArtifact({ + owner: repositoryOwner, + repo: repositoryName, + artifact_id: getArtifactResp.artifact.id + }) + + if (deleteArtifactResp.status !== 204) { + throw new InvalidResponseError( + `Invalid response from GitHub API: ${deleteArtifactResp.status} (${deleteArtifactResp?.headers?.['x-github-request-id']})` + ) + } + + return { + id: getArtifactResp.artifact.id + } +} + +/** + * @github/local-action Modified + */ +export async function deleteArtifactInternal( + artifactName: string +): Promise { + // Check the current list of artifacts for one with a matching name, sorted by + // most recent (highest ID). + const artifacts: Artifact[] = EnvMeta.artifacts + .filter((artifact: Artifact) => artifact.name === artifactName) + .sort((a, b) => Number(b.id) - Number(a.id)) + + if (artifacts.length === 0) + throw new ArtifactNotFoundError( + `Artifact not found for name: ${artifactName}` + ) + + if (artifacts.length > 1) + core.debug( + `More than one artifact found for a single name, returning newest (id: ${artifacts[0].id})` + ) + + const id = artifacts[0].id + + // Delete the artifact from the filesystem. + fs.rmSync( + path.join( + process.env.LOCAL_ACTION_ARTIFACT_PATH!, + `${artifacts[0].name}.zip` + ) + ) + + // Remove the artifact from the list of artifacts. + EnvMeta.artifacts = EnvMeta.artifacts.filter( + (artifact: Artifact) => artifact.id !== id + ) + + core.info(`Artifact '${artifactName}' (ID: ${id}) deleted`) + + return { + id + } +} diff --git a/src/stubs/artifact/internal/download/download-artifact.ts b/src/stubs/artifact/internal/download/download-artifact.ts new file mode 100644 index 0000000..fb3f3a4 --- /dev/null +++ b/src/stubs/artifact/internal/download/download-artifact.ts @@ -0,0 +1,223 @@ +import { getOctokit } from '@actions/github' +import * as httpClient from '@actions/http-client' +import fs from 'fs' +import path from 'path' +import { finished } from 'stream/promises' +import unzip from 'unzip-stream' +import { EnvMeta } from '../../../../stubs/env.js' +import * as core from '../../../core.js' +import { getGitHubWorkspaceDir } from '../shared/config.js' +import { ArtifactNotFoundError } from '../shared/errors.js' +import type { + Artifact, + DownloadArtifactOptions, + DownloadArtifactResponse +} from '../shared/interfaces.js' +import { getUserAgentString } from '../shared/user-agent.js' + +/** + * @github/local-action Unmodified + */ +const scrubQueryParameters = (url: string): string => { + const parsed = new URL(url) + parsed.search = '' + return parsed.toString() +} + +/** + * @github/local-action Unmodified + */ +async function exists(path: string): Promise { + try { + fs.accessSync(path) + return true + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + if (error.code === 'ENOENT') return false + else throw error + } +} + +/** + * @github/local-action Unmodified + */ +async function streamExtract(url: string, directory: string): Promise { + let retryCount = 0 + while (retryCount < 5) { + try { + await streamExtractExternal(url, directory) + return + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + retryCount++ + core.debug( + `Failed to download artifact after ${retryCount} retries due to ${error.message}. Retrying in 5 seconds...` + ) + // wait 5 seconds before retrying + await new Promise(resolve => setTimeout(resolve, 5000)) + } + } + + throw new Error(`Artifact download failed after ${retryCount} retries.`) +} + +/** + * @github/local-action Unmodified + */ +export async function streamExtractExternal( + url: string, + directory: string +): Promise { + const client = new httpClient.HttpClient(getUserAgentString()) + const response = await client.get(url) + if (response.message.statusCode !== 200) { + throw new Error( + `Unexpected HTTP response from blob storage: ${response.message.statusCode} ${response.message.statusMessage}` + ) + } + + const timeout = 30 * 1000 // 30 seconds + + return new Promise((resolve, reject) => { + const timerFn = (): void => { + response.message.destroy( + new Error(`Blob storage chunk did not respond in ${timeout}ms`) + ) + } + const timer = setTimeout(timerFn, timeout) + + response.message + .on('data', () => { + timer.refresh() + }) + .on('error', (error: Error) => { + core.debug( + `response.message: Artifact download failed: ${error.message}` + ) + clearTimeout(timer) + reject(error) + }) + .pipe(unzip.Extract({ path: directory })) + .on('close', () => { + clearTimeout(timer) + resolve() + }) + .on('error', (error: Error) => { + reject(error) + }) + }) +} + +/** + * @github/local-action Unmodified + */ +export async function downloadArtifactPublic( + artifactId: number, + repositoryOwner: string, + repositoryName: string, + token: string, + options?: DownloadArtifactOptions +): Promise { + const downloadPath = await resolveOrCreateDirectory(options?.path) + + const api = getOctokit(token) + + core.info( + `Downloading artifact '${artifactId}' from '${repositoryOwner}/${repositoryName}'` + ) + + const { headers, status } = await api.rest.actions.downloadArtifact({ + owner: repositoryOwner, + repo: repositoryName, + artifact_id: artifactId, + archive_format: 'zip', + request: { + redirect: 'manual' + } + }) + + if (status !== 302) + throw new Error(`Unable to download artifact. Unexpected status: ${status}`) + + const { location } = headers + if (!location) throw new Error(`Unable to redirect to artifact download url`) + + core.info( + `Redirecting to blob download url: ${scrubQueryParameters(location)}` + ) + + try { + core.info(`Starting download of artifact to: ${downloadPath}`) + await streamExtract(location, downloadPath) + core.info(`Artifact download completed successfully.`) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + throw new Error(`Unable to download and extract artifact: ${error.message}`) + } + + return { downloadPath } +} + +/** + * @github/local-action Modified + */ +export async function downloadArtifactInternal( + artifactId: number, + options?: DownloadArtifactOptions +): Promise { + const downloadPath = await resolveOrCreateDirectory(options?.path) + + // Check the current list of artifacts for one with a matching ID. + const artifacts: Artifact[] = EnvMeta.artifacts.filter( + (artifact: Artifact) => artifact.id === artifactId + ) + + if (artifacts.length === 0) + throw new ArtifactNotFoundError( + `No artifacts found for ID: ${artifactId}\nAre you trying to download from a different run? Try specifying a github-token with \`actions:read\` scope.` + ) + + if (artifacts.length > 1) + core.warning('Multiple artifacts found, defaulting to first.') + + try { + core.info(`Starting download of artifact to: ${downloadPath}`) + + // Extract the file to the specified directory. The archive will be + // available at `${LOCAL_ACTION_ARTIFACT_PATH}/${artifact.id}`. + const readStream = fs.createReadStream( + path.join( + process.env.LOCAL_ACTION_ARTIFACT_PATH!, + `${artifacts[0].name}.zip` + ) + ) + + readStream.pipe(unzip.Extract({ path: downloadPath })) + + await finished(readStream) + + core.info(`Artifact download completed successfully.`) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + throw new Error(`Unable to download and extract artifact: ${error.message}`) + } + + return { downloadPath } +} + +/** + * @github/local-action Unmodified + */ +async function resolveOrCreateDirectory( + downloadPath = getGitHubWorkspaceDir() +): Promise { + if (!(await exists(downloadPath))) { + core.debug( + `Artifact destination folder does not exist, creating: ${downloadPath}` + ) + fs.mkdirSync(downloadPath, { recursive: true }) + } else + core.debug(`Artifact destination folder already exists: ${downloadPath}`) + + return downloadPath +} diff --git a/src/stubs/artifact/internal/find/get-artifact.ts b/src/stubs/artifact/internal/find/get-artifact.ts new file mode 100644 index 0000000..cf73c3f --- /dev/null +++ b/src/stubs/artifact/internal/find/get-artifact.ts @@ -0,0 +1,115 @@ +import { getOctokit } from '@actions/github' +import { defaults as defaultGitHubOptions } from '@actions/github/lib/utils.js' +import type { OctokitOptions } from '@octokit/core' +import { requestLog } from '@octokit/plugin-request-log' +import { retry } from '@octokit/plugin-retry' +import { EnvMeta } from '../../../../stubs/env.js' +import * as core from '../../../core.js' +import { + ArtifactNotFoundError, + InvalidResponseError +} from '../shared/errors.js' +import type { Artifact, GetArtifactResponse } from '../shared/interfaces.js' +import { getUserAgentString } from '../shared/user-agent.js' +import { getRetryOptions } from './retry-options.js' + +/** + * @github/local-action Unmodified + */ +export async function getArtifactPublic( + artifactName: string, + workflowRunId: number, + repositoryOwner: string, + repositoryName: string, + token: string +): Promise { + const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions) + + const opts: OctokitOptions = { + log: undefined, + userAgent: getUserAgentString(), + previews: undefined, + retry: retryOpts, + request: requestOpts + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const github = getOctokit(token, opts, retry as any, requestLog as any) + + const getArtifactResp = await github.request( + 'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts{?name}', + { + owner: repositoryOwner, + repo: repositoryName, + run_id: workflowRunId, + name: artifactName + } + ) + + if (getArtifactResp.status !== 200) { + throw new InvalidResponseError( + `Invalid response from GitHub API: ${getArtifactResp.status} (${getArtifactResp?.headers?.['x-github-request-id']})` + ) + } + + if (getArtifactResp.data.artifacts.length === 0) { + throw new ArtifactNotFoundError( + `Artifact not found for name: ${artifactName} + Please ensure that your artifact is not expired and the artifact was uploaded using a compatible version of toolkit/upload-artifact. + For more information, visit the GitHub Artifacts FAQ: https://github.com/actions/toolkit/blob/main/packages/artifact/docs/faq.md` + ) + } + + let artifact = getArtifactResp.data.artifacts[0] + if (getArtifactResp.data.artifacts.length > 1) { + artifact = getArtifactResp.data.artifacts.sort( + (a: Artifact, b: Artifact) => b.id - a.id + )[0] + core.debug( + `More than one artifact found for a single name, returning newest (id: ${artifact.id})` + ) + } + + return { + artifact: { + name: artifact.name, + id: artifact.id, + size: artifact.size_in_bytes, + createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined + } + } +} + +/** + * @github/local-action Modified + */ +export async function getArtifactInternal( + artifactName: string +): Promise { + // Get all artifacts with a matching name, sorted by latest (highest ID). + const artifacts = EnvMeta.artifacts + .filter(artifact => artifact.name === artifactName) + .sort((a, b) => Number(b.id) - Number(a.id)) + + if (artifacts.length === 0) { + throw new ArtifactNotFoundError( + `Artifact not found for name: ${artifactName} + Please ensure that your artifact is not expired and the artifact was uploaded using a compatible version of toolkit/upload-artifact. + For more information, visit the GitHub Artifacts FAQ: https://github.com/actions/toolkit/blob/main/packages/artifact/docs/faq.md` + ) + } + + if (artifacts.length > 1) + core.debug( + `More than one artifact found for a single name, returning newest (id: ${artifacts[0].id})` + ) + + return { + artifact: { + name: artifacts[0].name, + id: Number(artifacts[0].id), + size: Number(artifacts[0].size), + createdAt: artifacts[0].createdAt + } + } +} diff --git a/src/stubs/artifact/internal/find/list-artifacts.ts b/src/stubs/artifact/internal/find/list-artifacts.ts new file mode 100644 index 0000000..47773d3 --- /dev/null +++ b/src/stubs/artifact/internal/find/list-artifacts.ts @@ -0,0 +1,146 @@ +import { getOctokit } from '@actions/github' +import { defaults as defaultGitHubOptions } from '@actions/github/lib/utils.js' +import type { OctokitOptions } from '@octokit/core' +import { requestLog } from '@octokit/plugin-request-log' +import { retry } from '@octokit/plugin-retry' +import { EnvMeta } from '../../../../stubs/env.js' +import * as core from '../../../core.js' +import { Artifact, ListArtifactsResponse } from '../shared/interfaces.js' +import { getUserAgentString } from '../shared/user-agent.js' +import { getRetryOptions } from './retry-options.js' + +// Limiting to 1000 for perf reasons +const maximumArtifactCount = 1000 +const paginationCount = 100 +const maxNumberOfPages = maximumArtifactCount / paginationCount + +/** + * @github/local-action Unmodified + */ +export async function listArtifactsPublic( + workflowRunId: number, + repositoryOwner: string, + repositoryName: string, + token: string, + latest = false +): Promise { + core.info( + `Fetching artifact list for workflow run ${workflowRunId} in repository ${repositoryOwner}/${repositoryName}` + ) + + let artifacts: Artifact[] = [] + const [retryOpts, requestOpts] = getRetryOptions(defaultGitHubOptions) + + const opts: OctokitOptions = { + log: undefined, + userAgent: getUserAgentString(), + previews: undefined, + retry: retryOpts, + request: requestOpts + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const github = getOctokit(token, opts, retry as any, requestLog as any) + + let currentPageNumber = 1 + const { data: listArtifactResponse } = + await github.rest.actions.listWorkflowRunArtifacts({ + owner: repositoryOwner, + repo: repositoryName, + run_id: workflowRunId, + per_page: paginationCount, + page: currentPageNumber + }) + + let numberOfPages = Math.ceil( + listArtifactResponse.total_count / paginationCount + ) + const totalArtifactCount = listArtifactResponse.total_count + if (totalArtifactCount > maximumArtifactCount) { + core.warning( + `Workflow run ${workflowRunId} has more than 1000 artifacts. Results will be incomplete as only the first ${maximumArtifactCount} artifacts will be returned` + ) + numberOfPages = maxNumberOfPages + } + + // Iterate over the first page + for (const artifact of listArtifactResponse.artifacts) { + artifacts.push({ + name: artifact.name, + id: artifact.id, + size: artifact.size_in_bytes, + createdAt: artifact.created_at ? new Date(artifact.created_at) : undefined + }) + } + + // Iterate over any remaining pages + for ( + currentPageNumber; + currentPageNumber < numberOfPages; + currentPageNumber++ + ) { + currentPageNumber++ + core.debug(`Fetching page ${currentPageNumber} of artifact list`) + + const { data: listArtifactResponse } = + await github.rest.actions.listWorkflowRunArtifacts({ + owner: repositoryOwner, + repo: repositoryName, + run_id: workflowRunId, + per_page: paginationCount, + page: currentPageNumber + }) + + for (const artifact of listArtifactResponse.artifacts) { + artifacts.push({ + name: artifact.name, + id: artifact.id, + size: artifact.size_in_bytes, + createdAt: artifact.created_at + ? new Date(artifact.created_at) + : undefined + }) + } + } + + if (latest) { + artifacts = filterLatest(artifacts) + } + + core.info(`Found ${artifacts.length} artifact(s)`) + + return { + artifacts + } +} + +/** + * @github/local-action Modified + */ +export async function listArtifactsInternal( + latest = false +): Promise { + const artifacts = latest ? filterLatest(EnvMeta.artifacts) : EnvMeta.artifacts + + core.info(`Found ${artifacts.length} artifact(s)`) + + return { + artifacts + } +} + +/** + * @github/local-action Unmodified + */ +function filterLatest(artifacts: Artifact[]): Artifact[] { + artifacts.sort((a, b) => b.id - a.id) + const latestArtifacts: Artifact[] = [] + const seenArtifactNames = new Set() + for (const artifact of artifacts) { + if (!seenArtifactNames.has(artifact.name)) { + latestArtifacts.push(artifact) + seenArtifactNames.add(artifact.name) + } + } + return latestArtifacts +} diff --git a/src/stubs/artifact/internal/find/retry-options.ts b/src/stubs/artifact/internal/find/retry-options.ts new file mode 100644 index 0000000..7d7e9de --- /dev/null +++ b/src/stubs/artifact/internal/find/retry-options.ts @@ -0,0 +1,48 @@ +/** + * @github/local-action Unmodified + */ + +import type { OctokitOptions } from '@octokit/core' +import type { RequestRequestOptions } from '@octokit/types' +import * as core from '../../../core.js' + +export type RetryOptions = { + doNotRetry?: number[] + enabled?: boolean +} + +// Defaults for fetching artifacts +const defaultMaxRetryNumber = 5 +const defaultExemptStatusCodes = [400, 401, 403, 404, 422] // https://github.com/octokit/plugin-retry.js/blob/9a2443746c350b3beedec35cf26e197ea318a261/src/index.ts#L14 + +export function getRetryOptions( + defaultOptions: OctokitOptions, + retries: number = defaultMaxRetryNumber, + exemptStatusCodes: number[] = defaultExemptStatusCodes +): [RetryOptions, RequestRequestOptions | undefined] { + if (retries <= 0) return [{ enabled: false }, defaultOptions.request] + + const retryOptions: RetryOptions = { + enabled: true + } + + if (exemptStatusCodes.length > 0) retryOptions.doNotRetry = exemptStatusCodes + + // The GitHub type has some defaults for `options.request` + // see: https://github.com/actions/toolkit/blob/4fbc5c941a57249b19562015edbd72add14be93d/packages/github/src/utils.ts#L15 + // We pass these in here so they are not overridden. + const requestOptions: RequestRequestOptions = { + ...defaultOptions.request, + retries + } + + core.debug( + `GitHub client configured with: (retries: ${ + requestOptions.retries + }, retry-exempt-status-code: ${ + retryOptions.doNotRetry ?? 'octokit default: [400, 401, 403, 404, 422]' + })` + ) + + return [retryOptions, requestOptions] +} diff --git a/src/stubs/artifact/internal/shared/config.ts b/src/stubs/artifact/internal/shared/config.ts new file mode 100644 index 0000000..5d351a2 --- /dev/null +++ b/src/stubs/artifact/internal/shared/config.ts @@ -0,0 +1,34 @@ +/** + * @github/local-action Unmodified + */ +export function getUploadChunkSize(): number { + return 8 * 1024 * 1024 // 8 MB Chunks +} + +/** + * @github/local-action Unmodified + */ +export function isGhes(): boolean { + const ghUrl = new URL( + process.env['GITHUB_SERVER_URL'] || 'https://github.com' + ) + + const hostname = ghUrl.hostname.trimEnd().toUpperCase() + const isGitHubHost = hostname === 'GITHUB.COM' + const isGheHost = hostname.endsWith('.GHE.COM') + const isLocalHost = hostname.endsWith('.LOCALHOST') + + return !isGitHubHost && !isGheHost && !isLocalHost +} + +/** + * @github/local-action Modified + */ +export function getGitHubWorkspaceDir(): string { + // Default to current working directory + const ghWorkspaceDir = process.env['GITHUB_WORKSPACE'] || process.cwd() + if (!ghWorkspaceDir) { + throw new Error('Unable to get the GITHUB_WORKSPACE env variable') + } + return ghWorkspaceDir +} diff --git a/src/stubs/artifact/internal/shared/errors.ts b/src/stubs/artifact/internal/shared/errors.ts new file mode 100644 index 0000000..5edd1ea --- /dev/null +++ b/src/stubs/artifact/internal/shared/errors.ts @@ -0,0 +1,49 @@ +/** + * @github/local-action Unmodified + */ +export class FilesNotFoundError extends Error { + files: string[] + + constructor(files: string[] = []) { + let message = 'No files were found to upload' + if (files.length > 0) { + message += `: ${files.join(', ')}` + } + + super(message) + this.files = files + this.name = 'FilesNotFoundError' + } +} + +/** + * @github/local-action Unmodified + */ +export class InvalidResponseError extends Error { + constructor(message: string) { + super(message) + this.name = 'InvalidResponseError' + } +} + +/** + * @github/local-action Unmodified + */ +export class ArtifactNotFoundError extends Error { + constructor(message = 'Artifact not found') { + super(message) + this.name = 'ArtifactNotFoundError' + } +} + +/** + * @github/local-action Unmodified + */ +export class GHESNotSupportedError extends Error { + constructor( + message = '@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES.' + ) { + super(message) + this.name = 'GHESNotSupportedError' + } +} diff --git a/src/stubs/artifact/internal/shared/interfaces.ts b/src/stubs/artifact/internal/shared/interfaces.ts new file mode 100644 index 0000000..5b60711 --- /dev/null +++ b/src/stubs/artifact/internal/shared/interfaces.ts @@ -0,0 +1,94 @@ +/** + * @github/local-action Unmodified + */ +export interface DownloadArtifactOptions { + path?: string +} + +/** + * @github/local-action Unmodified + */ +export interface DownloadArtifactResponse { + downloadPath?: string +} + +/** + * @github/local-action Unmodified + */ +export interface UploadArtifactResponse { + size?: number + id?: number + digest?: string +} + +/** + * @github/local-action Unmodified + */ +export interface UploadArtifactOptions { + retentionDays?: number + compressionLevel?: number +} + +/** + * @github/local-action Unmodified + */ +export interface GetArtifactResponse { + artifact: Artifact +} + +/** + * @github/local-action Unmodified + */ +export interface ListArtifactsOptions { + latest?: boolean +} + +/** + * @github/local-action Unmodified + */ +export interface ListArtifactsResponse { + artifacts: Artifact[] +} + +/** + * @github/local-action Unmodified + */ +export interface DownloadArtifactResponse { + downloadPath?: string +} + +/** + * @github/local-action Unmodified + */ +export interface DownloadArtifactOptions { + path?: string +} + +/** + * @github/local-action Unmodified + */ +export interface Artifact { + name: string + id: number + size: number + createdAt?: Date +} + +/** + * @github/local-action Unmodified + */ +export interface FindOptions { + findBy?: { + token: string + workflowRunId: number + repositoryOwner: string + repositoryName: string + } +} + +/** + * @github/local-action Unmodified + */ +export interface DeleteArtifactResponse { + id: number +} diff --git a/src/stubs/artifact/internal/shared/user-agent.ts b/src/stubs/artifact/internal/shared/user-agent.ts new file mode 100644 index 0000000..28a6358 --- /dev/null +++ b/src/stubs/artifact/internal/shared/user-agent.ts @@ -0,0 +1,6 @@ +/** + * @github/local-action Modified + */ +export function getUserAgentString(): string { + return `@github/local-action-${process.env.npm_package_version}` +} diff --git a/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts b/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts new file mode 100644 index 0000000..d6ed345 --- /dev/null +++ b/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts @@ -0,0 +1,86 @@ +/** + * @github/local-action Unmodified + */ + +import { info } from '../../../core.js' + +/** + * Invalid characters that cannot be in the artifact name or an uploaded file. Will be rejected + * from the server if attempted to be sent over. These characters are not allowed due to limitations with certain + * file systems such as NTFS. To maintain platform-agnostic behavior, all characters that are not supported by an + * individual filesystem/platform will not be supported on all fileSystems/platforms + * + * FilePaths can include characters such as \ and / which are not permitted in the artifact name alone + */ +const invalidArtifactFilePathCharacters = new Map([ + ['"', ' Double quote "'], + [':', ' Colon :'], + ['<', ' Less than <'], + ['>', ' Greater than >'], + ['|', ' Vertical bar |'], + ['*', ' Asterisk *'], + ['?', ' Question mark ?'], + ['\r', ' Carriage return \\r'], + ['\n', ' Line feed \\n'] +]) + +const invalidArtifactNameCharacters = new Map([ + ...invalidArtifactFilePathCharacters, + ['\\', ' Backslash \\'], + ['/', ' Forward slash /'] +]) + +/** + * Validates the name of the artifact to check to make sure there are no illegal characters + */ +export function validateArtifactName(name: string): void { + if (!name) { + throw new Error(`Provided artifact name input during validation is empty`) + } + + for (const [ + invalidCharacterKey, + errorMessageForCharacter + ] of invalidArtifactNameCharacters) { + if (name.includes(invalidCharacterKey)) { + throw new Error( + `The artifact name is not valid: ${name}. Contains the following character: ${errorMessageForCharacter} + +Invalid characters include: ${Array.from( + invalidArtifactNameCharacters.values() + ).toString()} + +These characters are not allowed in the artifact name due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems.` + ) + } + } + + info(`Artifact name is valid!`) +} + +/** + * Validates file paths to check for any illegal characters that can cause problems on different file systems + */ +export function validateFilePath(path: string): void { + if (!path) { + throw new Error(`Provided file path input during validation is empty`) + } + + for (const [ + invalidCharacterKey, + errorMessageForCharacter + ] of invalidArtifactFilePathCharacters) { + if (path.includes(invalidCharacterKey)) { + throw new Error( + `The path for one of the files in artifact is not valid: ${path}. Contains the following character: ${errorMessageForCharacter} + +Invalid characters include: ${Array.from( + invalidArtifactFilePathCharacters.values() + ).toString()} + +The following characters are not allowed in files that are uploaded due to limitations with certain file systems such as NTFS. To maintain file system agnostic behavior, these characters are intentionally not allowed to prevent potential problems with downloads on different file systems. + ` + ) + } + } +} diff --git a/src/stubs/artifact/internal/upload/upload-artifact.ts b/src/stubs/artifact/internal/upload/upload-artifact.ts new file mode 100644 index 0000000..47865a8 --- /dev/null +++ b/src/stubs/artifact/internal/upload/upload-artifact.ts @@ -0,0 +1,111 @@ +import * as core from '@actions/core' +import crypto from 'crypto' +import fs from 'fs' +import path from 'path' +import { finished } from 'stream/promises' +import { EnvMeta } from '../../../../stubs/env.js' +import { FilesNotFoundError, InvalidResponseError } from '../shared/errors.js' +import type { + Artifact, + UploadArtifactOptions, + UploadArtifactResponse +} from '../shared/interfaces.js' +import { validateArtifactName } from './path-and-artifact-name-validation.js' +import { + UploadZipSpecification, + getUploadZipSpecification, + validateRootDirectory +} from './upload-zip-specification.js' +import { createZipUploadStream } from './zip.js' + +/** + * @github/local-action Modified + */ +export async function uploadArtifact( + name: string, + files: string[], + rootDirectory: string, + options?: UploadArtifactOptions | undefined +): Promise { + validateArtifactName(name) + validateRootDirectory(rootDirectory) + + const zipSpecification: UploadZipSpecification[] = getUploadZipSpecification( + files, + rootDirectory + ) + if (zipSpecification.length === 0) + throw new FilesNotFoundError( + zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : [])) + ) + + // Multiple artifacts cannot have the same name. + if (EnvMeta.artifacts.some(a => a.name === name)) + throw new Error(`An artifact with the name ${name} already exists`) + + // Only 10 artifacts can be created in a single job. + if (EnvMeta.artifacts.length >= 10) + throw new Error('Maximum number of artifacts (10) created') + + // Create the artifact metadata + const artifact: Artifact = { + name, + id: EnvMeta.artifacts.length + 1, + size: 0, + createdAt: new Date() + } + + const response: UploadArtifactResponse = { + size: 0, + id: artifact.id, + digest: '' + } + + const zipUploadStream = await createZipUploadStream( + zipSpecification, + options?.compressionLevel + ) + + const writeStream = fs.createWriteStream( + path.join(process.env.LOCAL_ACTION_ARTIFACT_PATH!, `${artifact.name}.zip`) + ) + const hashStream = crypto.createHash('sha256') + + writeStream + .on('error', error => { + throw error + }) + .on('finish', () => { + try { + core.info(`Finalizing artifact upload`) + EnvMeta.artifacts.push(artifact) + core.info( + `Artifact ${artifact.name}.zip successfully finalized. Artifact ID ${artifact.id}` + ) + + response.size = artifact.size + core.info(`Artifact ${artifact.name}.zip successfully written to disk`) + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + core.debug(`Artifact creation failed: ${error}`) + throw new InvalidResponseError( + 'CreateArtifact: response from backend was not ok' + ) + } + }) + + zipUploadStream + .on('data', chunk => { + artifact.size += chunk.length + }) + .pipe(writeStream) + zipUploadStream.pipe(hashStream).setEncoding('hex') + + await finished(writeStream) + + hashStream.end() + response.digest = hashStream.read() + + return response +} diff --git a/src/stubs/artifact/internal/upload/upload-zip-specification.ts b/src/stubs/artifact/internal/upload/upload-zip-specification.ts new file mode 100644 index 0000000..9bc519c --- /dev/null +++ b/src/stubs/artifact/internal/upload/upload-zip-specification.ts @@ -0,0 +1,124 @@ +/** + * @github/local-action Unmodified + */ + +import * as fs from 'fs' +import { normalize, resolve } from 'path' +import * as core from '../../../core.js' +import { validateFilePath } from './path-and-artifact-name-validation.js' + +export interface UploadZipSpecification { + /** + * An absolute source path that points to a file that will be added to a zip. Null if creating a new directory + */ + sourcePath: string | null + + /** + * The destination path in a zip for a file + */ + destinationPath: string + + /** + * Information about the file + * https://nodejs.org/api/fs.html#class-fsstats + */ + stats: fs.Stats +} + +/** + * Checks if a root directory exists and is valid + * @param rootDirectory an absolute root directory path common to all input files that that will be trimmed from the final zip structure + */ +export function validateRootDirectory(rootDirectory: string): void { + if (!fs.existsSync(rootDirectory)) { + throw new Error( + `The provided rootDirectory ${rootDirectory} does not exist` + ) + } + if (!fs.statSync(rootDirectory).isDirectory()) { + throw new Error( + `The provided rootDirectory ${rootDirectory} is not a valid directory` + ) + } + core.info(`Root directory input is valid!`) +} + +/** + * Creates a specification that describes how a zip file will be created for a set of input files + * @param filesToZip a list of file that should be included in the zip + * @param rootDirectory an absolute root directory path common to all input files that that will be trimmed from the final zip structure + */ +export function getUploadZipSpecification( + filesToZip: string[], + rootDirectory: string +): UploadZipSpecification[] { + const specification: UploadZipSpecification[] = [] + + // Normalize and resolve, this allows for either absolute or relative paths to be used + rootDirectory = normalize(rootDirectory) + rootDirectory = resolve(rootDirectory) + + /* + Example + + Input: + rootDirectory: '/home/user/files/plz-upload' + artifactFiles: [ + '/home/user/files/plz-upload/file1.txt', + '/home/user/files/plz-upload/file2.txt', + '/home/user/files/plz-upload/dir/file3.txt' + ] + + Output: + specifications: [ + ['/home/user/files/plz-upload/file1.txt', '/file1.txt'], + ['/home/user/files/plz-upload/file1.txt', '/file2.txt'], + ['/home/user/files/plz-upload/file1.txt', '/dir/file3.txt'] + ] + + The final zip that is later uploaded will look like this: + + my-artifact.zip + - file.txt + - file2.txt + - dir/ + - file3.txt + */ + for (let file of filesToZip) { + const stats = fs.lstatSync(file, { throwIfNoEntry: false }) + if (!stats) { + throw new Error(`File ${file} does not exist`) + } + if (!stats.isDirectory()) { + // Normalize and resolve, this allows for either absolute or relative paths to be used + file = normalize(file) + file = resolve(file) + if (!file.startsWith(rootDirectory)) { + throw new Error( + `The rootDirectory: ${rootDirectory} is not a parent directory of the file: ${file}` + ) + } + + // Check for forbidden characters in file paths that may cause ambiguous behavior if downloaded on different file systems + const uploadPath = file.replace(rootDirectory, '') + validateFilePath(uploadPath) + + specification.push({ + sourcePath: file, + destinationPath: uploadPath, + stats + }) + } else { + // Empty directory + const directoryPath = file.replace(rootDirectory, '') + validateFilePath(directoryPath) + + specification.push({ + sourcePath: null, + destinationPath: directoryPath, + stats + }) + } + } + return specification +} diff --git a/src/stubs/artifact/internal/upload/zip.ts b/src/stubs/artifact/internal/upload/zip.ts new file mode 100644 index 0000000..a026c92 --- /dev/null +++ b/src/stubs/artifact/internal/upload/zip.ts @@ -0,0 +1,111 @@ +/** + * @github/local-action Unmodified + */ + +import archiver from 'archiver' +import { realpath } from 'fs/promises' +import * as stream from 'stream' +import * as core from '../../../core.js' +import { getUploadChunkSize } from '../shared/config.js' +import type { UploadZipSpecification } from './upload-zip-specification.js' + +export const DEFAULT_COMPRESSION_LEVEL = 6 + +// Custom stream transformer so we can set the highWaterMark property +// See https://github.com/nodejs/node/issues/8855 +export class ZipUploadStream extends stream.Transform { + constructor(bufferSize: number) { + super({ + highWaterMark: bufferSize + }) + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + _transform(chunk: any, enc: any, cb: any): void { + cb(null, chunk) + } +} + +export async function createZipUploadStream( + uploadSpecification: UploadZipSpecification[], + compressionLevel: number = DEFAULT_COMPRESSION_LEVEL +): Promise { + core.debug( + `Creating Artifact archive with compressionLevel: ${compressionLevel}` + ) + + const zip = archiver.create('zip', { + highWaterMark: getUploadChunkSize(), + zlib: { level: compressionLevel } + }) + + // register callbacks for various events during the zip lifecycle + zip.on('error', zipErrorCallback) + zip.on('warning', zipWarningCallback) + zip.on('finish', zipFinishCallback) + zip.on('end', zipEndCallback) + + for (const file of uploadSpecification) { + if (file.sourcePath !== null) { + // Check if symlink and resolve the source path + let sourcePath = file.sourcePath + if (file.stats.isSymbolicLink()) { + sourcePath = await realpath(file.sourcePath) + } + + // Add the file to the zip + zip.file(sourcePath, { + name: file.destinationPath + }) + } else { + // Add a directory to the zip + zip.append('', { name: file.destinationPath }) + } + } + + const bufferSize = getUploadChunkSize() + const zipUploadStream = new ZipUploadStream(bufferSize) + + core.debug( + `Zip write high watermark value ${zipUploadStream.writableHighWaterMark}` + ) + core.debug( + `Zip read high watermark value ${zipUploadStream.readableHighWaterMark}` + ) + + zip.pipe(zipUploadStream) + zip.finalize() + + return zipUploadStream +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const zipErrorCallback = (error: any): void => { + core.error('An error has occurred while creating the zip file for upload') + core.info(error) + + throw new Error('An error has occurred during zip creation for the artifact') +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const zipWarningCallback = (error: any): void => { + if (error.code === 'ENOENT') { + core.warning( + 'ENOENT warning during artifact zip creation. No such file or directory' + ) + core.info(error) + } else { + core.warning( + `A non-blocking warning has occurred during artifact zip creation: ${error.code}` + ) + core.info(error) + } +} + +const zipFinishCallback = (): void => { + core.debug('Zip stream for upload has finished.') +} + +const zipEndCallback = (): void => { + core.debug('Zip stream for upload has ended.') +} diff --git a/src/stubs/env.ts b/src/stubs/env.ts index c155e58..84e331f 100644 --- a/src/stubs/env.ts +++ b/src/stubs/env.ts @@ -6,6 +6,7 @@ import type { EnvMetadata } from '../types.js' export const EnvMeta: EnvMetadata = { actionFile: '', actionPath: '', + artifacts: [], dotenvFile: '', entrypoint: '', env: {}, @@ -22,6 +23,7 @@ export const EnvMeta: EnvMetadata = { export function ResetEnvMetadata(): void { EnvMeta.actionFile = '' EnvMeta.actionPath = '' + EnvMeta.artifacts = [] EnvMeta.dotenvFile = '' EnvMeta.entrypoint = '' EnvMeta.env = {} diff --git a/src/types.ts b/src/types.ts index 41bef77..06d07b2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import { Artifact } from './stubs/artifact/artifact.js' + /** Environment Metadata */ export type EnvMetadata = { /** Path to the `action.yml` file */ @@ -6,6 +8,9 @@ export type EnvMetadata = { /** Path to the action directory */ actionPath: string + /** Map of Action Artifacts */ + artifacts: Artifact[] + /** Path to the `.env` file */ dotenvFile: string From a78369638ee1f0611ec4177a1bda3bec6e3dcfc4 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Tue, 7 Jan 2025 12:46:46 -0500 Subject: [PATCH 3/8] Fix type reference --- __tests__/stubs/env-stubs.test.ts | 3 +++ src/types.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/__tests__/stubs/env-stubs.test.ts b/__tests__/stubs/env-stubs.test.ts index 15aac69..d46f8f4 100644 --- a/__tests__/stubs/env-stubs.test.ts +++ b/__tests__/stubs/env-stubs.test.ts @@ -7,6 +7,7 @@ import type { EnvMetadata } from '../../src/types.js' const empty: EnvMetadata = { actionFile: '', actionPath: '', + artifacts: [], dotenvFile: '', entrypoint: '', env: {}, @@ -37,6 +38,7 @@ describe('Env', () => { // Update the metadata EnvMeta.actionFile = 'action.yml' EnvMeta.actionPath = '/some/path' + EnvMeta.artifacts = [{ id: 1, name: 'test', size: 0 }] EnvMeta.dotenvFile = '.env' EnvMeta.entrypoint = 'main.ts' EnvMeta.env = { TEST: 'test' } @@ -48,6 +50,7 @@ describe('Env', () => { expect(EnvMeta).toMatchObject({ actionFile: 'action.yml', actionPath: '/some/path', + artifacts: [{ id: 1, name: 'test', size: 0 }], dotenvFile: '.env', entrypoint: 'main.ts', env: { TEST: 'test' }, diff --git a/src/types.ts b/src/types.ts index 06d07b2..68abec9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,4 @@ -import { Artifact } from './stubs/artifact/artifact.js' +import type { Artifact } from './stubs/artifact/internal/shared/interfaces.js' /** Environment Metadata */ export type EnvMetadata = { From f16988738d3182963ad666352f64ed6358e4335a Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Tue, 7 Jan 2025 12:56:01 -0500 Subject: [PATCH 4/8] Ignore unmodified artifacts functionality from coverage --- src/stubs/artifact/internal/client.ts | 1 + src/stubs/artifact/internal/delete/delete-artifact.ts | 1 + .../artifact/internal/download/download-artifact.ts | 6 ++++++ src/stubs/artifact/internal/find/get-artifact.ts | 1 + src/stubs/artifact/internal/find/list-artifacts.ts | 2 ++ src/stubs/artifact/internal/find/retry-options.ts | 1 + src/stubs/artifact/internal/shared/config.ts | 2 ++ src/stubs/artifact/internal/shared/errors.ts | 11 ++--------- .../upload/path-and-artifact-name-validation.ts | 1 + .../internal/upload/upload-zip-specification.ts | 1 + src/stubs/artifact/internal/upload/zip.ts | 1 + 11 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/stubs/artifact/internal/client.ts b/src/stubs/artifact/internal/client.ts index c6623b2..96717be 100644 --- a/src/stubs/artifact/internal/client.ts +++ b/src/stubs/artifact/internal/client.ts @@ -30,6 +30,7 @@ import { uploadArtifact } from './upload/upload-artifact.js' /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export interface ArtifactClient { /** * Uploads an artifact. diff --git a/src/stubs/artifact/internal/delete/delete-artifact.ts b/src/stubs/artifact/internal/delete/delete-artifact.ts index 6d0e4ad..d55a1ec 100644 --- a/src/stubs/artifact/internal/delete/delete-artifact.ts +++ b/src/stubs/artifact/internal/delete/delete-artifact.ts @@ -19,6 +19,7 @@ import { getUserAgentString } from '../shared/user-agent.js' /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export async function deleteArtifactPublic( artifactName: string, workflowRunId: number, diff --git a/src/stubs/artifact/internal/download/download-artifact.ts b/src/stubs/artifact/internal/download/download-artifact.ts index fb3f3a4..6aa11bd 100644 --- a/src/stubs/artifact/internal/download/download-artifact.ts +++ b/src/stubs/artifact/internal/download/download-artifact.ts @@ -18,6 +18,7 @@ import { getUserAgentString } from '../shared/user-agent.js' /** * @github/local-action Unmodified */ +/* istanbul ignore next */ const scrubQueryParameters = (url: string): string => { const parsed = new URL(url) parsed.search = '' @@ -27,6 +28,7 @@ const scrubQueryParameters = (url: string): string => { /** * @github/local-action Unmodified */ +/* istanbul ignore next */ async function exists(path: string): Promise { try { fs.accessSync(path) @@ -41,6 +43,7 @@ async function exists(path: string): Promise { /** * @github/local-action Unmodified */ +/* istanbul ignore next */ async function streamExtract(url: string, directory: string): Promise { let retryCount = 0 while (retryCount < 5) { @@ -64,6 +67,7 @@ async function streamExtract(url: string, directory: string): Promise { /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export async function streamExtractExternal( url: string, directory: string @@ -111,6 +115,7 @@ export async function streamExtractExternal( /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export async function downloadArtifactPublic( artifactId: number, repositoryOwner: string, @@ -208,6 +213,7 @@ export async function downloadArtifactInternal( /** * @github/local-action Unmodified */ +/* istanbul ignore next */ async function resolveOrCreateDirectory( downloadPath = getGitHubWorkspaceDir() ): Promise { diff --git a/src/stubs/artifact/internal/find/get-artifact.ts b/src/stubs/artifact/internal/find/get-artifact.ts index cf73c3f..c7ce69e 100644 --- a/src/stubs/artifact/internal/find/get-artifact.ts +++ b/src/stubs/artifact/internal/find/get-artifact.ts @@ -16,6 +16,7 @@ import { getRetryOptions } from './retry-options.js' /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export async function getArtifactPublic( artifactName: string, workflowRunId: number, diff --git a/src/stubs/artifact/internal/find/list-artifacts.ts b/src/stubs/artifact/internal/find/list-artifacts.ts index 47773d3..9a592e1 100644 --- a/src/stubs/artifact/internal/find/list-artifacts.ts +++ b/src/stubs/artifact/internal/find/list-artifacts.ts @@ -17,6 +17,7 @@ const maxNumberOfPages = maximumArtifactCount / paginationCount /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export async function listArtifactsPublic( workflowRunId: number, repositoryOwner: string, @@ -132,6 +133,7 @@ export async function listArtifactsInternal( /** * @github/local-action Unmodified */ +/* istanbul ignore next */ function filterLatest(artifacts: Artifact[]): Artifact[] { artifacts.sort((a, b) => b.id - a.id) const latestArtifacts: Artifact[] = [] diff --git a/src/stubs/artifact/internal/find/retry-options.ts b/src/stubs/artifact/internal/find/retry-options.ts index 7d7e9de..61f1c46 100644 --- a/src/stubs/artifact/internal/find/retry-options.ts +++ b/src/stubs/artifact/internal/find/retry-options.ts @@ -1,6 +1,7 @@ /** * @github/local-action Unmodified */ +/* istanbul ignore file */ import type { OctokitOptions } from '@octokit/core' import type { RequestRequestOptions } from '@octokit/types' diff --git a/src/stubs/artifact/internal/shared/config.ts b/src/stubs/artifact/internal/shared/config.ts index 5d351a2..e2db911 100644 --- a/src/stubs/artifact/internal/shared/config.ts +++ b/src/stubs/artifact/internal/shared/config.ts @@ -1,6 +1,7 @@ /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export function getUploadChunkSize(): number { return 8 * 1024 * 1024 // 8 MB Chunks } @@ -8,6 +9,7 @@ export function getUploadChunkSize(): number { /** * @github/local-action Unmodified */ +/* istanbul ignore next */ export function isGhes(): boolean { const ghUrl = new URL( process.env['GITHUB_SERVER_URL'] || 'https://github.com' diff --git a/src/stubs/artifact/internal/shared/errors.ts b/src/stubs/artifact/internal/shared/errors.ts index 5edd1ea..68e4efe 100644 --- a/src/stubs/artifact/internal/shared/errors.ts +++ b/src/stubs/artifact/internal/shared/errors.ts @@ -1,6 +1,8 @@ /** * @github/local-action Unmodified */ +/* istanbul ignore file */ + export class FilesNotFoundError extends Error { files: string[] @@ -16,9 +18,6 @@ export class FilesNotFoundError extends Error { } } -/** - * @github/local-action Unmodified - */ export class InvalidResponseError extends Error { constructor(message: string) { super(message) @@ -26,9 +25,6 @@ export class InvalidResponseError extends Error { } } -/** - * @github/local-action Unmodified - */ export class ArtifactNotFoundError extends Error { constructor(message = 'Artifact not found') { super(message) @@ -36,9 +32,6 @@ export class ArtifactNotFoundError extends Error { } } -/** - * @github/local-action Unmodified - */ export class GHESNotSupportedError extends Error { constructor( message = '@actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES.' diff --git a/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts b/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts index d6ed345..fab338b 100644 --- a/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts +++ b/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts @@ -1,6 +1,7 @@ /** * @github/local-action Unmodified */ +/* istanbul ignore file */ import { info } from '../../../core.js' diff --git a/src/stubs/artifact/internal/upload/upload-zip-specification.ts b/src/stubs/artifact/internal/upload/upload-zip-specification.ts index 9bc519c..6cc09d4 100644 --- a/src/stubs/artifact/internal/upload/upload-zip-specification.ts +++ b/src/stubs/artifact/internal/upload/upload-zip-specification.ts @@ -1,6 +1,7 @@ /** * @github/local-action Unmodified */ +/* istanbul ignore file */ import * as fs from 'fs' import { normalize, resolve } from 'path' diff --git a/src/stubs/artifact/internal/upload/zip.ts b/src/stubs/artifact/internal/upload/zip.ts index a026c92..4f16eb5 100644 --- a/src/stubs/artifact/internal/upload/zip.ts +++ b/src/stubs/artifact/internal/upload/zip.ts @@ -1,6 +1,7 @@ /** * @github/local-action Unmodified */ +/* istanbul ignore file */ import archiver from 'archiver' import { realpath } from 'fs/promises' From 24f63f03f504d09b2f5e72eadc5ce70407140ada Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Tue, 7 Jan 2025 15:06:18 -0500 Subject: [PATCH 5/8] Add unit tests --- __fixtures__/crypto.ts | 7 + __fixtures__/fs.ts | 15 + __fixtures__/stream/promises.ts | 3 + .../stubs/artifact/internal/client.test.ts | 288 ++++++++++++++++++ .../internal/delete/delete-artifact.test.ts | 66 ++++ .../download/download-artifact.test.ts | 62 ++++ .../internal/find/get-artifact.test.ts | 55 ++++ .../internal/find/list-artifacts.test.ts | 42 +++ .../artifact/internal/shared/config.test.ts | 28 ++ .../internal/shared/user-agent.test.ts | 23 ++ .../internal/upload/upload-artifact.test.ts | 128 ++++++++ .../internal/download/download-artifact.ts | 4 +- .../artifact/internal/find/list-artifacts.ts | 2 +- src/stubs/artifact/internal/shared/config.ts | 6 +- .../internal/upload/upload-artifact.ts | 5 +- tsconfig.eslint.json | 3 + 16 files changed, 727 insertions(+), 10 deletions(-) create mode 100644 __fixtures__/crypto.ts create mode 100644 __fixtures__/fs.ts create mode 100644 __fixtures__/stream/promises.ts create mode 100644 __tests__/stubs/artifact/internal/client.test.ts create mode 100644 __tests__/stubs/artifact/internal/delete/delete-artifact.test.ts create mode 100644 __tests__/stubs/artifact/internal/download/download-artifact.test.ts create mode 100644 __tests__/stubs/artifact/internal/find/get-artifact.test.ts create mode 100644 __tests__/stubs/artifact/internal/find/list-artifacts.test.ts create mode 100644 __tests__/stubs/artifact/internal/shared/config.test.ts create mode 100644 __tests__/stubs/artifact/internal/shared/user-agent.test.ts create mode 100644 __tests__/stubs/artifact/internal/upload/upload-artifact.test.ts diff --git a/__fixtures__/crypto.ts b/__fixtures__/crypto.ts new file mode 100644 index 0000000..581ea19 --- /dev/null +++ b/__fixtures__/crypto.ts @@ -0,0 +1,7 @@ +import { jest } from '@jest/globals' + +export const createHash = jest.fn() + +export default { + createHash +} diff --git a/__fixtures__/fs.ts b/__fixtures__/fs.ts new file mode 100644 index 0000000..5259e91 --- /dev/null +++ b/__fixtures__/fs.ts @@ -0,0 +1,15 @@ +import { jest } from '@jest/globals' + +export const accessSync = jest.fn() +export const createWriteStream = jest.fn() +export const createReadStream = jest.fn() +export const mkdirSync = jest.fn() +export const rmSync = jest.fn() + +export default { + accessSync, + createWriteStream, + createReadStream, + mkdirSync, + rmSync +} diff --git a/__fixtures__/stream/promises.ts b/__fixtures__/stream/promises.ts new file mode 100644 index 0000000..0fdeee9 --- /dev/null +++ b/__fixtures__/stream/promises.ts @@ -0,0 +1,3 @@ +import { jest } from '@jest/globals' + +export const finished = jest.fn() diff --git a/__tests__/stubs/artifact/internal/client.test.ts b/__tests__/stubs/artifact/internal/client.test.ts new file mode 100644 index 0000000..c840cc5 --- /dev/null +++ b/__tests__/stubs/artifact/internal/client.test.ts @@ -0,0 +1,288 @@ +import { jest } from '@jest/globals' +import * as core from '../../../../__fixtures__/core.js' +import { ResetCoreMetadata } from '../../../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../../../src/stubs/env.js' + +const isGhes = jest.fn().mockReturnValue(false) +const getGitHubWorkspaceDir = jest.fn().mockReturnValue('/github/workspace') +const getUploadChunkSize = jest.fn().mockReturnValue(8 * 1024 * 1024) +const uploadArtifact = jest.fn() +const downloadArtifactInternal = jest.fn() +const downloadArtifactPublic = jest.fn() +const listArtifactsInternal = jest.fn() +const listArtifactsPublic = jest.fn() +const getArtifactInternal = jest.fn() +const getArtifactPublic = jest.fn() +const deleteArtifactInternal = jest.fn() +const deleteArtifactPublic = jest.fn() + +jest.unstable_mockModule( + '../../../../src/stubs/artifact/internal/shared/config.js', + () => ({ + isGhes, + getGitHubWorkspaceDir, + getUploadChunkSize + }) +) +jest.unstable_mockModule( + '../../../../src/stubs/artifact/internal/upload/upload-artifact.js', + () => ({ + uploadArtifact + }) +) +jest.unstable_mockModule( + '../../../../src/stubs/artifact/internal/download/download-artifact.js', + () => ({ + downloadArtifactPublic, + downloadArtifactInternal + }) +) +jest.unstable_mockModule( + '../../../../src/stubs/artifact/internal/find/list-artifacts.js', + () => ({ + listArtifactsInternal, + listArtifactsPublic + }) +) +jest.unstable_mockModule( + '../../../../src/stubs/artifact/internal/find/get-artifact.js', + () => ({ + getArtifactInternal, + getArtifactPublic + }) +) +jest.unstable_mockModule( + '../../../../src/stubs/artifact/internal/delete/delete-artifact.js', + () => ({ + deleteArtifactInternal, + deleteArtifactPublic + }) +) +jest.unstable_mockModule('../../../../src/stubs/core.js', () => core) + +const { DefaultArtifactClient } = await import( + '../../../../src/stubs/artifact/internal/client.js' +) + +describe('DefaultArtifactClient', () => { + beforeEach(() => { + // Set environment variables + process.env.LOCAL_ACTION_ARTIFACT_PATH = '/tmp/artifacts' + + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + + EnvMeta.artifacts = [{ name: 'artifact-name', id: 1, size: 0 }] + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + + // Unset environment variables + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + }) + + describe('uploadArtifact', () => { + it('Throws if LOCAL_ACTION_ARTIFACT_PATH is not set', async () => { + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + + const client = new DefaultArtifactClient() + + await expect( + client.uploadArtifact('artifact-name', ['file1', 'file2'], 'root') + ).rejects.toThrow() + }) + + it('Throws if running on GHES', async () => { + isGhes.mockReturnValue(true) + + const client = new DefaultArtifactClient() + + await expect( + client.uploadArtifact('artifact-name', ['file1', 'file2'], 'root') + ).rejects.toThrow() + }) + + it('Uploads an artifact', async () => { + const client = new DefaultArtifactClient() + + await client.uploadArtifact('artifact-name', ['file1', 'file2'], 'root') + + expect(uploadArtifact).toHaveBeenCalled() + }) + }) + + describe('downloadArtifact', () => { + it('Throws if LOCAL_ACTION_ARTIFACT_PATH is not set', async () => { + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + + const client = new DefaultArtifactClient() + + await expect(client.downloadArtifact(1)).rejects.toThrow() + }) + + it('Throws if running on GHES', async () => { + isGhes.mockReturnValue(true) + + const client = new DefaultArtifactClient() + + await expect(client.downloadArtifact(1)).rejects.toThrow() + }) + + it('Downloads an artifact (internal)', async () => { + const client = new DefaultArtifactClient() + + await client.downloadArtifact(1) + + expect(downloadArtifactInternal).toHaveBeenCalled() + expect(downloadArtifactPublic).not.toHaveBeenCalled() + }) + + it('Downloads an artifact (public)', async () => { + const client = new DefaultArtifactClient() + + await client.downloadArtifact(1, { + findBy: { + repositoryOwner: 'owner', + repositoryName: 'repo', + workflowRunId: 1, + token: 'token' + } + }) + + expect(downloadArtifactInternal).not.toHaveBeenCalled() + expect(downloadArtifactPublic).toHaveBeenCalled() + }) + }) + + describe('listArtifacts', () => { + it('Throws if LOCAL_ACTION_ARTIFACT_PATH is not set', async () => { + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + + const client = new DefaultArtifactClient() + + await expect(client.listArtifacts()).rejects.toThrow() + }) + + it('Throws if running on GHES', async () => { + isGhes.mockReturnValue(true) + + const client = new DefaultArtifactClient() + + await expect(client.listArtifacts()).rejects.toThrow() + }) + + it('Lists artifacts (internal)', async () => { + const client = new DefaultArtifactClient() + + await client.listArtifacts() + + expect(listArtifactsInternal).toHaveBeenCalled() + expect(listArtifactsPublic).not.toHaveBeenCalled() + }) + + it('Lists artifacts (public)', async () => { + const client = new DefaultArtifactClient() + + await client.listArtifacts({ + findBy: { + repositoryOwner: 'owner', + repositoryName: 'repo', + workflowRunId: 1, + token: 'token' + } + }) + + expect(listArtifactsInternal).not.toHaveBeenCalled() + expect(listArtifactsPublic).toHaveBeenCalled() + }) + }) + + describe('getArtifact', () => { + it('Throws if LOCAL_ACTION_ARTIFACT_PATH is not set', async () => { + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + + const client = new DefaultArtifactClient() + + await expect(client.getArtifact('artifact-name')).rejects.toThrow() + }) + + it('Throws if running on GHES', async () => { + isGhes.mockReturnValue(true) + + const client = new DefaultArtifactClient() + + await expect(client.getArtifact('artifact-name')).rejects.toThrow() + }) + + it('Gets an artifact (internal)', async () => { + const client = new DefaultArtifactClient() + + await client.getArtifact('artifact-name') + + expect(getArtifactInternal).toHaveBeenCalled() + expect(getArtifactPublic).not.toHaveBeenCalled() + }) + + it('Gets an artifact (public)', async () => { + const client = new DefaultArtifactClient() + + await client.getArtifact('artifact-name', { + findBy: { + repositoryOwner: 'owner', + repositoryName: 'repo', + workflowRunId: 1, + token: 'token' + } + }) + + expect(getArtifactInternal).not.toHaveBeenCalled() + expect(getArtifactPublic).toHaveBeenCalled() + }) + }) + + describe('deleteArtifact', () => { + it('Throws if LOCAL_ACTION_ARTIFACT_PATH is not set', async () => { + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + + const client = new DefaultArtifactClient() + + await expect(client.deleteArtifact('artifact-name')).rejects.toThrow() + }) + + it('Throws if running on GHES', async () => { + isGhes.mockReturnValue(true) + + const client = new DefaultArtifactClient() + + await expect(client.deleteArtifact('artifact-name')).rejects.toThrow() + }) + + it('Deletes an artifact (internal)', async () => { + const client = new DefaultArtifactClient() + + await client.deleteArtifact('artifact-name') + + expect(deleteArtifactInternal).toHaveBeenCalled() + expect(deleteArtifactPublic).not.toHaveBeenCalled() + }) + + it('Deletes an artifact (public)', async () => { + const client = new DefaultArtifactClient() + + await client.deleteArtifact('artifact-name', { + findBy: { + repositoryOwner: 'owner', + repositoryName: 'repo', + workflowRunId: 1, + token: 'token' + } + }) + + expect(deleteArtifactInternal).not.toHaveBeenCalled() + expect(deleteArtifactPublic).toHaveBeenCalled() + }) + }) +}) diff --git a/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts b/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts new file mode 100644 index 0000000..5531ede --- /dev/null +++ b/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts @@ -0,0 +1,66 @@ +import { jest } from '@jest/globals' +import * as fs from '../../../../../__fixtures__/fs.js' +import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' + +jest.unstable_mockModule('fs', () => fs) + +const deleteArtifact = await import( + '../../../../../src/stubs/artifact/internal/delete/delete-artifact.js' +) + +describe('delete-artifact', () => { + beforeEach(() => { + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + + // Set environment variables + process.env.LOCAL_ACTION_ARTIFACT_PATH = '/tmp/artifacts' + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + + // Unset environment variables + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + }) + + describe('deleteArtifactInternal', () => { + it('Deletes an artifact', async () => { + EnvMeta.artifacts = [{ name: 'artifact-name', id: 1, size: 0 }] + + const response = + await deleteArtifact.deleteArtifactInternal('artifact-name') + + expect(fs.rmSync).toHaveBeenCalledTimes(1) + expect(EnvMeta.artifacts.length).toBe(0) + expect(response).toMatchObject({ + id: 1 + }) + }) + + it('Deletes the latest artifact', async () => { + EnvMeta.artifacts = [ + { name: 'artifact-name', id: 1, size: 0 }, + { name: 'artifact-name', id: 2, size: 0 } + ] + + const response = + await deleteArtifact.deleteArtifactInternal('artifact-name') + + expect(fs.rmSync).toHaveBeenCalledTimes(1) + expect(EnvMeta.artifacts.length).toBe(1) + expect(response).toMatchObject({ + id: 2 + }) + }) + + it('Throws if no matching artifact is found', async () => { + await expect( + deleteArtifact.deleteArtifactInternal('artifact-name') + ).rejects.toThrow() + }) + }) +}) diff --git a/__tests__/stubs/artifact/internal/download/download-artifact.test.ts b/__tests__/stubs/artifact/internal/download/download-artifact.test.ts new file mode 100644 index 0000000..51d399e --- /dev/null +++ b/__tests__/stubs/artifact/internal/download/download-artifact.test.ts @@ -0,0 +1,62 @@ +import { jest } from '@jest/globals' +import * as core from '../../../../../__fixtures__/core.js' +import * as crypto from '../../../../../__fixtures__/crypto.js' +import * as fs from '../../../../../__fixtures__/fs.js' +import * as stream from '../../../../../__fixtures__/stream/promises.js' +import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' + +const readStream = { + on: () => readStream, + pipe: () => readStream +} + +jest.unstable_mockModule('@actions/core', () => core) +jest.unstable_mockModule('crypto', () => crypto) +jest.unstable_mockModule('fs', () => fs) +jest.unstable_mockModule('stream/promises', () => stream) + +const downloadArtifact = await import( + '../../../../../src/stubs/artifact/internal/download/download-artifact.js' +) + +describe('download-artifact', () => { + beforeEach(() => { + // Reset environment variables + process.env.LOCAL_ACTION_ARTIFACT_PATH = '/tmp/artifacts' + + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + + // Reset mocks + fs.createReadStream.mockImplementation(() => readStream) + + EnvMeta.artifacts = [{ name: 'artifact-name', id: 1, size: 0 }] + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + + // Unset environment variables + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + }) + + describe('downloadArtifactInternal', () => { + it('Downloads an artifact', async () => { + await downloadArtifact.downloadArtifactInternal(1) + + expect(fs.createReadStream).toHaveBeenCalledTimes(1) + expect(stream.finished).toHaveBeenCalledTimes(1) + }) + + it('Throws if an artifact is not found', async () => { + EnvMeta.artifacts = [] + + await expect( + downloadArtifact.downloadArtifactInternal(1) + ).rejects.toThrow() + }) + }) +}) diff --git a/__tests__/stubs/artifact/internal/find/get-artifact.test.ts b/__tests__/stubs/artifact/internal/find/get-artifact.test.ts new file mode 100644 index 0000000..99ce2db --- /dev/null +++ b/__tests__/stubs/artifact/internal/find/get-artifact.test.ts @@ -0,0 +1,55 @@ +import { jest } from '@jest/globals' +import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' + +const getArtifact = await import( + '../../../../../src/stubs/artifact/internal/find/get-artifact.js' +) + +describe('get-artifact', () => { + beforeEach(() => { + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + }) + + describe('getArtifactInternal', () => { + it('Gets an artifact', async () => { + EnvMeta.artifacts = [{ name: 'artifact-name', id: 1, size: 0 }] + + const response = await getArtifact.getArtifactInternal('artifact-name') + + expect(response.artifact).toMatchObject({ + name: 'artifact-name', + id: 1, + size: 0 + }) + }) + + it('Gets the latest artifact', async () => { + EnvMeta.artifacts = [ + { name: 'artifact-name', id: 1, size: 0 }, + { name: 'artifact-name', id: 2, size: 0 } + ] + + const response = await getArtifact.getArtifactInternal('artifact-name') + + expect(response.artifact).toMatchObject({ + name: 'artifact-name', + id: 2, + size: 0 + }) + }) + + it('Throws if no matching artifact is found', async () => { + await expect( + getArtifact.getArtifactInternal('artifact-name') + ).rejects.toThrow() + }) + }) +}) diff --git a/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts b/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts new file mode 100644 index 0000000..026cda6 --- /dev/null +++ b/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts @@ -0,0 +1,42 @@ +import { jest } from '@jest/globals' +import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' + +const listArtifacts = await import( + '../../../../../src/stubs/artifact/internal/find/list-artifacts.js' +) + +describe('list-artifacts', () => { + beforeEach(() => { + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + }) + + describe('listArtifactsInternal', () => { + it('Lists artifacts', async () => { + EnvMeta.artifacts = [{ name: 'artifact-name', id: 1, size: 0 }] + + const response = await listArtifacts.listArtifactsInternal() + + expect(response.artifacts.length).toBe(1) + }) + + it('Only returns the latest artifacts', async () => { + EnvMeta.artifacts = [ + { name: 'artifact-name', id: 1, size: 0 }, + { name: 'artifact-name', id: 2, size: 0 }, + { name: 'artifact-name-2', id: 3, size: 0 } + ] + + const response = await listArtifacts.listArtifactsInternal(true) + + expect(response.artifacts.length).toBe(2) + }) + }) +}) diff --git a/__tests__/stubs/artifact/internal/shared/config.test.ts b/__tests__/stubs/artifact/internal/shared/config.test.ts new file mode 100644 index 0000000..36c3e1b --- /dev/null +++ b/__tests__/stubs/artifact/internal/shared/config.test.ts @@ -0,0 +1,28 @@ +import { jest } from '@jest/globals' +import { getGitHubWorkspaceDir } from '../../../../../src/stubs/artifact/internal/shared/config.js' +import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' +import { ResetEnvMetadata } from '../../../../../src/stubs/env.js' + +describe('config', () => { + beforeEach(() => { + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + + delete process.env.GITHUB_WORKSPACE + }) + + it('Gets the workspace directory (default)', () => { + expect(getGitHubWorkspaceDir()).toEqual(process.cwd()) + }) + + it('Gets the workspace directory (env var)', () => { + process.env.GITHUB_WORKSPACE = '/tmp/workspace' + expect(getGitHubWorkspaceDir()).toEqual('/tmp/workspace') + }) +}) diff --git a/__tests__/stubs/artifact/internal/shared/user-agent.test.ts b/__tests__/stubs/artifact/internal/shared/user-agent.test.ts new file mode 100644 index 0000000..b55c497 --- /dev/null +++ b/__tests__/stubs/artifact/internal/shared/user-agent.test.ts @@ -0,0 +1,23 @@ +import { jest } from '@jest/globals' +import { getUserAgentString } from '../../../../../src/stubs/artifact/internal/shared/user-agent.js' +import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' +import { ResetEnvMetadata } from '../../../../../src/stubs/env.js' + +describe('user-agent', () => { + beforeEach(() => { + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + }) + + it('Outputs the correct user agent header', () => { + expect(getUserAgentString()).toEqual( + `@github/local-action-${process.env.npm_package_version}` + ) + }) +}) diff --git a/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts b/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts new file mode 100644 index 0000000..5bbc184 --- /dev/null +++ b/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts @@ -0,0 +1,128 @@ +import { jest } from '@jest/globals' +import { type Stats } from 'fs' +import * as core from '../../../../../__fixtures__/core.js' +import * as crypto from '../../../../../__fixtures__/crypto.js' +import * as fs from '../../../../../__fixtures__/fs.js' +import * as stream from '../../../../../__fixtures__/stream/promises.js' +import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' +import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' + +const validateArtifactName = jest.fn() +const validateRootDirectory = jest.fn() +const getUploadZipSpecification = jest.fn() +const createZipUploadStream = jest.fn() +const zipUploadStream = { + on: () => zipUploadStream, + pipe: () => zipUploadStream, + setEncoding: () => zipUploadStream +} +const hashStream = { + end: () => hashStream, + read: () => hashStream +} +const writeStream = { + on: () => writeStream +} + +jest.unstable_mockModule('@actions/core', () => core) +jest.unstable_mockModule('crypto', () => crypto) +jest.unstable_mockModule('fs', () => fs) +jest.unstable_mockModule('stream/promises', () => stream) +jest.unstable_mockModule( + '../../../../../src/stubs/artifact/internal/upload/upload-zip-specification.js', + () => ({ + validateRootDirectory, + getUploadZipSpecification + }) +) +jest.unstable_mockModule( + '../../../../../src/stubs/artifact/internal/upload/path-and-artifact-name-validation.js', + () => ({ + validateArtifactName + }) +) +jest.unstable_mockModule( + '../../../../../src/stubs/artifact/internal/upload/zip.js', + () => ({ + createZipUploadStream + }) +) + +const uploadArtifact = await import( + '../../../../../src/stubs/artifact/internal/upload/upload-artifact.js' +) + +describe('upload-artifacts', () => { + beforeEach(() => { + // Reset environment variables + process.env.LOCAL_ACTION_ARTIFACT_PATH = '/tmp/artifacts' + + // Reset metadata + ResetEnvMetadata() + ResetCoreMetadata() + + // Reset mocks + getUploadZipSpecification.mockImplementation(() => [ + { + sourcePath: 'source-path', + destinationPath: 'destination-path', + stats: {} as Stats + } + ]) + createZipUploadStream.mockImplementation(() => zipUploadStream) + crypto.createHash.mockImplementation(() => hashStream) + fs.createWriteStream.mockImplementation(() => writeStream) + }) + + afterEach(() => { + // Reset all spies + jest.resetAllMocks() + + // Unset environment variables + delete process.env.LOCAL_ACTION_ARTIFACT_PATH + }) + + describe('uploadArtifact', () => { + it('Uploads an artifact', async () => { + await uploadArtifact.uploadArtifact( + 'artifact-name', + ['file1', 'file2'], + 'root' + ) + + expect(getUploadZipSpecification).toHaveBeenCalledTimes(1) + expect(createZipUploadStream).toHaveBeenCalledTimes(1) + expect(fs.createWriteStream).toHaveBeenCalledTimes(1) + expect(crypto.createHash).toHaveBeenCalledTimes(1) + expect(stream.finished).toHaveBeenCalledTimes(1) + }) + + it('Throws if no files are included in the artifact', async () => { + getUploadZipSpecification.mockImplementation(() => []) + + await expect( + uploadArtifact.uploadArtifact('artifact-name', [], 'root') + ).rejects.toThrow() + }) + + it('Throws if an artifact exists with the same name', async () => { + EnvMeta.artifacts = [{ name: 'artifact-name', id: 1, size: 0 }] + + await expect( + uploadArtifact.uploadArtifact('artifact-name', ['file'], 'root') + ).rejects.toThrow() + }) + + it('Throws if more than 10 artifacts are created', async () => { + EnvMeta.artifacts = Array.from({ length: 10 }, (_, i) => ({ + name: `artifact-${i}`, + id: i + 1, + size: 0 + })) + + await expect( + uploadArtifact.uploadArtifact('new-artifact', ['file'], 'root') + ).rejects.toThrow() + }) + }) +}) diff --git a/src/stubs/artifact/internal/download/download-artifact.ts b/src/stubs/artifact/internal/download/download-artifact.ts index 6aa11bd..1d4d6c4 100644 --- a/src/stubs/artifact/internal/download/download-artifact.ts +++ b/src/stubs/artifact/internal/download/download-artifact.ts @@ -182,9 +182,6 @@ export async function downloadArtifactInternal( `No artifacts found for ID: ${artifactId}\nAre you trying to download from a different run? Try specifying a github-token with \`actions:read\` scope.` ) - if (artifacts.length > 1) - core.warning('Multiple artifacts found, defaulting to first.') - try { core.info(`Starting download of artifact to: ${downloadPath}`) @@ -204,6 +201,7 @@ export async function downloadArtifactInternal( core.info(`Artifact download completed successfully.`) // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { + /* istanbul ignore next */ throw new Error(`Unable to download and extract artifact: ${error.message}`) } diff --git a/src/stubs/artifact/internal/find/list-artifacts.ts b/src/stubs/artifact/internal/find/list-artifacts.ts index 9a592e1..edf2487 100644 --- a/src/stubs/artifact/internal/find/list-artifacts.ts +++ b/src/stubs/artifact/internal/find/list-artifacts.ts @@ -5,7 +5,7 @@ import { requestLog } from '@octokit/plugin-request-log' import { retry } from '@octokit/plugin-retry' import { EnvMeta } from '../../../../stubs/env.js' import * as core from '../../../core.js' -import { Artifact, ListArtifactsResponse } from '../shared/interfaces.js' +import type { Artifact, ListArtifactsResponse } from '../shared/interfaces.js' import { getUserAgentString } from '../shared/user-agent.js' import { getRetryOptions } from './retry-options.js' diff --git a/src/stubs/artifact/internal/shared/config.ts b/src/stubs/artifact/internal/shared/config.ts index e2db911..b2edd7e 100644 --- a/src/stubs/artifact/internal/shared/config.ts +++ b/src/stubs/artifact/internal/shared/config.ts @@ -28,9 +28,5 @@ export function isGhes(): boolean { */ export function getGitHubWorkspaceDir(): string { // Default to current working directory - const ghWorkspaceDir = process.env['GITHUB_WORKSPACE'] || process.cwd() - if (!ghWorkspaceDir) { - throw new Error('Unable to get the GITHUB_WORKSPACE env variable') - } - return ghWorkspaceDir + return process.env['GITHUB_WORKSPACE'] || process.cwd() } diff --git a/src/stubs/artifact/internal/upload/upload-artifact.ts b/src/stubs/artifact/internal/upload/upload-artifact.ts index 47865a8..a259a84 100644 --- a/src/stubs/artifact/internal/upload/upload-artifact.ts +++ b/src/stubs/artifact/internal/upload/upload-artifact.ts @@ -12,7 +12,7 @@ import type { } from '../shared/interfaces.js' import { validateArtifactName } from './path-and-artifact-name-validation.js' import { - UploadZipSpecification, + type UploadZipSpecification, getUploadZipSpecification, validateRootDirectory } from './upload-zip-specification.js' @@ -34,6 +34,7 @@ export async function uploadArtifact( files, rootDirectory ) + /* istanbul ignore next */ if (zipSpecification.length === 0) throw new FilesNotFoundError( zipSpecification.flatMap(s => (s.sourcePath ? [s.sourcePath] : [])) @@ -71,6 +72,7 @@ export async function uploadArtifact( ) const hashStream = crypto.createHash('sha256') + /* istanbul ignore next */ writeStream .on('error', error => { throw error @@ -95,6 +97,7 @@ export async function uploadArtifact( } }) + /* istanbul ignore next */ zipUploadStream .on('data', chunk => { artifact.size += chunk.length diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json index 3619e22..4705547 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.eslint.json @@ -5,7 +5,10 @@ "allowJs": true }, "include": [ + "__fixtures__/stream/", "__fixtures__/core.ts", + "__fixtures__/crypto.ts", + "__fixtures__/fs.ts", "__fixtures__/tsconfig-paths.ts", "__tests__", "src", From 78a383134c5a6e2e70f252e3807439805c9d7cb6 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Tue, 7 Jan 2025 15:13:25 -0500 Subject: [PATCH 6/8] Reduce test output --- .../stubs/artifact/internal/delete/delete-artifact.test.ts | 2 ++ .../stubs/artifact/internal/download/download-artifact.test.ts | 2 +- __tests__/stubs/artifact/internal/find/get-artifact.test.ts | 3 +++ __tests__/stubs/artifact/internal/find/list-artifacts.test.ts | 3 +++ .../stubs/artifact/internal/upload/upload-artifact.test.ts | 2 +- src/stubs/artifact/internal/client.ts | 2 +- src/stubs/artifact/internal/upload/upload-artifact.ts | 2 +- 7 files changed, 12 insertions(+), 4 deletions(-) diff --git a/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts b/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts index 5531ede..535adc9 100644 --- a/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts +++ b/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts @@ -1,9 +1,11 @@ import { jest } from '@jest/globals' +import * as core from '../../../../../__fixtures__/core.js' import * as fs from '../../../../../__fixtures__/fs.js' import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' jest.unstable_mockModule('fs', () => fs) +jest.unstable_mockModule('../../../../../src/stubs/core.js', () => core) const deleteArtifact = await import( '../../../../../src/stubs/artifact/internal/delete/delete-artifact.js' diff --git a/__tests__/stubs/artifact/internal/download/download-artifact.test.ts b/__tests__/stubs/artifact/internal/download/download-artifact.test.ts index 51d399e..e6bc5bc 100644 --- a/__tests__/stubs/artifact/internal/download/download-artifact.test.ts +++ b/__tests__/stubs/artifact/internal/download/download-artifact.test.ts @@ -11,10 +11,10 @@ const readStream = { pipe: () => readStream } -jest.unstable_mockModule('@actions/core', () => core) jest.unstable_mockModule('crypto', () => crypto) jest.unstable_mockModule('fs', () => fs) jest.unstable_mockModule('stream/promises', () => stream) +jest.unstable_mockModule('../../../../../src/stubs/core.js', () => core) const downloadArtifact = await import( '../../../../../src/stubs/artifact/internal/download/download-artifact.js' diff --git a/__tests__/stubs/artifact/internal/find/get-artifact.test.ts b/__tests__/stubs/artifact/internal/find/get-artifact.test.ts index 99ce2db..8c463ae 100644 --- a/__tests__/stubs/artifact/internal/find/get-artifact.test.ts +++ b/__tests__/stubs/artifact/internal/find/get-artifact.test.ts @@ -1,7 +1,10 @@ import { jest } from '@jest/globals' +import * as core from '../../../../../__fixtures__/core.js' import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' +jest.unstable_mockModule('../../../../../src/stubs/core.js', () => core) + const getArtifact = await import( '../../../../../src/stubs/artifact/internal/find/get-artifact.js' ) diff --git a/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts b/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts index 026cda6..39b55ba 100644 --- a/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts +++ b/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts @@ -1,7 +1,10 @@ import { jest } from '@jest/globals' +import * as core from '../../../../../__fixtures__/core.js' import { ResetCoreMetadata } from '../../../../../src/stubs/core.js' import { EnvMeta, ResetEnvMetadata } from '../../../../../src/stubs/env.js' +jest.unstable_mockModule('@actions/core', () => core) + const listArtifacts = await import( '../../../../../src/stubs/artifact/internal/find/list-artifacts.js' ) diff --git a/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts b/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts index 5bbc184..7af49c5 100644 --- a/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts +++ b/__tests__/stubs/artifact/internal/upload/upload-artifact.test.ts @@ -24,10 +24,10 @@ const writeStream = { on: () => writeStream } -jest.unstable_mockModule('@actions/core', () => core) jest.unstable_mockModule('crypto', () => crypto) jest.unstable_mockModule('fs', () => fs) jest.unstable_mockModule('stream/promises', () => stream) +jest.unstable_mockModule('../../../../../src/stubs/core.js', () => core) jest.unstable_mockModule( '../../../../../src/stubs/artifact/internal/upload/upload-zip-specification.js', () => ({ diff --git a/src/stubs/artifact/internal/client.ts b/src/stubs/artifact/internal/client.ts index 96717be..f50769f 100644 --- a/src/stubs/artifact/internal/client.ts +++ b/src/stubs/artifact/internal/client.ts @@ -14,7 +14,7 @@ import { } from './find/list-artifacts.js' import { isGhes } from './shared/config.js' import { GHESNotSupportedError } from './shared/errors.js' -import { +import type { DeleteArtifactResponse, DownloadArtifactOptions, DownloadArtifactResponse, diff --git a/src/stubs/artifact/internal/upload/upload-artifact.ts b/src/stubs/artifact/internal/upload/upload-artifact.ts index a259a84..1077400 100644 --- a/src/stubs/artifact/internal/upload/upload-artifact.ts +++ b/src/stubs/artifact/internal/upload/upload-artifact.ts @@ -1,9 +1,9 @@ -import * as core from '@actions/core' import crypto from 'crypto' import fs from 'fs' import path from 'path' import { finished } from 'stream/promises' import { EnvMeta } from '../../../../stubs/env.js' +import * as core from '../../../core.ts' import { FilesNotFoundError, InvalidResponseError } from '../shared/errors.js' import type { Artifact, From 56d38deb3a35c7f092d166c1dae0fceba2142834 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Tue, 7 Jan 2025 15:14:17 -0500 Subject: [PATCH 7/8] Minor version bump --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c5170b1..5286ec1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@github/local-action", - "version": "2.2.1", + "version": "2.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@github/local-action", - "version": "2.2.1", + "version": "2.3.0", "license": "MIT", "dependencies": { "@actions/artifact": "^2.2.0", diff --git a/package.json b/package.json index 57ab1b8..c887ce6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@github/local-action", "description": "Local Debugging for GitHub Actions", - "version": "2.2.1", + "version": "2.3.0", "type": "module", "author": "Nick Alteen ", "private": false, From 45c064a5554364b79eef9219d256b7dbf79ac950 Mon Sep 17 00:00:00 2001 From: Nick Alteen Date: Tue, 7 Jan 2025 15:24:49 -0500 Subject: [PATCH 8/8] Disable test coverage on step --- src/stubs/artifact/internal/shared/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stubs/artifact/internal/shared/config.ts b/src/stubs/artifact/internal/shared/config.ts index b2edd7e..34d83b5 100644 --- a/src/stubs/artifact/internal/shared/config.ts +++ b/src/stubs/artifact/internal/shared/config.ts @@ -28,5 +28,6 @@ export function isGhes(): boolean { */ export function getGitHubWorkspaceDir(): string { // Default to current working directory + /* istanbul ignore next */ return process.env['GITHUB_WORKSPACE'] || process.cwd() }