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/__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__/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/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..535adc9 --- /dev/null +++ b/__tests__/stubs/artifact/internal/delete/delete-artifact.test.ts @@ -0,0 +1,68 @@ +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' +) + +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..e6bc5bc --- /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('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' +) + +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..8c463ae --- /dev/null +++ b/__tests__/stubs/artifact/internal/find/get-artifact.test.ts @@ -0,0 +1,58 @@ +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' +) + +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..39b55ba --- /dev/null +++ b/__tests__/stubs/artifact/internal/find/list-artifacts.test.ts @@ -0,0 +1,45 @@ +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' +) + +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..7af49c5 --- /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('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', + () => ({ + 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/__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..d46f8f4 100644 --- a/__tests__/stubs/env-stubs.test.ts +++ b/__tests__/stubs/env-stubs.test.ts @@ -1,12 +1,13 @@ 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 */ 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/__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/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..5286ec1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,21 @@ { "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", "@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..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, @@ -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/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..8ce65d8 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -1,8 +1,9 @@ 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 { 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' import { printTitle } from '../utils/output.js' import { isESM } from '../utils/package.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..f50769f --- /dev/null +++ b/src/stubs/artifact/internal/client.ts @@ -0,0 +1,313 @@ +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 type { + DeleteArtifactResponse, + DownloadArtifactOptions, + DownloadArtifactResponse, + FindOptions, + GetArtifactResponse, + ListArtifactsOptions, + ListArtifactsResponse, + UploadArtifactOptions, + UploadArtifactResponse +} from './shared/interfaces.js' +import { uploadArtifact } from './upload/upload-artifact.js' + +/** + * @github/local-action Unmodified + */ +/* istanbul ignore next */ +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..d55a1ec --- /dev/null +++ b/src/stubs/artifact/internal/delete/delete-artifact.ts @@ -0,0 +1,109 @@ +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 + */ +/* istanbul ignore next */ +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..1d4d6c4 --- /dev/null +++ b/src/stubs/artifact/internal/download/download-artifact.ts @@ -0,0 +1,227 @@ +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 + */ +/* istanbul ignore next */ +const scrubQueryParameters = (url: string): string => { + const parsed = new URL(url) + parsed.search = '' + return parsed.toString() +} + +/** + * @github/local-action Unmodified + */ +/* istanbul ignore next */ +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 + */ +/* istanbul ignore next */ +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 + */ +/* istanbul ignore next */ +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 + */ +/* istanbul ignore next */ +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.` + ) + + 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) { + /* istanbul ignore next */ + throw new Error(`Unable to download and extract artifact: ${error.message}`) + } + + return { downloadPath } +} + +/** + * @github/local-action Unmodified + */ +/* istanbul ignore next */ +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..c7ce69e --- /dev/null +++ b/src/stubs/artifact/internal/find/get-artifact.ts @@ -0,0 +1,116 @@ +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 + */ +/* istanbul ignore next */ +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..edf2487 --- /dev/null +++ b/src/stubs/artifact/internal/find/list-artifacts.ts @@ -0,0 +1,148 @@ +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 type { 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 + */ +/* istanbul ignore next */ +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 + */ +/* istanbul ignore next */ +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..61f1c46 --- /dev/null +++ b/src/stubs/artifact/internal/find/retry-options.ts @@ -0,0 +1,49 @@ +/** + * @github/local-action Unmodified + */ +/* istanbul ignore file */ + +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..34d83b5 --- /dev/null +++ b/src/stubs/artifact/internal/shared/config.ts @@ -0,0 +1,33 @@ +/** + * @github/local-action Unmodified + */ +/* istanbul ignore next */ +export function getUploadChunkSize(): number { + return 8 * 1024 * 1024 // 8 MB Chunks +} + +/** + * @github/local-action Unmodified + */ +/* istanbul ignore next */ +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 + /* istanbul ignore next */ + return process.env['GITHUB_WORKSPACE'] || process.cwd() +} diff --git a/src/stubs/artifact/internal/shared/errors.ts b/src/stubs/artifact/internal/shared/errors.ts new file mode 100644 index 0000000..68e4efe --- /dev/null +++ b/src/stubs/artifact/internal/shared/errors.ts @@ -0,0 +1,42 @@ +/** + * @github/local-action Unmodified + */ +/* istanbul ignore file */ + +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' + } +} + +export class InvalidResponseError extends Error { + constructor(message: string) { + super(message) + this.name = 'InvalidResponseError' + } +} + +export class ArtifactNotFoundError extends Error { + constructor(message = 'Artifact not found') { + super(message) + this.name = 'ArtifactNotFoundError' + } +} + +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..fab338b --- /dev/null +++ b/src/stubs/artifact/internal/upload/path-and-artifact-name-validation.ts @@ -0,0 +1,87 @@ +/** + * @github/local-action Unmodified + */ +/* istanbul ignore file */ + +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..1077400 --- /dev/null +++ b/src/stubs/artifact/internal/upload/upload-artifact.ts @@ -0,0 +1,114 @@ +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, + UploadArtifactOptions, + UploadArtifactResponse +} from '../shared/interfaces.js' +import { validateArtifactName } from './path-and-artifact-name-validation.js' +import { + type 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 + ) + /* istanbul ignore next */ + 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') + + /* istanbul ignore next */ + 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' + ) + } + }) + + /* istanbul ignore next */ + 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..6cc09d4 --- /dev/null +++ b/src/stubs/artifact/internal/upload/upload-zip-specification.ts @@ -0,0 +1,125 @@ +/** + * @github/local-action Unmodified + */ +/* istanbul ignore file */ + +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..4f16eb5 --- /dev/null +++ b/src/stubs/artifact/internal/upload/zip.ts @@ -0,0 +1,112 @@ +/** + * @github/local-action Unmodified + */ +/* istanbul ignore file */ + +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/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 92% rename from src/stubs/env-stubs.ts rename to src/stubs/env.ts index c155e58..84e331f 100644 --- a/src/stubs/env-stubs.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/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/types.ts b/src/types.ts index 41bef77..68abec9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,5 @@ +import type { Artifact } from './stubs/artifact/internal/shared/interfaces.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 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. 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",