From 0a0b332b064b4787c476fc8f2ea6d245a60258e8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:34:07 +1300 Subject: [PATCH 01/28] refactor: move main --- src/{main.ts => await-remote-run.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{main.ts => await-remote-run.ts} (100%) diff --git a/src/main.ts b/src/await-remote-run.ts similarity index 100% rename from src/main.ts rename to src/await-remote-run.ts From d6a98da7c5f48fd59e6b44ed6f57473b147d57dd Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:35:54 +1300 Subject: [PATCH 02/28] refactor: run from new main --- src/await-remote-run.ts | 4 +--- src/main.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 src/main.ts diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 026605b..a085695 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -36,7 +36,7 @@ async function logFailureDetails(runId: number): Promise { } } -async function run(): Promise { +export async function run(): Promise { try { const config = getConfig(); const startTime = Date.now(); @@ -110,5 +110,3 @@ async function run(): Promise { } } } - -((): Promise => run())(); diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..93d3003 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,9 @@ +import { run } from "./await-remote-run.ts"; + +async function main(): Promise { + await run(); +} + +if (!process.env.VITEST) { + await main(); +} From 60196606beb8c6fc81db9829f301804901fa0e26 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:46:02 +1300 Subject: [PATCH 03/28] refactor: lift some prework to main --- src/await-remote-run.ts | 22 +++++++--------------- src/constants.ts | 8 ++++++++ src/main.ts | 22 +++++++++++++++++++++- 3 files changed, 36 insertions(+), 16 deletions(-) create mode 100644 src/constants.ts diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index a085695..b28cb7b 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -1,11 +1,9 @@ import * as core from "@actions/core"; -import { getConfig } from "./action.ts"; +import { type ActionConfig } from "./action.ts"; import { - getWorkflowRunActiveJobUrlRetry, getWorkflowRunFailedJobs, getWorkflowRunState, - init, retryOnError, WorkflowRunConclusion, WorkflowRunStatus, @@ -36,22 +34,16 @@ async function logFailureDetails(runId: number): Promise { } } -export async function run(): Promise { +interface RunOpts { + config: ActionConfig; + startTime: number; +} +export async function run({ config, startTime }: RunOpts): Promise { try { - const config = getConfig(); - const startTime = Date.now(); - init(config); - const timeoutMs = config.runTimeoutSeconds * 1000; + let attemptNo = 0; let elapsedTime = Date.now() - startTime; - - core.info( - `Awaiting completion of Workflow Run ${config.runId}...\n` + - ` ID: ${config.runId}\n` + - ` URL: ${await getWorkflowRunActiveJobUrlRetry(config.runId, 1000)}`, - ); - while (elapsedTime < timeoutMs) { attemptNo++; elapsedTime = Date.now() - startTime; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..3a5b09a --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,8 @@ +/* eslint-disable @typescript-eslint/no-inferrable-types */ + +export const WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS: number = 1000; + +// export const WORKFLOW_FETCH_TIMEOUT_MS: number = 60 * 1000; +// export const WORKFLOW_JOB_STEPS_RETRY_MS: number = 5000; +// export const WORKFLOW_JOB_STEPS_SERVER_ERROR_RETRY_MAX: number = 3; +// export const WORKFLOW_JOB_STEPS_SERVER_ERROR_RETRY_MS: number = 500; diff --git a/src/main.ts b/src/main.ts index 93d3003..641e6d5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,27 @@ +import * as core from "@actions/core"; + +import { getConfig } from "./action.ts"; +import * as api from "./api.ts"; import { run } from "./await-remote-run.ts"; +import * as constants from "./constants.ts"; async function main(): Promise { - await run(); + const startTime = Date.now(); + + const config = getConfig(); + api.init(config); + + const activeJobUrl = await api.getWorkflowRunActiveJobUrlRetry( + config.runId, + constants.WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS, + ); + core.info( + `Awaiting completion of Workflow Run ${config.runId}...\n` + + ` ID: ${config.runId}\n` + + ` URL: ${activeJobUrl}`, + ); + + await run({ config, startTime }); } if (!process.env.VITEST) { From 1d83f75808fde6e7bf6b782e1501388b295c16df Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:50:55 +1300 Subject: [PATCH 04/28] refactor: rename api gets to fetches --- src/api.spec.ts | 50 ++++++++++++++++++++--------------------- src/api.ts | 26 ++++++++++----------- src/await-remote-run.ts | 10 ++++----- src/main.ts | 2 +- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/api.spec.ts b/src/api.spec.ts index 6755130..6b66bcc 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -11,10 +11,10 @@ import { } from "vitest"; import { - getWorkflowRunActiveJobUrl, - getWorkflowRunActiveJobUrlRetry, - getWorkflowRunFailedJobs, - getWorkflowRunState, + fetchWorkflowRunActiveJobUrl, + fetchWorkflowRunActiveJobUrlRetry, + fetchWorkflowRunFailedJobs, + fetchWorkflowRunState, init, retryOnError, } from "./api.ts"; @@ -62,7 +62,7 @@ describe("API", () => { vi.restoreAllMocks(); }); - describe("getWorkflowRunState", () => { + describe("fetchWorkflowRunState", () => { it("should return the workflow run state for a given run ID", async () => { const mockData = { status: "completed", @@ -75,7 +75,7 @@ describe("API", () => { }), ); - const state = await getWorkflowRunState(123456); + const state = await fetchWorkflowRunState(123456); expect(state.conclusion).toStrictEqual(mockData.conclusion); expect(state.status).toStrictEqual(mockData.status); }); @@ -89,13 +89,13 @@ describe("API", () => { }), ); - await expect(getWorkflowRunState(0)).rejects.toThrow( - `Failed to get Workflow Run state, expected 200 but received ${errorStatus}`, + await expect(fetchWorkflowRunState(0)).rejects.toThrow( + `Failed to fetch Workflow Run state, expected 200 but received ${errorStatus}`, ); }); }); - describe("getWorkflowRunJobs", () => { + describe("fetchWorkflowRunJobs", () => { const mockData = { total_count: 1, jobs: [ @@ -123,7 +123,7 @@ describe("API", () => { ], }; - describe("getWorkflowRunFailedJobs", () => { + describe("fetchWorkflowRunFailedJobs", () => { it("should return the jobs for a failed workflow run given a run ID", async () => { vi.spyOn( mockOctokit.rest.actions, @@ -135,7 +135,7 @@ describe("API", () => { }), ); - const jobs = await getWorkflowRunFailedJobs(123456); + const jobs = await fetchWorkflowRunFailedJobs(123456); expect(jobs).toHaveLength(1); expect(jobs[0]?.id).toStrictEqual(mockData.jobs[0]?.id); expect(jobs[0]?.name).toStrictEqual(mockData.jobs[0]?.name); @@ -157,8 +157,8 @@ describe("API", () => { }), ); - await expect(getWorkflowRunFailedJobs(0)).rejects.toThrow( - `Failed to get Jobs for Workflow Run, expected 200 but received ${errorStatus}`, + await expect(fetchWorkflowRunFailedJobs(0)).rejects.toThrow( + `Failed to fetch Jobs for Workflow Run, expected 200 but received ${errorStatus}`, ); }); @@ -174,7 +174,7 @@ describe("API", () => { }), ); - const { steps } = (await getWorkflowRunFailedJobs(123456))[0]!; + const { steps } = (await fetchWorkflowRunFailedJobs(123456))[0]!; expect(steps).toHaveLength(mockData.jobs[0]!.steps.length); for (let i = 0; i < mockSteps.length; i++) { expect(steps[i]?.name).toStrictEqual(mockSteps[i]?.name); @@ -185,7 +185,7 @@ describe("API", () => { }); }); - describe("getWorkflowRunActiveJobUrl", () => { + describe("fetchWorkflowRunActiveJobUrl", () => { let inProgressMockData: any; beforeEach(() => { @@ -212,7 +212,7 @@ describe("API", () => { }), ); - const url = await getWorkflowRunActiveJobUrl(123456); + const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(mockData.jobs[0]?.html_url); }); @@ -229,7 +229,7 @@ describe("API", () => { }), ); - const url = await getWorkflowRunActiveJobUrl(123456); + const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(mockData.jobs[0]?.html_url); }); @@ -245,8 +245,8 @@ describe("API", () => { }), ); - await expect(getWorkflowRunActiveJobUrl(0)).rejects.toThrow( - `Failed to get Jobs for Workflow Run, expected 200 but received ${errorStatus}`, + await expect(fetchWorkflowRunActiveJobUrl(0)).rejects.toThrow( + `Failed to fetch Jobs for Workflow Run, expected 200 but received ${errorStatus}`, ); }); @@ -263,7 +263,7 @@ describe("API", () => { }), ); - const url = await getWorkflowRunActiveJobUrl(123456); + const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(undefined); }); @@ -280,11 +280,11 @@ describe("API", () => { }), ); - const url = await getWorkflowRunActiveJobUrl(123456); + const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual("GitHub failed to return the URL"); }); - describe("getWorkflowRunActiveJobUrlRetry", () => { + describe("fetchWorkflowRunActiveJobUrlRetry", () => { beforeEach(() => { vi.useFakeTimers(); }); @@ -306,7 +306,7 @@ describe("API", () => { }), ); - const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 100); + const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 100); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); @@ -342,7 +342,7 @@ describe("API", () => { }), ); - const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 200); + const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 200); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); @@ -361,7 +361,7 @@ describe("API", () => { }), ); - const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 200); + const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 200); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); diff --git a/src/api.ts b/src/api.ts index faf1b69..5d17105 100644 --- a/src/api.ts +++ b/src/api.ts @@ -34,7 +34,7 @@ export interface WorkflowRunState { conclusion: WorkflowRunConclusion | null; } -export async function getWorkflowRunState( +export async function fetchWorkflowRunState( runId: number, ): Promise { try { @@ -48,7 +48,7 @@ export async function getWorkflowRunState( // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (response.status !== 200) { throw new Error( - `Failed to get Workflow Run state, expected 200 but received ${response.status}`, + `Failed to fetch Workflow Run state, expected 200 but received ${response.status}`, ); } @@ -67,7 +67,7 @@ export async function getWorkflowRunState( } catch (error) { if (error instanceof Error) { core.error( - `getWorkflowRunState: An unexpected error has occurred: ${error.message}`, + `fetchWorkflowRunState: An unexpected error has occurred: ${error.message}`, ); // eslint-disable-next-line @typescript-eslint/no-unused-expressions error.stack && core.debug(error.stack); @@ -97,7 +97,7 @@ type ListJobsForWorkflowRunResponse = Awaited< ReturnType >; -async function getWorkflowRunJobs( +async function fetchWorkflowRunJobs( runId: number, ): Promise { // https://docs.github.com/en/rest/reference/actions#list-jobs-for-a-workflow-run @@ -111,18 +111,18 @@ async function getWorkflowRunJobs( // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (response.status !== 200) { throw new Error( - `Failed to get Jobs for Workflow Run, expected 200 but received ${response.status}`, + `Failed to fetch Jobs for Workflow Run, expected 200 but received ${response.status}`, ); } return response; } -export async function getWorkflowRunFailedJobs( +export async function fetchWorkflowRunFailedJobs( runId: number, ): Promise { try { - const response = await getWorkflowRunJobs(runId); + const response = await fetchWorkflowRunJobs(runId); const fetchedFailedJobs = response.data.jobs.filter( (job) => job.conclusion === "failure", ); @@ -173,7 +173,7 @@ export async function getWorkflowRunFailedJobs( } catch (error) { if (error instanceof Error) { core.error( - `getWorkflowRunJobFailures: An unexpected error has occurred: ${error.message}`, + `fetchWorkflowRunFailedJobs: An unexpected error has occurred: ${error.message}`, ); // eslint-disable-next-line @typescript-eslint/no-unused-expressions error.stack && core.debug(error.stack); @@ -182,11 +182,11 @@ export async function getWorkflowRunFailedJobs( } } -export async function getWorkflowRunActiveJobUrl( +export async function fetchWorkflowRunActiveJobUrl( runId: number, ): Promise { try { - const response = await getWorkflowRunJobs(runId); + const response = await fetchWorkflowRunJobs(runId); const fetchedInProgressJobs = response.data.jobs.filter( (job) => job.status === "in_progress" || job.status === "completed", ); @@ -211,7 +211,7 @@ export async function getWorkflowRunActiveJobUrl( } catch (error) { if (error instanceof Error) { core.error( - `getWorkflowRunActiveJobUrl: An unexpected error has occurred: ${error.message}`, + `fetchWorkflowRunActiveJobUrl: An unexpected error has occurred: ${error.message}`, ); // eslint-disable-next-line @typescript-eslint/no-unused-expressions error.stack && core.debug(error.stack); @@ -220,7 +220,7 @@ export async function getWorkflowRunActiveJobUrl( } } -export async function getWorkflowRunActiveJobUrlRetry( +export async function fetchWorkflowRunActiveJobUrlRetry( runId: number, timeout: number, ): Promise { @@ -233,7 +233,7 @@ export async function getWorkflowRunActiveJobUrlRetry( `No 'in_progress' or 'completed' Jobs found for Workflow Run ${runId}, retrying...`, ); - const url = await getWorkflowRunActiveJobUrl(runId); + const url = await fetchWorkflowRunActiveJobUrl(runId); if (url) { return url; } diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index b28cb7b..478c743 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -2,15 +2,15 @@ import * as core from "@actions/core"; import { type ActionConfig } from "./action.ts"; import { - getWorkflowRunFailedJobs, - getWorkflowRunState, + fetchWorkflowRunFailedJobs, + fetchWorkflowRunState, retryOnError, WorkflowRunConclusion, WorkflowRunStatus, } from "./api.ts"; async function logFailureDetails(runId: number): Promise { - const failedJobs = await getWorkflowRunFailedJobs(runId); + const failedJobs = await fetchWorkflowRunFailedJobs(runId); for (const failedJob of failedJobs) { const failedSteps = failedJob.steps .filter((step) => step.conclusion !== "success") @@ -49,8 +49,8 @@ export async function run({ config, startTime }: RunOpts): Promise { elapsedTime = Date.now() - startTime; const { status, conclusion } = await retryOnError( - async () => getWorkflowRunState(config.runId), - "getWorkflowRunState", + async () => fetchWorkflowRunState(config.runId), + "fetchWorkflowRunState", 400, ); diff --git a/src/main.ts b/src/main.ts index 641e6d5..059ac19 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,7 +11,7 @@ async function main(): Promise { const config = getConfig(); api.init(config); - const activeJobUrl = await api.getWorkflowRunActiveJobUrlRetry( + const activeJobUrl = await api.fetchWorkflowRunActiveJobUrlRetry( config.runId, constants.WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS, ); From 4fa54c88507c6710fb37f961377a0b95db4d0f1a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:54:52 +1300 Subject: [PATCH 05/28] chore: resolve some lint warnings --- src/api.ts | 13 +++++-------- src/await-remote-run.ts | 3 +-- src/constants.ts | 5 ----- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/api.ts b/src/api.ts index 5d17105..3238387 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-unnecessary-condition */ + import * as core from "@actions/core"; import * as github from "@actions/github"; @@ -45,7 +47,6 @@ export async function fetchWorkflowRunState( run_id: runId, }); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (response.status !== 200) { throw new Error( `Failed to fetch Workflow Run state, expected 200 but received ${response.status}`, @@ -69,8 +70,7 @@ export async function fetchWorkflowRunState( core.error( `fetchWorkflowRunState: An unexpected error has occurred: ${error.message}`, ); - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - error.stack && core.debug(error.stack); + core.debug(error.stack ?? ""); } throw error; } @@ -108,7 +108,6 @@ async function fetchWorkflowRunJobs( filter: "latest", }); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (response.status !== 200) { throw new Error( `Failed to fetch Jobs for Workflow Run, expected 200 but received ${response.status}`, @@ -175,8 +174,7 @@ export async function fetchWorkflowRunFailedJobs( core.error( `fetchWorkflowRunFailedJobs: An unexpected error has occurred: ${error.message}`, ); - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - error.stack && core.debug(error.stack); + core.debug(error.stack ?? ""); } throw error; } @@ -213,8 +211,7 @@ export async function fetchWorkflowRunActiveJobUrl( core.error( `fetchWorkflowRunActiveJobUrl: An unexpected error has occurred: ${error.message}`, ); - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - error.stack && core.debug(error.stack); + core.debug(error.stack ?? ""); } throw error; } diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 478c743..081d22d 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -96,8 +96,7 @@ export async function run({ config, startTime }: RunOpts): Promise { if (!error.message.includes("Timeout")) { core.warning("Does the token have the correct permissions?"); } - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - error.stack && core.debug(error.stack); + core.debug(error.stack ?? ""); core.setFailed(error.message); } } diff --git a/src/constants.ts b/src/constants.ts index 3a5b09a..0a1f44c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,8 +1,3 @@ /* eslint-disable @typescript-eslint/no-inferrable-types */ export const WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS: number = 1000; - -// export const WORKFLOW_FETCH_TIMEOUT_MS: number = 60 * 1000; -// export const WORKFLOW_JOB_STEPS_RETRY_MS: number = 5000; -// export const WORKFLOW_JOB_STEPS_SERVER_ERROR_RETRY_MAX: number = 3; -// export const WORKFLOW_JOB_STEPS_SERVER_ERROR_RETRY_MS: number = 500; From 9a524c993421927f286d80252405327617974529 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:56:20 +1300 Subject: [PATCH 06/28] refactor: lift wrapping try/catch to main --- src/await-remote-run.ts | 95 ++++++++++++++++++----------------------- src/main.ts | 37 ++++++++++------ 2 files changed, 65 insertions(+), 67 deletions(-) diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 081d22d..949008e 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -39,65 +39,52 @@ interface RunOpts { startTime: number; } export async function run({ config, startTime }: RunOpts): Promise { - try { - const timeoutMs = config.runTimeoutSeconds * 1000; + const timeoutMs = config.runTimeoutSeconds * 1000; - let attemptNo = 0; - let elapsedTime = Date.now() - startTime; - while (elapsedTime < timeoutMs) { - attemptNo++; - elapsedTime = Date.now() - startTime; + let attemptNo = 0; + let elapsedTime = Date.now() - startTime; + while (elapsedTime < timeoutMs) { + attemptNo++; + elapsedTime = Date.now() - startTime; - const { status, conclusion } = await retryOnError( - async () => fetchWorkflowRunState(config.runId), - "fetchWorkflowRunState", - 400, - ); + const { status, conclusion } = await retryOnError( + async () => fetchWorkflowRunState(config.runId), + "fetchWorkflowRunState", + 400, + ); - if (status === WorkflowRunStatus.Completed) { - switch (conclusion) { - case WorkflowRunConclusion.Success: - core.info( - "Run Completed:\n" + - ` Run ID: ${config.runId}\n` + - ` Status: ${status}\n` + - ` Conclusion: ${conclusion}`, - ); - return; - case WorkflowRunConclusion.ActionRequired: - case WorkflowRunConclusion.Cancelled: - case WorkflowRunConclusion.Failure: - case WorkflowRunConclusion.Neutral: - case WorkflowRunConclusion.Skipped: - case WorkflowRunConclusion.TimedOut: - core.error(`Run has failed with conclusion: ${conclusion}`); - await logFailureDetails(config.runId); - core.setFailed(conclusion); - return; - default: - core.setFailed(`Unknown conclusion: ${conclusion}`); - return; - } + if (status === WorkflowRunStatus.Completed) { + switch (conclusion) { + case WorkflowRunConclusion.Success: + core.info( + "Run Completed:\n" + + ` Run ID: ${config.runId}\n` + + ` Status: ${status}\n` + + ` Conclusion: ${conclusion}`, + ); + return; + case WorkflowRunConclusion.ActionRequired: + case WorkflowRunConclusion.Cancelled: + case WorkflowRunConclusion.Failure: + case WorkflowRunConclusion.Neutral: + case WorkflowRunConclusion.Skipped: + case WorkflowRunConclusion.TimedOut: + core.error(`Run has failed with conclusion: ${conclusion}`); + await logFailureDetails(config.runId); + core.setFailed(conclusion); + return; + default: + core.setFailed(`Unknown conclusion: ${conclusion}`); + return; } - - core.debug(`Run has not concluded, attempt ${attemptNo}...`); - - await new Promise((resolve) => - setTimeout(resolve, config.pollIntervalMs), - ); } - throw new Error( - `Timeout exceeded while awaiting completion of Run ${config.runId}`, - ); - } catch (error) { - if (error instanceof Error) { - core.error(`Failed to complete: ${error.message}`); - if (!error.message.includes("Timeout")) { - core.warning("Does the token have the correct permissions?"); - } - core.debug(error.stack ?? ""); - core.setFailed(error.message); - } + core.debug(`Run has not concluded, attempt ${attemptNo}...`); + + await new Promise((resolve) => setTimeout(resolve, config.pollIntervalMs)); } + + throw new Error( + `Timeout exceeded while awaiting completion of Run ${config.runId}`, + ); } diff --git a/src/main.ts b/src/main.ts index 059ac19..ed35047 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,22 +6,33 @@ import { run } from "./await-remote-run.ts"; import * as constants from "./constants.ts"; async function main(): Promise { - const startTime = Date.now(); + try { + const startTime = Date.now(); - const config = getConfig(); - api.init(config); + const config = getConfig(); + api.init(config); - const activeJobUrl = await api.fetchWorkflowRunActiveJobUrlRetry( - config.runId, - constants.WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS, - ); - core.info( - `Awaiting completion of Workflow Run ${config.runId}...\n` + - ` ID: ${config.runId}\n` + - ` URL: ${activeJobUrl}`, - ); + const activeJobUrl = await api.fetchWorkflowRunActiveJobUrlRetry( + config.runId, + constants.WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS, + ); + core.info( + `Awaiting completion of Workflow Run ${config.runId}...\n` + + ` ID: ${config.runId}\n` + + ` URL: ${activeJobUrl}`, + ); - await run({ config, startTime }); + await run({ config, startTime }); + } catch (error) { + if (error instanceof Error) { + core.error(`Failed to complete: ${error.message}`); + if (!error.message.includes("Timeout")) { + core.warning("Does the token have the correct permissions?"); + } + core.debug(error.stack ?? ""); + core.setFailed(error.message); + } + } } if (!process.env.VITEST) { From e6fc07f81de8d43d7c532f3d8ae88b82f5ed7a49 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:59:11 +1300 Subject: [PATCH 07/28] refactor: add ts-reset # Conflicts: # package.json # pnpm-lock.yaml # Conflicts: # package.json # pnpm-lock.yaml # Conflicts: # package.json # pnpm-lock.yaml # Conflicts: # pnpm-lock.yaml # Conflicts: # package.json # pnpm-lock.yaml --- package.json | 1 + pnpm-lock.yaml | 8 ++++++++ src/reset.d.ts | 2 ++ tsconfig.json | 2 +- 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 src/reset.d.ts diff --git a/package.json b/package.json index 55488f5..0f63972 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@eslint/compat": "^1.2.8", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.25.1", + "@total-typescript/ts-reset": "^0.6.1", "@types/eslint__js": "^8.42.3", "@types/node": "^20.17.32", "@typescript-eslint/eslint-plugin": "^8.31.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6891b5f..9f1b6f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,9 @@ importers: '@eslint/js': specifier: ^9.25.1 version: 9.25.1 + '@total-typescript/ts-reset': + specifier: ^0.6.1 + version: 0.6.1 '@types/eslint__js': specifier: ^8.42.3 version: 8.42.3 @@ -565,6 +568,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@total-typescript/ts-reset@0.6.1': + resolution: {integrity: sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==} + '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} @@ -2775,6 +2781,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@total-typescript/ts-reset@0.6.1': {} + '@tybys/wasm-util@0.9.0': dependencies: tslib: 2.8.1 diff --git a/src/reset.d.ts b/src/reset.d.ts new file mode 100644 index 0000000..e4d600c --- /dev/null +++ b/src/reset.d.ts @@ -0,0 +1,2 @@ +// Do not add any other lines of code to this file! +import "@total-typescript/ts-reset"; diff --git a/tsconfig.json b/tsconfig.json index 5c79be3..7af44a1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es2021", + "target": "es2023", "module": "nodenext", "noEmit": true, From bbff48f64e24e1565a0b38a230cc3c5df7e027d7 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:59:23 +1300 Subject: [PATCH 08/28] fix: prevent createRequire clashes --- esbuild.config.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 7c34029..a195b8d 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -20,8 +20,8 @@ import { analyzeMetafile, build } from "esbuild"; // Ensure require is properly defined: https://github.com/evanw/esbuild/issues/1921 banner: { js: - "import { createRequire } from 'module';\n" + - "const require = createRequire(import.meta.url);", + "import { createRequire as __await_remote_run_cr } from 'node:module';\n" + + "const require = __await_remote_run_cr(import.meta.url);", }, }); From 5ee0821a0577460178e30a5cb0d94837cb82fff5 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 6 Oct 2024 17:03:48 +1300 Subject: [PATCH 09/28] refactor: start using result types for retryOnError --- package.json | 1 + pnpm-lock.yaml | 9 +++ src/api.spec.ts | 112 ++++++++++++++++++++++++--------- src/api.ts | 37 ++++++----- src/await-remote-run.ts | 60 +++++++++--------- src/test-utils/logging.mock.ts | 90 ++++++++++++++++++++++++++ src/types.ts | 16 +++++ src/utils.ts | 3 + 8 files changed, 256 insertions(+), 72 deletions(-) create mode 100644 src/test-utils/logging.mock.ts create mode 100644 src/types.ts create mode 100644 src/utils.ts diff --git a/package.json b/package.json index 0f63972..838b848 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@eslint/compat": "^1.2.8", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.25.1", + "@opentf/std": "^0.13.0", "@total-typescript/ts-reset": "^0.6.1", "@types/eslint__js": "^8.42.3", "@types/node": "^20.17.32", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f1b6f4..9c933db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,9 @@ importers: '@eslint/js': specifier: ^9.25.1 version: 9.25.1 + '@opentf/std': + specifier: ^0.13.0 + version: 0.13.0 '@total-typescript/ts-reset': specifier: ^0.6.1 version: 0.6.1 @@ -450,6 +453,10 @@ packages: '@octokit/types@13.10.0': resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} + '@opentf/std@0.13.0': + resolution: {integrity: sha512-VG9vn7oML5prxWipDvod1X7z9+3fyyCbw+SuD5F7cWx9F1bXFZdAYGIKGqqHLtfxz3mrXZWHcxnm8d0YwQ7tKQ==} + engines: {node: '>=16.20.2'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2710,6 +2717,8 @@ snapshots: dependencies: '@octokit/openapi-types': 24.2.0 + '@opentf/std@0.13.0': {} + '@pkgjs/parseargs@0.11.0': optional: true diff --git a/src/api.spec.ts b/src/api.spec.ts index 6b66bcc..108430b 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -1,13 +1,13 @@ import * as core from "@actions/core"; import * as github from "@actions/github"; import { + afterAll, afterEach, beforeEach, describe, expect, it, vi, - type MockInstance, } from "vitest"; import { @@ -18,6 +18,7 @@ import { init, retryOnError, } from "./api.ts"; +import { mockLoggingFunctions } from "./test-utils/logging.mock.ts"; vi.mock("@actions/core"); vi.mock("@actions/github"); @@ -51,6 +52,13 @@ describe("API", () => { pollIntervalMs: 2500, }; + const { coreWarningLogMock, assertOnlyCalled, assertNoneCalled } = + mockLoggingFunctions(); + + afterAll(() => { + vi.restoreAllMocks(); + }); + beforeEach(() => { vi.spyOn(core, "getInput").mockReturnValue(""); // eslint-disable-next-line @typescript-eslint/no-unsafe-argument @@ -59,7 +67,7 @@ describe("API", () => { }); afterEach(() => { - vi.restoreAllMocks(); + vi.resetAllMocks(); }); describe("fetchWorkflowRunState", () => { @@ -373,50 +381,92 @@ describe("API", () => { }); describe("retryOnError", () => { - let warningLogSpy: MockInstance; - beforeEach(() => { vi.useFakeTimers(); - warningLogSpy = vi.spyOn(core, "warning"); }); afterEach(() => { vi.useRealTimers(); - warningLogSpy.mockRestore(); + }); + + it("should return a success result", async () => { + const testFunc = vi + .fn<() => Promise>() + .mockImplementation(() => Promise.resolve("completed")); + + const result = await retryOnError(() => testFunc(), 5000); + + if (!result.success) { + expect.fail(); + } + + expect(result.success).toStrictEqual(true); + expect(result.value).toStrictEqual("completed"); + assertNoneCalled(); }); it("should retry a function if it throws an error", async () => { - const funcName = "testFunc"; const errorMsg = "some error"; const testFunc = vi .fn<() => Promise>() .mockImplementation(() => Promise.resolve("completed")) .mockImplementationOnce(() => Promise.reject(Error(errorMsg))); - const retryPromise = retryOnError(() => testFunc(), funcName); + const retryPromise = retryOnError(testFunc, 5000); // Progress timers to first failure - vi.advanceTimersByTime(500); - await vi.advanceTimersByTimeAsync(500); - - expect(warningLogSpy).toHaveBeenCalledOnce(); - expect(warningLogSpy).toHaveBeenCalledWith( - "retryOnError: An unexpected error has occurred:\n" + - ` name: ${funcName}\n` + - ` error: ${errorMsg}`, - ); + await vi.advanceTimersByTimeAsync(1000); + + assertOnlyCalled(coreWarningLogMock); + expect(coreWarningLogMock).toHaveBeenCalledOnce(); + expect(coreWarningLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` + "retryOnError: An unexpected error has occurred: + name: spy + error: some error" + `); + expect(coreWarningLogMock.mock.calls[0]?.[0]).toContain(testFunc.name); + coreWarningLogMock.mockReset(); // Progress timers to second success - vi.advanceTimersByTime(500); - await vi.advanceTimersByTimeAsync(500); + await vi.advanceTimersByTimeAsync(1000); + const result = await retryPromise; - expect(warningLogSpy).toHaveBeenCalledOnce(); - expect(result).toStrictEqual("completed"); + if (!result.success) { + expect.fail(); + } + + assertNoneCalled(); + expect(result.success).toStrictEqual(true); + expect(result.value).toStrictEqual("completed"); + }); + + it("should display a fallback function name if none is available", async () => { + const errorMsg = "some error"; + const testFunc = vi + .fn<() => Promise>() + .mockImplementationOnce(() => Promise.reject(Error(errorMsg))); + + // Use anonymous function + const retryPromise = retryOnError(() => testFunc(), 5000); + + // Progress timers to first failure + await vi.advanceTimersByTimeAsync(1000); + + assertOnlyCalled(coreWarningLogMock); + expect(coreWarningLogMock).toHaveBeenCalledOnce(); + expect(coreWarningLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` + "retryOnError: An unexpected error has occurred: + name: anonymous function + error: some error" + `); + coreWarningLogMock.mockReset(); + + // Clean up promise + await retryPromise; }); - it("should throw the original error if timed out while calling the function", async () => { - const funcName = "testFunc"; + it("should return a timeout result", async () => { const errorMsg = "some error"; const testFunc = vi .fn<() => Promise>() @@ -425,13 +475,19 @@ describe("API", () => { throw new Error(errorMsg); }); - const retryPromise = retryOnError(() => testFunc(), funcName, 500); + const retryPromise = retryOnError(() => testFunc(), 500); + + await vi.advanceTimersByTimeAsync(2000); + + const result = await retryPromise; - vi.advanceTimersByTime(500); - // eslint-disable-next-line @typescript-eslint/no-floating-promises - vi.advanceTimersByTimeAsync(500); + if (result.success) { + expect.fail(); + } - await expect(retryPromise).rejects.toThrowError("some error"); + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("timeout"); + assertNoneCalled(); }); }); }); diff --git a/src/api.ts b/src/api.ts index 3238387..8c2984c 100644 --- a/src/api.ts +++ b/src/api.ts @@ -4,6 +4,8 @@ import * as core from "@actions/core"; import * as github from "@actions/github"; import { type ActionConfig, getConfig } from "./action.ts"; +import type { Result } from "./types.ts"; +import { sleep } from "./utils.ts"; type Octokit = ReturnType<(typeof github)["getOctokit"]>; @@ -244,32 +246,35 @@ export async function fetchWorkflowRunActiveJobUrlRetry( export async function retryOnError( func: () => Promise, - name: string, - timeout = 5000, -): Promise { + timeoutMs: number, + functionName?: string, +): Promise> { const startTime = Date.now(); - let elapsedTime = Date.now() - startTime; - while (elapsedTime < timeout) { - elapsedTime = Date.now() - startTime; + let elapsedTime = 0; + while (elapsedTime < timeoutMs) { try { - return await func(); + const value = await func(); + return { + success: true, + value: value, + }; } catch (error) { - if (error instanceof Error) { - // We now exceed the time, so throw the error up - if (Date.now() - startTime >= timeout) { - throw error; - } - + if (error instanceof Error && Date.now() - startTime < timeoutMs) { core.warning( "retryOnError: An unexpected error has occurred:\n" + - ` name: ${name}\n` + + ` name: ${functionName ?? (func.name || "anonymous function")}\n` + ` error: ${error.message}`, ); } - await new Promise((resolve) => setTimeout(resolve, 1000)); } + + await sleep(1000); + elapsedTime = Date.now() - startTime; } - throw new Error(`Timeout exceeded while attempting to retry ${name}`); + return { + success: false, + reason: "timeout", + }; } diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 949008e..0010cc2 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -8,6 +8,7 @@ import { WorkflowRunConclusion, WorkflowRunStatus, } from "./api.ts"; +import { sleep } from "./utils.ts"; async function logFailureDetails(runId: number): Promise { const failedJobs = await fetchWorkflowRunFailedJobs(runId); @@ -47,41 +48,44 @@ export async function run({ config, startTime }: RunOpts): Promise { attemptNo++; elapsedTime = Date.now() - startTime; - const { status, conclusion } = await retryOnError( + const fetchWorkflowRunStateResult = await retryOnError( async () => fetchWorkflowRunState(config.runId), - "fetchWorkflowRunState", 400, + "fetchWorkflowRunState", ); + if (fetchWorkflowRunStateResult.success) { + const { status, conclusion } = fetchWorkflowRunStateResult.value; - if (status === WorkflowRunStatus.Completed) { - switch (conclusion) { - case WorkflowRunConclusion.Success: - core.info( - "Run Completed:\n" + - ` Run ID: ${config.runId}\n` + - ` Status: ${status}\n` + - ` Conclusion: ${conclusion}`, - ); - return; - case WorkflowRunConclusion.ActionRequired: - case WorkflowRunConclusion.Cancelled: - case WorkflowRunConclusion.Failure: - case WorkflowRunConclusion.Neutral: - case WorkflowRunConclusion.Skipped: - case WorkflowRunConclusion.TimedOut: - core.error(`Run has failed with conclusion: ${conclusion}`); - await logFailureDetails(config.runId); - core.setFailed(conclusion); - return; - default: - core.setFailed(`Unknown conclusion: ${conclusion}`); - return; + if (status === WorkflowRunStatus.Completed) { + switch (conclusion) { + case WorkflowRunConclusion.Success: + core.info( + "Run Completed:\n" + + ` Run ID: ${config.runId}\n` + + ` Status: ${status}\n` + + ` Conclusion: ${conclusion}`, + ); + return; + case WorkflowRunConclusion.ActionRequired: + case WorkflowRunConclusion.Cancelled: + case WorkflowRunConclusion.Failure: + case WorkflowRunConclusion.Neutral: + case WorkflowRunConclusion.Skipped: + case WorkflowRunConclusion.TimedOut: + core.error(`Run has failed with conclusion: ${conclusion}`); + await logFailureDetails(config.runId); + core.setFailed(conclusion); + return; + default: + core.setFailed(`Unknown conclusion: ${conclusion}`); + return; + } } + } else { + core.debug(`Run has not concluded, attempt ${attemptNo}...`); } - core.debug(`Run has not concluded, attempt ${attemptNo}...`); - - await new Promise((resolve) => setTimeout(resolve, config.pollIntervalMs)); + await sleep(config.pollIntervalMs); } throw new Error( diff --git a/src/test-utils/logging.mock.ts b/src/test-utils/logging.mock.ts new file mode 100644 index 0000000..26f60bb --- /dev/null +++ b/src/test-utils/logging.mock.ts @@ -0,0 +1,90 @@ +import * as core from "@actions/core"; +import { symDiff } from "@opentf/std"; +import { type MockInstance, vi, expect } from "vitest"; + +// Consuming test suites must first call: +// vi.mock("@actions/core"); + +interface MockedLoggingFunctions { + coreDebugLogMock: MockInstance<(message: string) => void>; + coreInfoLogMock: MockInstance<(message: string) => void>; + coreWarningLogMock: MockInstance<(message: string) => void>; + coreErrorLogMock: MockInstance<(message: string) => void>; + assertOnlyCalled: ( + ...coreLogMocks: MockInstance<(message: string) => void>[] + ) => void; + assertNoneCalled: () => void; +} + +export function mockLoggingFunctions(): MockedLoggingFunctions { + const coreDebugLogMock: MockInstance = vi + .spyOn(core, "debug") + .mockImplementation(() => undefined); + const coreInfoLogMock: MockInstance = vi + .spyOn(core, "info") + .mockImplementation(() => undefined); + const coreWarningLogMock: MockInstance = vi.spyOn( + core, + "warning", + ); + const coreErrorLogMock: MockInstance = vi + .spyOn(core, "error") + .mockImplementation(() => undefined); + + const coreLogMockSet = new Set void>>([ + coreDebugLogMock, + coreInfoLogMock, + coreWarningLogMock, + coreErrorLogMock, + ]); + const assertOnlyCalled = ( + ...coreLogMocks: MockInstance<(message: string) => void>[] + ): void => { + assertOnlyCalledInner(coreLogMockSet, ...coreLogMocks); + }; + + const assertNoneCalled = (): void => { + assertNoneCalledInner(coreLogMockSet); + }; + + return { + coreDebugLogMock, + coreInfoLogMock, + coreWarningLogMock, + coreErrorLogMock, + assertOnlyCalled, + assertNoneCalled, + }; +} + +/** + * Explicitly assert no rogue log calls are made + * that are not correctly asserted in these tests + */ +function assertOnlyCalledInner( + coreLogMockSet: Set void>>, + ...coreLogMocks: MockInstance<(message: string) => void>[] +): void { + if (coreLogMocks.length <= 0) { + throw new Error( + "assertOnlyCalled must be called with at least one mock to assert", + ); + } + + // Once Node 22 is LTS, this can be: + // const diff = coreLogMockSet.symmetricDifference(new Set(coreLogMocks)); + + const diff = symDiff([[...coreLogMockSet], coreLogMocks]); + + for (const logMock of diff) { + expect(logMock).not.toHaveBeenCalled(); + } +} + +function assertNoneCalledInner( + coreLogMockSet: Set void>>, +): void { + for (const logMock of coreLogMockSet) { + expect(logMock).not.toHaveBeenCalled(); + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..c863d05 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,16 @@ +export type Result = ResultSuccess | ResultTimeout | ResultInvalidInput; + +interface ResultSuccess { + success: true; + value: T; +} + +interface ResultTimeout { + success: false; + reason: "timeout"; +} + +interface ResultInvalidInput { + success: false; + reason: "invalid input"; +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..379472d --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,3 @@ +export function sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} From e6b436bee28569a92ea70f97679de96eb078f2e7 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 7 Oct 2024 11:28:11 +1300 Subject: [PATCH 10/28] docs: update api doc links --- README.md | 4 ++-- src/api.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 49e0fe4..6795b1b 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,12 @@ The permissions required for this action to function correctly are: For the sake of transparency please note that this action uses the following API calls: -- [Get a workflow run](https://docs.github.com/en/rest/reference/actions#get-a-workflow-run) +- [Get a workflow run](https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run) - GET `/repos/{owner}/{repo}/actions/runs/{run_id}` - Permissions: - `repo` - `actions:read` -- [List jobs for a workflow run](https://docs.github.com/en/rest/reference/actions#list-jobs-for-a-workflow-run) +- [List jobs for a workflow run](https://docs.github.com/en/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run) - GET `/repos/{owner}/{repo}/actions/runs/{run_id}/jobs` - Permissions: - `repo` diff --git a/src/api.ts b/src/api.ts index 8c2984c..13f2a28 100644 --- a/src/api.ts +++ b/src/api.ts @@ -42,7 +42,7 @@ export async function fetchWorkflowRunState( runId: number, ): Promise { try { - // https://docs.github.com/en/rest/reference/actions#get-a-workflow-run + // https://docs.github.com/en/rest/actions/workflow-runs#get-a-workflow-run const response = await octokit.rest.actions.getWorkflowRun({ owner: config.owner, repo: config.repo, @@ -102,7 +102,7 @@ type ListJobsForWorkflowRunResponse = Awaited< async function fetchWorkflowRunJobs( runId: number, ): Promise { - // https://docs.github.com/en/rest/reference/actions#list-jobs-for-a-workflow-run + // https://docs.github.com/en/rest/actions/workflow-jobs#list-jobs-for-a-workflow-run const response = await octokit.rest.actions.listJobsForWorkflowRun({ owner: config.owner, repo: config.repo, From 5a12d9155be8724eb67d02043cfd92a3afe65104 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 7 Oct 2024 11:44:19 +1300 Subject: [PATCH 11/28] chore: add knip and address findings --- .github/workflows/test.yml | 13 ++++++ .prettierignore | 1 - eslint.config.mjs | 9 ++-- knip.ts | 15 +++++++ package.json | 9 ++-- pnpm-lock.yaml | 89 +++++++++++++++++++++++++++++++++++--- src/api.ts | 6 +-- src/await-remote-run.ts | 9 ++-- 8 files changed, 130 insertions(+), 21 deletions(-) create mode 100644 knip.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 93253ec..5c37be5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,3 +39,16 @@ jobs: uses: codecov/codecov-action@v5 env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + knip-report: + if: ${{ github.event_name == 'pull_request' }} + needs: [build] + runs-on: ubuntu-latest + permissions: + checks: write + issues: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - uses: jdx/mise-action@v2 + - run: pnpm i + - uses: codex-/knip-reporter@v2 diff --git a/.prettierignore b/.prettierignore index f221387..1521c8b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1 @@ dist -lib diff --git a/eslint.config.mjs b/eslint.config.mjs index 66c21c6..13f11e6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -48,10 +48,11 @@ export default tsEslint.config( }, { ignores: [ - "**/coverage", - "**/dist", - "**/esbuild.config.mjs", - "**/vitest.config.ts", + "coverage", + "dist", + "esbuild.config.mjs", + "knip.ts", + "vitest.config.ts", ], }, { diff --git a/knip.ts b/knip.ts new file mode 100644 index 0000000..ccacc2a --- /dev/null +++ b/knip.ts @@ -0,0 +1,15 @@ +import type { KnipConfig } from "knip"; + +const config: KnipConfig = { + ignore: ["dist/**"], + ignoreDependencies: [ + // Used in eslint.config.mjs + "eslint-plugin-github", + "eslint-plugin-import", + // Required by eslint-plugin-import-x + "@typescript-eslint/parser", + "eslint-import-resolver-typescript", + ], +}; + +export default config; diff --git a/package.json b/package.json index 838b848..8527586 100644 --- a/package.json +++ b/package.json @@ -10,13 +10,13 @@ "build": "pnpm run build:types && pnpm run build:bundle", "build:bundle": "node ./esbuild.config.mjs", "build:types": "tsc", - "format:check": "prettier --check **/*.ts", + "format:check": "prettier --check **/*.*", "format": "pnpm run format:check --write", "lint": "eslint .", "lint:fix": "pnpm run lint --fix", - "release": "release-it", "test": "vitest", - "test:coverage": "vitest --coverage" + "test:coverage": "vitest --coverage", + "knip": "knip" }, "repository": { "type": "git", @@ -37,7 +37,7 @@ "@eslint/js": "^9.25.1", "@opentf/std": "^0.13.0", "@total-typescript/ts-reset": "^0.6.1", - "@types/eslint__js": "^8.42.3", + "@types/eslint__js": "^9.14.0", "@types/node": "^20.17.32", "@typescript-eslint/eslint-plugin": "^8.31.1", "@typescript-eslint/parser": "^8.31.1", @@ -51,6 +51,7 @@ "eslint-plugin-github": "^5.1.8", "eslint-plugin-import": "^2.31.0", "eslint-plugin-import-x": "^4.11.0", + "knip": "^5.53.0", "prettier": "3.5.3", "typescript": "^5.8.3", "typescript-eslint": "^8.31.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9c933db..a197ad8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,8 +31,8 @@ importers: specifier: ^0.6.1 version: 0.6.1 '@types/eslint__js': - specifier: ^8.42.3 - version: 8.42.3 + specifier: ^9.14.0 + version: 9.14.0 '@types/node': specifier: ^20.17.32 version: 20.17.32 @@ -72,6 +72,9 @@ importers: eslint-plugin-import-x: specifier: ^4.11.0 version: 4.11.0(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + knip: + specifier: ^5.53.0 + version: 5.53.0(@types/node@20.17.32)(typescript@5.8.3) prettier: specifier: 3.5.3 version: 3.5.3 @@ -584,8 +587,9 @@ packages: '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - '@types/eslint__js@8.42.3': - resolution: {integrity: sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==} + '@types/eslint__js@9.14.0': + resolution: {integrity: sha512-s0jepCjOJWB/GKcuba4jISaVpBudw3ClXJ3fUK4tugChUMQsp6kSwuA8Dcx6wFd/JsJqcY8n4rEpa5RTHs5ypA==} + deprecated: This is a stub types definition. @eslint/js provides its own type definitions, so you do not need this installed. '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} @@ -1094,6 +1098,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + engines: {node: '>=10.13.0'} + es-abstract@1.23.9: resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} engines: {node: '>= 0.4'} @@ -1444,6 +1452,9 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -1687,6 +1698,14 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + knip@5.53.0: + resolution: {integrity: sha512-z8tTV59Rkwd/FRaSikKUIyLx9YY846muUJtmF+nM7e7f63LGyjqnSSsGBfE9RorqNanVqXvTjosGYkfKCmV7Nw==} + engines: {node: '>=18.18.0'} + hasBin: true + peerDependencies: + '@types/node': '>=18' + typescript: '>=5.0.4' + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -2089,6 +2108,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + smol-toml@1.3.4: + resolution: {integrity: sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==} + engines: {node: '>= 18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2149,6 +2172,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-json-comments@5.0.1: + resolution: {integrity: sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==} + engines: {node: '>=14.16'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2164,6 +2191,10 @@ packages: resolution: {integrity: sha512-R1urvuyiTaWfeCggqEvpDJwAlDVdsT9NM+IP//Tk2x7qHCkSvBk/fwFgw/TLAHzZlrAnnazMcRw0ZD8HlYFTEQ==} engines: {node: ^14.18.0 || >=16.0.0} + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -2409,6 +2440,15 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} + zod-validation-error@3.4.0: + resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.18.0 + + zod@3.24.4: + resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + snapshots: '@actions/core@1.11.1': @@ -2801,10 +2841,11 @@ snapshots: dependencies: '@types/estree': 1.0.7 '@types/json-schema': 7.0.15 + optional: true - '@types/eslint__js@8.42.3': + '@types/eslint__js@9.14.0': dependencies: - '@types/eslint': 9.6.1 + '@eslint/js': 9.25.1 '@types/estree@1.0.7': {} @@ -3363,6 +3404,11 @@ snapshots: emoji-regex@9.2.2: {} + enhanced-resolve@5.18.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.1 + es-abstract@1.23.9: dependencies: array-buffer-byte-length: 1.0.2 @@ -3901,6 +3947,8 @@ snapshots: gopd@1.2.0: {} + graceful-fs@4.2.11: {} + graphemer@1.4.0: {} has-bigints@1.1.0: {} @@ -4135,6 +4183,23 @@ snapshots: dependencies: json-buffer: 3.0.1 + knip@5.53.0(@types/node@20.17.32)(typescript@5.8.3): + dependencies: + '@nodelib/fs.walk': 1.2.8 + '@types/node': 20.17.32 + enhanced-resolve: 5.18.1 + fast-glob: 3.3.3 + jiti: 2.4.2 + js-yaml: 4.1.0 + minimist: 1.2.8 + picocolors: 1.1.1 + picomatch: 4.0.2 + smol-toml: 1.3.4 + strip-json-comments: 5.0.1 + typescript: 5.8.3 + zod: 3.24.4 + zod-validation-error: 3.4.0(zod@3.24.4) + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -4569,6 +4634,8 @@ snapshots: signal-exit@4.1.0: {} + smol-toml@1.3.4: {} + source-map-js@1.2.1: {} stable-hash@0.0.5: {} @@ -4634,6 +4701,8 @@ snapshots: strip-json-comments@3.1.1: {} + strip-json-comments@5.0.1: {} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -4647,6 +4716,8 @@ snapshots: '@pkgr/core': 0.2.0 tslib: 2.8.1 + tapable@2.2.1: {} + tar@6.2.1: dependencies: chownr: 2.0.0 @@ -4940,3 +5011,9 @@ snapshots: yocto-queue@0.1.0: {} yoctocolors@2.1.1: {} + + zod-validation-error@3.4.0(zod@3.24.4): + dependencies: + zod: 3.24.4 + + zod@3.24.4: {} diff --git a/src/api.ts b/src/api.ts index 13f2a28..fff576b 100644 --- a/src/api.ts +++ b/src/api.ts @@ -33,7 +33,7 @@ export function init(cfg?: ActionConfig): void { octokit = github.getOctokit(config.token); } -export interface WorkflowRunState { +interface WorkflowRunState { status: WorkflowRunStatus | null; conclusion: WorkflowRunConclusion | null; } @@ -78,7 +78,7 @@ export async function fetchWorkflowRunState( } } -export interface WorkflowRunJob { +interface WorkflowRunJob { id: number; name: string; status: "queued" | "in_progress" | "completed" | "waiting"; @@ -87,7 +87,7 @@ export interface WorkflowRunJob { url: string | null; } -export interface WorkflowRunJobStep { +interface WorkflowRunJobStep { name: string; status: string; conclusion: string | null; diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 0010cc2..a6d42c4 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -55,8 +55,11 @@ export async function run({ config, startTime }: RunOpts): Promise { ); if (fetchWorkflowRunStateResult.success) { const { status, conclusion } = fetchWorkflowRunStateResult.value; - - if (status === WorkflowRunStatus.Completed) { + if (status === WorkflowRunStatus.Queued) { + core.debug(`Run is queued to begin, attempt ${attemptNo}...`); + } else if (status === WorkflowRunStatus.InProgress) { + core.debug(`Run is in progress, attempt ${attemptNo}...`); + } else if (status === WorkflowRunStatus.Completed) { switch (conclusion) { case WorkflowRunConclusion.Success: core.info( @@ -82,7 +85,7 @@ export async function run({ config, startTime }: RunOpts): Promise { } } } else { - core.debug(`Run has not concluded, attempt ${attemptNo}...`); + core.debug(`Run has not yet been identified, attempt ${attemptNo}...`); } await sleep(config.pollIntervalMs); From 0aaae83ecabbe9cbf24423ebc436b70bc817c7d2 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 13:48:43 +1300 Subject: [PATCH 12/28] refactor: use result types for flow control --- src/api.ts | 11 ++++ src/await-remote-run.ts | 115 ++++++++++++++++++++++++++++------------ src/main.ts | 35 +++++++++--- src/types.ts | 11 ++-- 4 files changed, 123 insertions(+), 49 deletions(-) diff --git a/src/api.ts b/src/api.ts index fff576b..408bd6e 100644 --- a/src/api.ts +++ b/src/api.ts @@ -12,9 +12,18 @@ type Octokit = ReturnType<(typeof github)["getOctokit"]>; let config: ActionConfig; let octokit: Octokit; +/** + * The Status and Conclusion types are difficult to find a reliable source + * of truth for, but this seems accurate from testing: + * https://docs.github.com/en/enterprise-server@3.14/rest/guides/using-the-rest-api-to-interact-with-checks#about-check-suites + */ + export enum WorkflowRunStatus { Queued = "queued", InProgress = "in_progress", + Requested = "requested", + Waiting = "waiting", + Pending = "pending", Completed = "completed", } @@ -25,6 +34,8 @@ export enum WorkflowRunConclusion { Cancelled = "cancelled", Skipped = "skipped", TimedOut = "timed_out", + Stale = "stale", + StartupFailure = "startup_failure", ActionRequired = "action_required", } diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index a6d42c4..dd74a4e 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -8,9 +8,61 @@ import { WorkflowRunConclusion, WorkflowRunStatus, } from "./api.ts"; +import type { Result } from "./types.ts"; import { sleep } from "./utils.ts"; -async function logFailureDetails(runId: number): Promise { +function getWorkflowRunStatusResult( + status: WorkflowRunStatus | null, + attemptNo: number, +): Result { + if (status === WorkflowRunStatus.Queued) { + core.debug(`Run is queued to begin, attempt ${attemptNo}...`); + } else if (status === WorkflowRunStatus.InProgress) { + core.debug(`Run is in progress, attempt ${attemptNo}...`); + } else if (status === WorkflowRunStatus.Completed) { + core.debug("Run has completed"); + return { success: true, value: status }; + } + return { success: false, reason: "inconclusive" }; +} + +function getWorkflowRunConclusionResult( + conclusion: WorkflowRunConclusion | null, +): Result { + switch (conclusion) { + case WorkflowRunConclusion.Success: + return { success: true, value: conclusion }; + case WorkflowRunConclusion.ActionRequired: + case WorkflowRunConclusion.Cancelled: + case WorkflowRunConclusion.Failure: + case WorkflowRunConclusion.Neutral: + case WorkflowRunConclusion.Skipped: + case WorkflowRunConclusion.TimedOut: + core.error(`Run has failed with conclusion: ${conclusion}`); + return { success: false, reason: "timeout" }; + default: + core.error(`Run has failed with unsupported conclusion: ${conclusion}`); + core.info("Please open an issue with this conclusion value"); + return { success: false, reason: "unsupported" }; + } +} + +export function handleActionSuccess( + runId: number, + conclusion: WorkflowRunConclusion, +): void { + core.info( + "Run Completed:\n" + + ` Run ID: ${runId}\n` + + ` Status: ${WorkflowRunStatus.Completed}\n` + + ` Conclusion: ${conclusion}`, + ); +} + +export async function handleActionFail( + failureMsg: string, + runId: number, +): Promise { const failedJobs = await fetchWorkflowRunFailedJobs(runId); for (const failedJob of failedJobs) { const failedSteps = failedJob.steps @@ -33,20 +85,26 @@ async function logFailureDetails(runId: number): Promise { failedSteps, ); } + core.error(`Failed: ${failureMsg}`); + core.setFailed(failureMsg); } interface RunOpts { - config: ActionConfig; startTime: number; + config: ActionConfig; } -export async function run({ config, startTime }: RunOpts): Promise { +export async function getWorkflowRunResult({ + startTime, + config, +}: RunOpts): Promise< + Result<{ status: WorkflowRunStatus; conclusion: WorkflowRunConclusion }> +> { const timeoutMs = config.runTimeoutSeconds * 1000; let attemptNo = 0; let elapsedTime = Date.now() - startTime; while (elapsedTime < timeoutMs) { attemptNo++; - elapsedTime = Date.now() - startTime; const fetchWorkflowRunStateResult = await retryOnError( async () => fetchWorkflowRunState(config.runId), @@ -55,33 +113,20 @@ export async function run({ config, startTime }: RunOpts): Promise { ); if (fetchWorkflowRunStateResult.success) { const { status, conclusion } = fetchWorkflowRunStateResult.value; - if (status === WorkflowRunStatus.Queued) { - core.debug(`Run is queued to begin, attempt ${attemptNo}...`); - } else if (status === WorkflowRunStatus.InProgress) { - core.debug(`Run is in progress, attempt ${attemptNo}...`); - } else if (status === WorkflowRunStatus.Completed) { - switch (conclusion) { - case WorkflowRunConclusion.Success: - core.info( - "Run Completed:\n" + - ` Run ID: ${config.runId}\n` + - ` Status: ${status}\n` + - ` Conclusion: ${conclusion}`, - ); - return; - case WorkflowRunConclusion.ActionRequired: - case WorkflowRunConclusion.Cancelled: - case WorkflowRunConclusion.Failure: - case WorkflowRunConclusion.Neutral: - case WorkflowRunConclusion.Skipped: - case WorkflowRunConclusion.TimedOut: - core.error(`Run has failed with conclusion: ${conclusion}`); - await logFailureDetails(config.runId); - core.setFailed(conclusion); - return; - default: - core.setFailed(`Unknown conclusion: ${conclusion}`); - return; + const statusResult = getWorkflowRunStatusResult(status, attemptNo); + if (statusResult.success) { + const conclusionResult = getWorkflowRunConclusionResult(conclusion); + + if (conclusionResult.success) { + return { + success: true, + value: { + status: statusResult.value, + conclusion: conclusionResult.value, + }, + }; + } else { + return conclusionResult; } } } else { @@ -89,9 +134,11 @@ export async function run({ config, startTime }: RunOpts): Promise { } await sleep(config.pollIntervalMs); + elapsedTime = Date.now() - startTime; } - throw new Error( - `Timeout exceeded while awaiting completion of Run ${config.runId}`, - ); + return { + success: false, + reason: "timeout", + }; } diff --git a/src/main.ts b/src/main.ts index ed35047..fd27b10 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,11 @@ import * as core from "@actions/core"; import { getConfig } from "./action.ts"; import * as api from "./api.ts"; -import { run } from "./await-remote-run.ts"; +import { + getWorkflowRunResult, + handleActionFail, + handleActionSuccess, +} from "./await-remote-run.ts"; import * as constants from "./constants.ts"; async function main(): Promise { @@ -22,15 +26,32 @@ async function main(): Promise { ` URL: ${activeJobUrl}`, ); - await run({ config, startTime }); + const result = await getWorkflowRunResult({ config, startTime }); + if (result.success) { + handleActionSuccess(config.runId, result.value.conclusion); + } else { + const elapsedTime = Date.now() - startTime; + const failureMsg = + result.reason === "timeout" + ? `Timeout exceeded while attempting to await run conclusion (${elapsedTime}ms)` + : result.reason === "inconclusive" + ? "Run was inconclusive" + : "Unsupported conclusion was returned"; + await handleActionFail(failureMsg, config.runId); + } } catch (error) { if (error instanceof Error) { - core.error(`Failed to complete: ${error.message}`); - if (!error.message.includes("Timeout")) { - core.warning("Does the token have the correct permissions?"); - } + const failureMsg = `Failed: An unhandled error has occurred: ${error.message}`; + core.setFailed(failureMsg); + core.error(failureMsg); core.debug(error.stack ?? ""); - core.setFailed(error.message); + } else { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + const failureMsg = `Failed: An unknown error has occurred: ${error}`; + core.setFailed(failureMsg); + core.error(failureMsg); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + core.debug(error as any); } } } diff --git a/src/types.ts b/src/types.ts index c863d05..f456d70 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,16 +1,11 @@ -export type Result = ResultSuccess | ResultTimeout | ResultInvalidInput; +export type Result = ResultSuccess | ResultFailure; interface ResultSuccess { success: true; value: T; } -interface ResultTimeout { +interface ResultFailure { success: false; - reason: "timeout"; -} - -interface ResultInvalidInput { - success: false; - reason: "invalid input"; + reason: "timeout" | "inconclusive" | "unsupported"; } From e974ae39b1d4324e86894d6a98ab67ccadb1179d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 13:51:50 +1300 Subject: [PATCH 13/28] refactor: remove passing action config into getWorkflowRunResult --- src/await-remote-run.ts | 17 +++++++++-------- src/main.ts | 7 ++++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index dd74a4e..6251b9f 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -1,6 +1,5 @@ import * as core from "@actions/core"; -import { type ActionConfig } from "./action.ts"; import { fetchWorkflowRunFailedJobs, fetchWorkflowRunState, @@ -91,23 +90,25 @@ export async function handleActionFail( interface RunOpts { startTime: number; - config: ActionConfig; + pollIntervalMs: number; + runId: number; + runTimeoutMs: number; } export async function getWorkflowRunResult({ startTime, - config, + runId, + runTimeoutMs, + pollIntervalMs, }: RunOpts): Promise< Result<{ status: WorkflowRunStatus; conclusion: WorkflowRunConclusion }> > { - const timeoutMs = config.runTimeoutSeconds * 1000; - let attemptNo = 0; let elapsedTime = Date.now() - startTime; - while (elapsedTime < timeoutMs) { + while (elapsedTime < runTimeoutMs) { attemptNo++; const fetchWorkflowRunStateResult = await retryOnError( - async () => fetchWorkflowRunState(config.runId), + async () => fetchWorkflowRunState(runId), 400, "fetchWorkflowRunState", ); @@ -133,7 +134,7 @@ export async function getWorkflowRunResult({ core.debug(`Run has not yet been identified, attempt ${attemptNo}...`); } - await sleep(config.pollIntervalMs); + await sleep(pollIntervalMs); elapsedTime = Date.now() - startTime; } diff --git a/src/main.ts b/src/main.ts index fd27b10..98095cf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,7 +26,12 @@ async function main(): Promise { ` URL: ${activeJobUrl}`, ); - const result = await getWorkflowRunResult({ config, startTime }); + const result = await getWorkflowRunResult({ + startTime, + pollIntervalMs: config.pollIntervalMs, + runId: config.runId, + runTimeoutMs: config.runTimeoutSeconds * 1000, + }); if (result.success) { handleActionSuccess(config.runId, result.value.conclusion); } else { From d73efcea59a8e064746eba474f58cf27c7299ebf Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:34:08 +1300 Subject: [PATCH 14/28] fix: prevent extra iterations occurring on fetchWorkflowRunActiveJobUrlRetry --- src/api.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api.ts b/src/api.ts index 408bd6e..37d5266 100644 --- a/src/api.ts +++ b/src/api.ts @@ -238,17 +238,17 @@ export async function fetchWorkflowRunActiveJobUrlRetry( let elapsedTime = Date.now() - startTime; while (elapsedTime < timeout) { - elapsedTime = Date.now() - startTime; - core.debug( - `No 'in_progress' or 'completed' Jobs found for Workflow Run ${runId}, retrying...`, - ); - const url = await fetchWorkflowRunActiveJobUrl(runId); if (url) { return url; } - await new Promise((resolve) => setTimeout(resolve, 200)); + core.debug( + `No 'in_progress' or 'completed' Jobs found for Workflow Run ${runId}, retrying...`, + ); + + await sleep(200); + elapsedTime = Date.now() - startTime; } core.debug(`Timed out while trying to fetch URL for Workflow Run ${runId}`); From e5d2a7e3df8825feb0db0ff8ae28a47d43d98f2d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:35:30 +1300 Subject: [PATCH 15/28] fix: job logging indentation on fetchWorkflowRunFailedJobs --- src/api.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/api.ts b/src/api.ts index 37d5266..9dd713c 100644 --- a/src/api.ts +++ b/src/api.ts @@ -173,11 +173,11 @@ export async function fetchWorkflowRunFailedJobs( for (const job of jobs) { const steps = job.steps.map((step) => `${step.number}: ${step.name}`); core.debug( - ` Job: ${job.name}\n` + - ` ID: ${job.id}\n` + - ` Status: ${job.status}\n` + - ` Conclusion: ${job.conclusion}\n` + - ` Steps: [${steps.join(", ")}]`, + ` Job: ${job.name}\n` + + ` ID: ${job.id}\n` + + ` Status: ${job.status}\n` + + ` Conclusion: ${job.conclusion}\n` + + ` Steps: [${steps.join(", ")}]`, ); } From 1361475378013138871bcb6ac8f68d15616e69b2 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:42:54 +1300 Subject: [PATCH 16/28] test: add logging snapshots to api tests --- src/__snapshots__/api.spec.ts.snap | 25 +++ src/api.spec.ts | 247 +++++++++++++++++++++++++---- 2 files changed, 240 insertions(+), 32 deletions(-) create mode 100644 src/__snapshots__/api.spec.ts.snap diff --git a/src/__snapshots__/api.spec.ts.snap b/src/__snapshots__/api.spec.ts.snap new file mode 100644 index 0000000..0c45783 --- /dev/null +++ b/src/__snapshots__/api.spec.ts.snap @@ -0,0 +1,25 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`API > fetchWorkflowRunJobs > fetchWorkflowRunFailedJobs > should return the jobs for a failed workflow run given a run ID 1`] = ` +{ + "conclusion": "failure", + "id": 123456789, + "name": "test-run", + "status": "completed", + "steps": [ + { + "conclusion": "success", + "name": "Step 1", + "number": 1, + "status": "completed", + }, + { + "conclusion": "failure", + "name": "Step 2", + "number": 6, + "status": "completed", + }, + ], + "url": "https://github.com/codex-/await-remote-run/runs/123456789", +} +`; diff --git a/src/api.spec.ts b/src/api.spec.ts index 108430b..3f374d4 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -52,8 +52,13 @@ describe("API", () => { pollIntervalMs: 2500, }; - const { coreWarningLogMock, assertOnlyCalled, assertNoneCalled } = - mockLoggingFunctions(); + const { + coreErrorLogMock, + coreWarningLogMock, + coreDebugLogMock, + assertOnlyCalled, + assertNoneCalled, + } = mockLoggingFunctions(); afterAll(() => { vi.restoreAllMocks(); @@ -83,9 +88,21 @@ describe("API", () => { }), ); + // Behaviour const state = await fetchWorkflowRunState(123456); expect(state.conclusion).toStrictEqual(mockData.conclusion); expect(state.status).toStrictEqual(mockData.status); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot(` + "Fetched Run: + Repository: owner/repository + Run ID: 123456 + Status: completed + Conclusion: cancelled" + `); }); it("should throw if a non-200 status is returned", async () => { @@ -97,9 +114,18 @@ describe("API", () => { }), ); - await expect(fetchWorkflowRunState(0)).rejects.toThrow( + // Behaviour + await expect(fetchWorkflowRunState(0)).rejects.toThrowError( `Failed to fetch Workflow Run state, expected 200 but received ${errorStatus}`, ); + + // Logging + assertOnlyCalled(coreDebugLogMock, coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"fetchWorkflowRunState: An unexpected error has occurred: Failed to fetch Workflow Run state, expected 200 but received 401"`, + ); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); }); }); @@ -143,14 +169,53 @@ describe("API", () => { }), ); + // Behaviour const jobs = await fetchWorkflowRunFailedJobs(123456); expect(jobs).toHaveLength(1); - expect(jobs[0]?.id).toStrictEqual(mockData.jobs[0]?.id); - expect(jobs[0]?.name).toStrictEqual(mockData.jobs[0]?.name); - expect(jobs[0]?.status).toStrictEqual(mockData.jobs[0]?.status); - expect(jobs[0]?.conclusion).toStrictEqual(mockData.jobs[0]?.conclusion); - expect(jobs[0]?.url).toStrictEqual(mockData.jobs[0]?.html_url); - expect(Array.isArray(jobs[0]?.steps)).toStrictEqual(true); + expect(jobs[0]).toMatchSnapshot(); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledTimes(2); + expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: [test-run]" + `); + expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot(` + " Job: test-run + ID: 123456789 + Status: completed + Conclusion: failure + Steps: [1: Step 1, 6: Step 2]" + `); + }); + + it("should log a warning if no failed jobs are found", async () => { + vi.spyOn( + mockOctokit.rest.actions, + "listJobsForWorkflowRun", + ).mockReturnValue( + Promise.resolve({ + data: { + total_count: 0, + jobs: [], + }, + status: 200, + }), + ); + + // Behaviour + const jobs = await fetchWorkflowRunFailedJobs(123456); + expect(jobs).toHaveLength(0); + + // Logging + assertOnlyCalled(coreWarningLogMock); + expect(coreWarningLogMock).toHaveBeenCalledOnce(); + expect(coreWarningLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"Failed to find failed Jobs for Workflow Run 123456"`, + ); }); it("should throw if a non-200 status is returned", async () => { @@ -165,9 +230,18 @@ describe("API", () => { }), ); - await expect(fetchWorkflowRunFailedJobs(0)).rejects.toThrow( + // Behaviour + await expect(fetchWorkflowRunFailedJobs(0)).rejects.toThrowError( `Failed to fetch Jobs for Workflow Run, expected 200 but received ${errorStatus}`, ); + + // Logging + assertOnlyCalled(coreErrorLogMock, coreDebugLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"fetchWorkflowRunFailedJobs: An unexpected error has occurred: Failed to fetch Jobs for Workflow Run, expected 200 but received 401"`, + ); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); }); it("should return the steps for a failed Job", async () => { @@ -182,14 +256,26 @@ describe("API", () => { }), ); + // Behaviour const { steps } = (await fetchWorkflowRunFailedJobs(123456))[0]!; - expect(steps).toHaveLength(mockData.jobs[0]!.steps.length); - for (let i = 0; i < mockSteps.length; i++) { - expect(steps[i]?.name).toStrictEqual(mockSteps[i]?.name); - expect(steps[i]?.number).toStrictEqual(mockSteps[i]?.number); - expect(steps[i]?.status).toStrictEqual(mockSteps[i]?.status); - expect(steps[i]?.conclusion).toStrictEqual(mockSteps[i]?.conclusion); - } + expect(steps).toMatchObject(mockSteps); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledTimes(2); + expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: [test-run]" + `); + expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot(` + " Job: test-run + ID: 123456789 + Status: completed + Conclusion: failure + Steps: [1: Step 1, 6: Step 2]" + `); }); }); @@ -237,8 +323,19 @@ describe("API", () => { }), ); + // Behaviour const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(mockData.jobs[0]?.html_url); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot(` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: [test-run (completed)]" + `); }); it("should throw if a non-200 status is returned", async () => { @@ -253,9 +350,18 @@ describe("API", () => { }), ); - await expect(fetchWorkflowRunActiveJobUrl(0)).rejects.toThrow( + // Behaviour + await expect(fetchWorkflowRunActiveJobUrl(0)).rejects.toThrowError( `Failed to fetch Jobs for Workflow Run, expected 200 but received ${errorStatus}`, ); + + // Logging + assertOnlyCalled(coreErrorLogMock, coreDebugLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"fetchWorkflowRunActiveJobUrl: An unexpected error has occurred: Failed to fetch Jobs for Workflow Run, expected 200 but received 401"`, + ); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); }); it("should return undefined if no in_progress job is found", async () => { @@ -271,8 +377,21 @@ describe("API", () => { }), ); + // Behaviour const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(undefined); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + ` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: []" + `, + ); }); it("should return even if GitHub fails to return a URL", async () => { @@ -288,8 +407,21 @@ describe("API", () => { }), ); + // Behaviour const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual("GitHub failed to return the URL"); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + ` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: [test-run (in_progress)]" + `, + ); }); describe("fetchWorkflowRunActiveJobUrlRetry", () => { @@ -314,12 +446,31 @@ describe("API", () => { }), ); + // Behaviour const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 100); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); const url = await urlPromise; expect(url).toStrictEqual("Unable to fetch URL"); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledTimes(3); + expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + ` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: []" + `, + ); + expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot( + `"No 'in_progress' or 'completed' Jobs found for Workflow Run 123456, retrying..."`, + ); + expect(coreDebugLogMock.mock.calls[2]?.[0]).toMatchInlineSnapshot( + `"Timed out while trying to fetch URL for Workflow Run 123456"`, + ); }); it("should return a message if no job is found within the timeout period", async () => { @@ -350,12 +501,29 @@ describe("API", () => { }), ); + // Behaviour const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 200); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); const url = await urlPromise; expect(url).toStrictEqual("Unable to fetch URL"); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledTimes(3); + expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: []" + `); + expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot( + `"No 'in_progress' or 'completed' Jobs found for Workflow Run 123456, retrying..."`, + ); + expect(coreDebugLogMock.mock.calls[2]?.[0]).toMatchInlineSnapshot( + `"Timed out while trying to fetch URL for Workflow Run 123456"`, + ); }); it("should return a URL if an in_progress job is found", async () => { @@ -369,12 +537,23 @@ describe("API", () => { }), ); + // Behaviour const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 200); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); const url = await urlPromise; expect(url).toStrictEqual(inProgressMockData.jobs[0]?.html_url); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot(` + "Fetched Jobs for Run: + Repository: owner/repository + Run ID: 123456 + Jobs: [test-run (in_progress)]" + `); }); }); }); @@ -394,14 +573,15 @@ describe("API", () => { .fn<() => Promise>() .mockImplementation(() => Promise.resolve("completed")); + // Behaviour const result = await retryOnError(() => testFunc(), 5000); - if (!result.success) { expect.fail(); } - expect(result.success).toStrictEqual(true); expect(result.value).toStrictEqual("completed"); + + // Logging assertNoneCalled(); }); @@ -410,13 +590,14 @@ describe("API", () => { const testFunc = vi .fn<() => Promise>() .mockImplementation(() => Promise.resolve("completed")) - .mockImplementationOnce(() => Promise.reject(Error(errorMsg))); + .mockImplementationOnce(() => Promise.reject(new Error(errorMsg))); + // Behaviour const retryPromise = retryOnError(testFunc, 5000); - // Progress timers to first failure await vi.advanceTimersByTimeAsync(1000); + // Logging assertOnlyCalled(coreWarningLogMock); expect(coreWarningLogMock).toHaveBeenCalledOnce(); expect(coreWarningLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` @@ -427,32 +608,36 @@ describe("API", () => { expect(coreWarningLogMock.mock.calls[0]?.[0]).toContain(testFunc.name); coreWarningLogMock.mockReset(); + // Behaviour // Progress timers to second success await vi.advanceTimersByTimeAsync(1000); - const result = await retryPromise; - if (!result.success) { expect.fail(); } - assertNoneCalled(); expect(result.success).toStrictEqual(true); expect(result.value).toStrictEqual("completed"); + + // Logging + assertNoneCalled(); }); it("should display a fallback function name if none is available", async () => { const errorMsg = "some error"; const testFunc = vi .fn<() => Promise>() - .mockImplementationOnce(() => Promise.reject(Error(errorMsg))); + .mockImplementationOnce(() => Promise.reject(new Error(errorMsg))); + // Behaviour // Use anonymous function const retryPromise = retryOnError(() => testFunc(), 5000); - // Progress timers to first failure await vi.advanceTimersByTimeAsync(1000); + // Clean up promise + await retryPromise; + // Logging assertOnlyCalled(coreWarningLogMock); expect(coreWarningLogMock).toHaveBeenCalledOnce(); expect(coreWarningLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` @@ -461,9 +646,6 @@ describe("API", () => { error: some error" `); coreWarningLogMock.mockReset(); - - // Clean up promise - await retryPromise; }); it("should return a timeout result", async () => { @@ -475,10 +657,9 @@ describe("API", () => { throw new Error(errorMsg); }); + // Behaviour const retryPromise = retryOnError(() => testFunc(), 500); - await vi.advanceTimersByTimeAsync(2000); - const result = await retryPromise; if (result.success) { @@ -487,6 +668,8 @@ describe("API", () => { expect(result.success).toStrictEqual(false); expect(result.reason).toStrictEqual("timeout"); + + // Logging assertNoneCalled(); }); }); From abb62ce92e89ba5bbda521713f71b55f11f9cf87 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:49:58 +1300 Subject: [PATCH 17/28] test: cover missing cases for action config parsing and assert logging --- src/action.spec.ts | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/action.spec.ts b/src/action.spec.ts index 0ffa1f6..b452749 100644 --- a/src/action.spec.ts +++ b/src/action.spec.ts @@ -2,10 +2,13 @@ import * as core from "@actions/core"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { type ActionConfig, getConfig } from "./action.ts"; +import { mockLoggingFunctions } from "./test-utils/logging.mock.ts"; vi.mock("@actions/core"); describe("Action", () => { + const { assertNoneCalled } = mockLoggingFunctions(); + describe("getConfig", () => { // Represent the process.env inputs. let mockEnvConfig: any; @@ -47,6 +50,7 @@ describe("Action", () => { }); it("should return a valid config", () => { + // Behaviour const config: ActionConfig = getConfig(); // Assert that the numbers / types have been properly loaded. @@ -56,20 +60,53 @@ describe("Action", () => { expect(config.runId).toStrictEqual(123456); expect(config.runTimeoutSeconds).toStrictEqual(300); expect(config.pollIntervalMs).toStrictEqual(2500); + + // Logging + assertNoneCalled(); }); it("should provide a default run timeout if none is supplied", () => { mockEnvConfig.run_timeout_seconds = ""; - const config: ActionConfig = getConfig(); + // Behaviour + const config: ActionConfig = getConfig(); expect(config.runTimeoutSeconds).toStrictEqual(300); + + // Logging + assertNoneCalled(); }); it("should provide a default polling interval if none is supplied", () => { mockEnvConfig.poll_interval_ms = ""; - const config: ActionConfig = getConfig(); + // Behaviour + const config: ActionConfig = getConfig(); expect(config.pollIntervalMs).toStrictEqual(5000); + + // Logging + assertNoneCalled(); + }); + + it("should throw if an invalid number value is provided", () => { + mockEnvConfig.run_timeout_seconds = "invalid value"; + + // Behaviour + expect(() => getConfig()).toThrowError( + "Unable to parse value: invalid value", + ); + + // Logging + assertNoneCalled(); + }); + + it("should throw if no run ID value is provided", () => { + mockEnvConfig.run_id = ""; + + // Behaviour + expect(() => getConfig()).toThrowError("Run ID must be provided"); + + // Logging + assertNoneCalled(); }); }); }); From b8a17044c834f78da0517c63533855521b278442 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:51:49 +1300 Subject: [PATCH 18/28] chore: exclude test mocks from code coverage --- vitest.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/vitest.config.ts b/vitest.config.ts index 548f563..1b48c9d 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ provider: "v8", reporter: ["text", "lcov"], include: ["src/**/*.ts"], + exclude: ["src/**/*.spec.*", "src/test-utils/**/*.ts", "src/reset.d.ts"], }, }, }); From 242f5cda3699be4699b70d6f5a33e1fcb4ce4be2 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 15:05:27 +1300 Subject: [PATCH 19/28] test: add getWorkflowRunStatusResult tests --- src/await-remote-run.spec.ts | 139 +++++++++++++++++++++++++++++++++++ src/await-remote-run.ts | 16 ++-- 2 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 src/await-remote-run.spec.ts diff --git a/src/await-remote-run.spec.ts b/src/await-remote-run.spec.ts new file mode 100644 index 0000000..e5d4077 --- /dev/null +++ b/src/await-remote-run.spec.ts @@ -0,0 +1,139 @@ +import * as core from "@actions/core"; +import * as github from "@actions/github"; +import { + afterAll, + afterEach, + beforeEach, + describe, + expect, + it, + vi, +} from "vitest"; + +import { + fetchWorkflowRunActiveJobUrl, + fetchWorkflowRunActiveJobUrlRetry, + fetchWorkflowRunFailedJobs, + fetchWorkflowRunState, + init, + retryOnError, + WorkflowRunStatus, +} from "./api.ts"; +import { mockLoggingFunctions } from "./test-utils/logging.mock.ts"; +import { getWorkflowRunStatusResult } from "./await-remote-run.ts"; + +vi.mock("@actions/core"); +vi.mock("@actions/github"); + +interface MockResponse { + data: any; + status: number; +} + +const mockOctokit = { + rest: { + actions: { + getWorkflowRun: (_req?: any): Promise => { + throw new Error("Should be mocked"); + }, + listJobsForWorkflowRun: (_req?: any): Promise => { + throw new Error("Should be mocked"); + }, + }, + }, +}; + +describe("await-remote-run", () => { + const { + coreErrorLogMock, + coreWarningLogMock, + coreDebugLogMock, + assertOnlyCalled, + assertNoneCalled, + } = mockLoggingFunctions(); + + afterAll(() => { + vi.restoreAllMocks(); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + describe("getWorkflowRunStatusResult", () => { + it("should return success on completed status", () => { + // Behaviour + const result = getWorkflowRunStatusResult(WorkflowRunStatus.Completed, 0); + if (!result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(true); + expect(result.value).toStrictEqual(WorkflowRunStatus.Completed); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"Run has completed"`, + ); + }); + + it("should return inconclusive on queued status", () => { + // Behaviour + const result = getWorkflowRunStatusResult(WorkflowRunStatus.Queued, 0); + if (result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("inconclusive"); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"Run is queued to begin, attempt 0..."`, + ); + }); + + it("should return inconclusive on in_progress status", () => { + // Behaviour + const result = getWorkflowRunStatusResult( + WorkflowRunStatus.InProgress, + 0, + ); + if (result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("inconclusive"); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"Run is in progress, attempt 0..."`, + ); + }); + + it.each([ + WorkflowRunStatus.Pending, + WorkflowRunStatus.Requested, + WorkflowRunStatus.Waiting, + ])("should return unsupported on %s status", (status) => { + // Behaviour + const result = getWorkflowRunStatusResult(status, 0); + if (result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("unsupported"); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + expect(coreDebugLogMock.mock.lastCall?.[0]).toStrictEqual( + `Run has returned an unsupported status: ${status}`, + ); + }); + }); +}); diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 6251b9f..90d3d73 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -10,19 +10,25 @@ import { import type { Result } from "./types.ts"; import { sleep } from "./utils.ts"; -function getWorkflowRunStatusResult( +export function getWorkflowRunStatusResult( status: WorkflowRunStatus | null, attemptNo: number, ): Result { + if (status === WorkflowRunStatus.Completed) { + core.debug("Run has completed"); + return { success: true, value: status }; + } + if (status === WorkflowRunStatus.Queued) { core.debug(`Run is queued to begin, attempt ${attemptNo}...`); + return { success: false, reason: "inconclusive" }; } else if (status === WorkflowRunStatus.InProgress) { core.debug(`Run is in progress, attempt ${attemptNo}...`); - } else if (status === WorkflowRunStatus.Completed) { - core.debug("Run has completed"); - return { success: true, value: status }; + return { success: false, reason: "inconclusive" }; } - return { success: false, reason: "inconclusive" }; + + core.debug(`Run has returned an unsupported status: ${status}`); + return { success: false, reason: "unsupported" }; } function getWorkflowRunConclusionResult( From b91d6065a2d8651e49c150ae3c14428ac186365a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 28 Oct 2024 12:09:18 +1300 Subject: [PATCH 20/28] test: fix bug in logging assertions --- src/test-utils/logging.mock.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test-utils/logging.mock.ts b/src/test-utils/logging.mock.ts index 26f60bb..2e28586 100644 --- a/src/test-utils/logging.mock.ts +++ b/src/test-utils/logging.mock.ts @@ -74,11 +74,13 @@ function assertOnlyCalledInner( // Once Node 22 is LTS, this can be: // const diff = coreLogMockSet.symmetricDifference(new Set(coreLogMocks)); - const diff = symDiff([[...coreLogMockSet], coreLogMocks]); - - for (const logMock of diff) { + const notCalled = symDiff([[...coreLogMockSet], coreLogMocks]); + for (const logMock of notCalled) { expect(logMock).not.toHaveBeenCalled(); } + for (const logMock of coreLogMocks) { + expect(logMock).toHaveBeenCalled(); + } } function assertNoneCalledInner( From 5cee2efbb1d748eaa344cda527d924893e79ae28 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 7 Nov 2024 07:30:09 +1300 Subject: [PATCH 21/28] refactor: handleActionFailure reworked to set failure before logging --- .../await-remote-run.spec.ts.snap | 38 +++ src/await-remote-run.spec.ts | 261 +++++++++++++++--- src/await-remote-run.ts | 17 +- 3 files changed, 265 insertions(+), 51 deletions(-) create mode 100644 src/__snapshots__/await-remote-run.spec.ts.snap diff --git a/src/__snapshots__/await-remote-run.spec.ts.snap b/src/__snapshots__/await-remote-run.spec.ts.snap new file mode 100644 index 0000000..0ad918d --- /dev/null +++ b/src/__snapshots__/await-remote-run.spec.ts.snap @@ -0,0 +1,38 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`await-remote-run > handleActionFail > should fetch and log failed jobs from the remote run 2`] = ` +"Job First Job: + ID: 0 + Status: completed + Conclusion: failure + URL: url + Steps (non-success): + 0: First Step + Status: completed + Conclusion: failure" +`; + +exports[`await-remote-run > handleActionFail > should fetch and log failed jobs from the remote run 3`] = ` +"Job Second Job: + ID: 0 + Status: completed + Conclusion: failure + URL: url + Steps (non-success): +" +`; + +exports[`await-remote-run > handleActionFail > should only log steps that did not succeed 2`] = ` +"Job First Job: + ID: 0 + Status: completed + Conclusion: failure + URL: url + Steps (non-success): + 1: Second Step + Status: completed + Conclusion: failure + 2: Third Step + Status: completed + Conclusion: skipped" +`; diff --git a/src/await-remote-run.spec.ts b/src/await-remote-run.spec.ts index e5d4077..6b772b8 100644 --- a/src/await-remote-run.spec.ts +++ b/src/await-remote-run.spec.ts @@ -1,5 +1,4 @@ import * as core from "@actions/core"; -import * as github from "@actions/github"; import { afterAll, afterEach, @@ -8,45 +7,26 @@ import { expect, it, vi, + type MockInstance, } from "vitest"; +import * as api from "./api.ts"; import { - fetchWorkflowRunActiveJobUrl, - fetchWorkflowRunActiveJobUrlRetry, - fetchWorkflowRunFailedJobs, - fetchWorkflowRunState, - init, - retryOnError, - WorkflowRunStatus, -} from "./api.ts"; + getWorkflowRunConclusionResult, + getWorkflowRunStatusResult, + handleActionFail, +} from "./await-remote-run.ts"; import { mockLoggingFunctions } from "./test-utils/logging.mock.ts"; -import { getWorkflowRunStatusResult } from "./await-remote-run.ts"; +import { WorkflowRunConclusion, WorkflowRunStatus } from "./types.ts"; vi.mock("@actions/core"); vi.mock("@actions/github"); - -interface MockResponse { - data: any; - status: number; -} - -const mockOctokit = { - rest: { - actions: { - getWorkflowRun: (_req?: any): Promise => { - throw new Error("Should be mocked"); - }, - listJobsForWorkflowRun: (_req?: any): Promise => { - throw new Error("Should be mocked"); - }, - }, - }, -}; +vi.mock("./api.ts"); describe("await-remote-run", () => { const { coreErrorLogMock, - coreWarningLogMock, + coreInfoLogMock, coreDebugLogMock, assertOnlyCalled, assertNoneCalled, @@ -71,11 +51,7 @@ describe("await-remote-run", () => { expect(result.value).toStrictEqual(WorkflowRunStatus.Completed); // Logging - assertOnlyCalled(coreDebugLogMock); - expect(coreDebugLogMock).toHaveBeenCalledOnce(); - expect(coreDebugLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( - `"Run has completed"`, - ); + assertNoneCalled(); }); it("should return inconclusive on queued status", () => { @@ -85,7 +61,7 @@ describe("await-remote-run", () => { expect.fail(); } expect(result.success).toStrictEqual(false); - expect(result.reason).toStrictEqual("inconclusive"); + expect(result.reason).toStrictEqual("pending"); // Logging assertOnlyCalled(coreDebugLogMock); @@ -105,7 +81,7 @@ describe("await-remote-run", () => { expect.fail(); } expect(result.success).toStrictEqual(false); - expect(result.reason).toStrictEqual("inconclusive"); + expect(result.reason).toStrictEqual("pending"); // Logging assertOnlyCalled(coreDebugLogMock); @@ -132,8 +108,219 @@ describe("await-remote-run", () => { assertOnlyCalled(coreDebugLogMock); expect(coreDebugLogMock).toHaveBeenCalledOnce(); expect(coreDebugLogMock.mock.lastCall?.[0]).toStrictEqual( - `Run has returned an unsupported status: ${status}`, + `Run status is unsupported: ${status}`, + ); + }); + }); + + describe("getWorkflowRunConclusionResult", () => { + it("should return success on success conclusion", () => { + // Behaviour + const result = getWorkflowRunConclusionResult( + WorkflowRunConclusion.Success, + ); + if (!result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(true); + expect(result.value).toStrictEqual(WorkflowRunConclusion.Success); + + // Logging + assertNoneCalled(); + }); + + it("should return non-success on an unsupported conclusion", () => { + // Behaviour + const result = getWorkflowRunConclusionResult( + "random_conclusion" as WorkflowRunConclusion, + ); + if (result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("unsupported"); + + // Logging + assertOnlyCalled(coreErrorLogMock, coreInfoLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"Run has failed with unsupported conclusion: random_conclusion"`, + ); + expect(coreInfoLogMock).toHaveBeenCalledOnce(); + expect(coreInfoLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"Please open an issue with this conclusion value"`, + ); + }); + + it("should return non-success on timeout conclusion", () => { + // Behaviour + const result = getWorkflowRunConclusionResult( + WorkflowRunConclusion.TimedOut, + ); + if (result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("timeout"); + + // Logging + assertOnlyCalled(coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.lastCall?.[0]).toMatchInlineSnapshot( + `"Run has timeout out"`, + ); + }); + + it.each([ + WorkflowRunConclusion.ActionRequired, + WorkflowRunConclusion.Cancelled, + WorkflowRunConclusion.Failure, + WorkflowRunConclusion.Neutral, + WorkflowRunConclusion.Skipped, + ])("should return non-success on %s conclusion", (conclusion) => { + // Behaviour + const result = getWorkflowRunConclusionResult(conclusion); + if (result.success) { + expect.fail(); + } + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("inconclusive"); + + // Logging + assertOnlyCalled(coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.lastCall?.[0]).toStrictEqual( + `Run has failed with conclusion: ${conclusion}`, + ); + }); + }); + + describe("handleActionFail", () => { + let setFailedSpy: MockInstance; + let setOutputSpy: MockInstance; + + let apiFetchWorkflowRunFailedJobsMock: MockInstance< + typeof api.fetchWorkflowRunFailedJobs + >; + + beforeEach(() => { + setFailedSpy = vi.spyOn(core, "setFailed"); + setOutputSpy = vi.spyOn(core, "setOutput"); + + apiFetchWorkflowRunFailedJobsMock = vi.spyOn( + api, + "fetchWorkflowRunFailedJobs", + ); + }); + + it("should set the action output and status", async () => { + apiFetchWorkflowRunFailedJobsMock.mockResolvedValue([]); + + const testMsg = "Test Message"; + await handleActionFail(testMsg, 0); + + // Behaviour + expect(setFailedSpy).toHaveBeenCalled(); + expect(setOutputSpy).not.toHaveBeenCalled(); + + // Logging + assertOnlyCalled(coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + `"Failed: Test Message"`, + ); + }); + + it("should fetch and log failed jobs from the remote run", async () => { + const jobs = [ + { + name: "First Job", + id: 0, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Failure, + url: "url", + steps: [ + { + name: "First Step", + number: 0, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Failure, + }, + ], + }, + { + name: "Second Job", + id: 0, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Failure, + url: "url", + steps: [ + { + name: "First Step", + number: 0, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Success, + }, + ], + }, + ]; + apiFetchWorkflowRunFailedJobsMock.mockResolvedValue(jobs); + + const testMsg = "Test Message"; + await handleActionFail(testMsg, 0); + + // Logging + assertOnlyCalled(coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledTimes(3); + expect(coreErrorLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + `"Failed: Test Message"`, + ); + expect(coreErrorLogMock.mock.calls[1]?.[0]).toMatchSnapshot(); + expect(coreErrorLogMock.mock.calls[2]?.[0]).toMatchSnapshot(); + }); + + it("should only log steps that did not succeed", async () => { + const jobs = [ + { + name: "First Job", + id: 0, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Failure, + url: "url", + steps: [ + { + name: "First Step", + number: 0, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Success, + }, + { + name: "Second Step", + number: 1, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Failure, + }, + { + name: "Third Step", + number: 2, + status: "completed" as const, + conclusion: WorkflowRunConclusion.Skipped, + }, + ], + }, + ]; + apiFetchWorkflowRunFailedJobsMock.mockResolvedValue(jobs); + + const testMsg = "Test Message"; + await handleActionFail(testMsg, 0); + + // Logging + assertOnlyCalled(coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledTimes(2); + expect(coreErrorLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + `"Failed: Test Message"`, ); + expect(coreErrorLogMock.mock.calls[1]?.[0]).toMatchSnapshot(); }); }); }); diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 90d3d73..9560b36 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -52,22 +52,13 @@ function getWorkflowRunConclusionResult( } } -export function handleActionSuccess( - runId: number, - conclusion: WorkflowRunConclusion, -): void { - core.info( - "Run Completed:\n" + - ` Run ID: ${runId}\n` + - ` Status: ${WorkflowRunStatus.Completed}\n` + - ` Conclusion: ${conclusion}`, - ); -} - export async function handleActionFail( failureMsg: string, runId: number, ): Promise { + core.error(`Failed: ${failureMsg}`); + core.setFailed(failureMsg); + const failedJobs = await fetchWorkflowRunFailedJobs(runId); for (const failedJob of failedJobs) { const failedSteps = failedJob.steps @@ -90,8 +81,6 @@ export async function handleActionFail( failedSteps, ); } - core.error(`Failed: ${failureMsg}`); - core.setFailed(failureMsg); } interface RunOpts { From 6f35b7e66b2b385fb60dee9f5fb608aba5634ccc Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 7 Nov 2024 07:33:24 +1300 Subject: [PATCH 22/28] refactor: rework some types --- src/api.ts | 33 +++---------------- src/await-remote-run.ts | 73 +++++++++++++++++++++++++---------------- src/main.ts | 18 +++++----- src/types.ts | 59 +++++++++++++++++++++++++++++++-- 4 files changed, 116 insertions(+), 67 deletions(-) diff --git a/src/api.ts b/src/api.ts index 9dd713c..5dd297d 100644 --- a/src/api.ts +++ b/src/api.ts @@ -4,7 +4,11 @@ import * as core from "@actions/core"; import * as github from "@actions/github"; import { type ActionConfig, getConfig } from "./action.ts"; -import type { Result } from "./types.ts"; +import type { + Result, + WorkflowRunConclusion, + WorkflowRunStatus, +} from "./types.ts"; import { sleep } from "./utils.ts"; type Octokit = ReturnType<(typeof github)["getOctokit"]>; @@ -12,33 +16,6 @@ type Octokit = ReturnType<(typeof github)["getOctokit"]>; let config: ActionConfig; let octokit: Octokit; -/** - * The Status and Conclusion types are difficult to find a reliable source - * of truth for, but this seems accurate from testing: - * https://docs.github.com/en/enterprise-server@3.14/rest/guides/using-the-rest-api-to-interact-with-checks#about-check-suites - */ - -export enum WorkflowRunStatus { - Queued = "queued", - InProgress = "in_progress", - Requested = "requested", - Waiting = "waiting", - Pending = "pending", - Completed = "completed", -} - -export enum WorkflowRunConclusion { - Success = "success", - Failure = "failure", - Neutral = "neutral", - Cancelled = "cancelled", - Skipped = "skipped", - TimedOut = "timed_out", - Stale = "stale", - StartupFailure = "startup_failure", - ActionRequired = "action_required", -} - export function init(cfg?: ActionConfig): void { config = cfg ?? getConfig(); octokit = github.getOctokit(config.token); diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 9560b36..a847f68 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -4,36 +4,39 @@ import { fetchWorkflowRunFailedJobs, fetchWorkflowRunState, retryOnError, +} from "./api.ts"; +import { WorkflowRunConclusion, WorkflowRunStatus, -} from "./api.ts"; -import type { Result } from "./types.ts"; + type Result, + type WorkflowRunConclusionResult, + type WorkflowRunStatusResult, +} from "./types.ts"; import { sleep } from "./utils.ts"; export function getWorkflowRunStatusResult( status: WorkflowRunStatus | null, attemptNo: number, -): Result { +): WorkflowRunStatusResult { if (status === WorkflowRunStatus.Completed) { - core.debug("Run has completed"); return { success: true, value: status }; } if (status === WorkflowRunStatus.Queued) { core.debug(`Run is queued to begin, attempt ${attemptNo}...`); - return { success: false, reason: "inconclusive" }; + return { success: false, reason: "pending", value: status }; } else if (status === WorkflowRunStatus.InProgress) { core.debug(`Run is in progress, attempt ${attemptNo}...`); - return { success: false, reason: "inconclusive" }; + return { success: false, reason: "pending", value: status }; } - core.debug(`Run has returned an unsupported status: ${status}`); - return { success: false, reason: "unsupported" }; + core.debug(`Run status is unsupported: ${status}`); + return { success: false, reason: "unsupported", value: status ?? "null" }; } -function getWorkflowRunConclusionResult( +export function getWorkflowRunConclusionResult( conclusion: WorkflowRunConclusion | null, -): Result { +): WorkflowRunConclusionResult { switch (conclusion) { case WorkflowRunConclusion.Success: return { success: true, value: conclusion }; @@ -42,13 +45,19 @@ function getWorkflowRunConclusionResult( case WorkflowRunConclusion.Failure: case WorkflowRunConclusion.Neutral: case WorkflowRunConclusion.Skipped: - case WorkflowRunConclusion.TimedOut: core.error(`Run has failed with conclusion: ${conclusion}`); - return { success: false, reason: "timeout" }; + return { success: false, reason: "inconclusive", value: conclusion }; + case WorkflowRunConclusion.TimedOut: + core.error("Run has timeout out"); + return { success: false, reason: "timeout", value: conclusion }; default: core.error(`Run has failed with unsupported conclusion: ${conclusion}`); core.info("Please open an issue with this conclusion value"); - return { success: false, reason: "unsupported" }; + return { + success: false, + reason: "unsupported", + value: conclusion ?? "null", + }; } } @@ -95,7 +104,10 @@ export async function getWorkflowRunResult({ runTimeoutMs, pollIntervalMs, }: RunOpts): Promise< - Result<{ status: WorkflowRunStatus; conclusion: WorkflowRunConclusion }> + Result< + | { status: WorkflowRunStatus.Completed; conclusion: WorkflowRunConclusion } + | { status: WorkflowRunStatus; conclusion?: WorkflowRunConclusion } + > > { let attemptNo = 0; let elapsedTime = Date.now() - startTime; @@ -107,26 +119,31 @@ export async function getWorkflowRunResult({ 400, "fetchWorkflowRunState", ); - if (fetchWorkflowRunStateResult.success) { + if (!fetchWorkflowRunStateResult.success) { + core.debug(`Failed to fetch run state, attempt ${attemptNo}...`); + } else { const { status, conclusion } = fetchWorkflowRunStateResult.value; const statusResult = getWorkflowRunStatusResult(status, attemptNo); if (statusResult.success) { + // We only get a conclusion should the status resolve, otherwise it is null. const conclusionResult = getWorkflowRunConclusionResult(conclusion); - if (conclusionResult.success) { - return { - success: true, - value: { - status: statusResult.value, - conclusion: conclusionResult.value, - }, - }; - } else { - return conclusionResult; - } + return { + success: true, + value: { + status: statusResult.value, + conclusion: conclusionResult.success + ? conclusionResult.value + : undefined, + }, + }; + } + + // If the status is unsupported, we can't guarantee it will ever + // resolve. Alert to raise this so we can handle it properly. + if (statusResult.reason === "unsupported") { + return statusResult; } - } else { - core.debug(`Run has not yet been identified, attempt ${attemptNo}...`); } await sleep(pollIntervalMs); diff --git a/src/main.ts b/src/main.ts index 98095cf..6c405a3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,12 +2,9 @@ import * as core from "@actions/core"; import { getConfig } from "./action.ts"; import * as api from "./api.ts"; -import { - getWorkflowRunResult, - handleActionFail, - handleActionSuccess, -} from "./await-remote-run.ts"; +import { getWorkflowRunResult, handleActionFail } from "./await-remote-run.ts"; import * as constants from "./constants.ts"; +import { WorkflowRunConclusion, WorkflowRunStatus } from "./types.ts"; async function main(): Promise { try { @@ -33,15 +30,18 @@ async function main(): Promise { runTimeoutMs: config.runTimeoutSeconds * 1000, }); if (result.success) { - handleActionSuccess(config.runId, result.value.conclusion); + core.info( + "Run Completed:\n" + + ` Run ID: ${config.runId}\n` + + ` Status: ${WorkflowRunStatus.Completed}\n` + + ` Conclusion: ${WorkflowRunConclusion.Success}`, + ); } else { const elapsedTime = Date.now() - startTime; const failureMsg = result.reason === "timeout" ? `Timeout exceeded while attempting to await run conclusion (${elapsedTime}ms)` - : result.reason === "inconclusive" - ? "Run was inconclusive" - : "Unsupported conclusion was returned"; + : `An unsupported value was reached: ${result.value}`; await handleActionFail(failureMsg, config.runId); } } catch (error) { diff --git a/src/types.ts b/src/types.ts index f456d70..5d2ec97 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,31 @@ -export type Result = ResultSuccess | ResultFailure; +/** + * The Status and Conclusion types are difficult to find a reliable source + * of truth for, but this seems accurate from testing: + * https://docs.github.com/en/enterprise-server@3.14/rest/guides/using-the-rest-api-to-interact-with-checks#about-check-suites + */ + +export enum WorkflowRunStatus { + Queued = "queued", + InProgress = "in_progress", + Requested = "requested", + Waiting = "waiting", + Pending = "pending", + Completed = "completed", +} + +export enum WorkflowRunConclusion { + Success = "success", + Failure = "failure", + Neutral = "neutral", + Cancelled = "cancelled", + Skipped = "skipped", + TimedOut = "timed_out", + Stale = "stale", + StartupFailure = "startup_failure", + ActionRequired = "action_required", +} + +export type Result = ResultSuccess | ResultFailure | ResultUnsupported; interface ResultSuccess { success: true; @@ -7,5 +34,33 @@ interface ResultSuccess { interface ResultFailure { success: false; - reason: "timeout" | "inconclusive" | "unsupported"; + reason: "timeout"; +} + +interface ResultUnsupported { + success: false; + reason: "unsupported"; + value: string; +} + +export type WorkflowRunStatusResult = + | ResultSuccess + | ResultStatusPending + | ResultUnsupported; + +interface ResultStatusPending { + success: false; + reason: "pending"; + value: WorkflowRunStatus; +} + +export type WorkflowRunConclusionResult = + | ResultSuccess + | ResultConclusionInconclusive + | ResultUnsupported; + +interface ResultConclusionInconclusive { + success: false; + reason: "inconclusive" | "timeout"; + value: WorkflowRunConclusion; } From fe79e8bdb94973dc6322f5565c5bbc17ef925e05 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 17:40:17 +1300 Subject: [PATCH 23/28] refactor: Request users to open issues if they encounter an unhandled status --- src/await-remote-run.spec.ts | 11 ++++++++--- src/await-remote-run.ts | 3 ++- src/types.ts | 4 ++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/await-remote-run.spec.ts b/src/await-remote-run.spec.ts index 6b772b8..d06106f 100644 --- a/src/await-remote-run.spec.ts +++ b/src/await-remote-run.spec.ts @@ -13,6 +13,7 @@ import { import * as api from "./api.ts"; import { getWorkflowRunConclusionResult, + getWorkflowRunResult, getWorkflowRunStatusResult, handleActionFail, } from "./await-remote-run.ts"; @@ -105,11 +106,15 @@ describe("await-remote-run", () => { expect(result.reason).toStrictEqual("unsupported"); // Logging - assertOnlyCalled(coreDebugLogMock); - expect(coreDebugLogMock).toHaveBeenCalledOnce(); - expect(coreDebugLogMock.mock.lastCall?.[0]).toStrictEqual( + assertOnlyCalled(coreErrorLogMock, coreInfoLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.lastCall?.[0]).toStrictEqual( `Run status is unsupported: ${status}`, ); + expect(coreInfoLogMock).toHaveBeenCalledOnce(); + expect(coreInfoLogMock.mock.lastCall?.[0]).toStrictEqual( + "Please open an issue with this status value", + ); }); }); diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index a847f68..3af8ecc 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -30,7 +30,8 @@ export function getWorkflowRunStatusResult( return { success: false, reason: "pending", value: status }; } - core.debug(`Run status is unsupported: ${status}`); + core.error(`Run status is unsupported: ${status}`); + core.info("Please open an issue with this status value"); return { success: false, reason: "unsupported", value: status ?? "null" }; } diff --git a/src/types.ts b/src/types.ts index 5d2ec97..88831c2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -25,14 +25,14 @@ export enum WorkflowRunConclusion { ActionRequired = "action_required", } -export type Result = ResultSuccess | ResultFailure | ResultUnsupported; +export type Result = ResultSuccess | RequestTimeout | ResultUnsupported; interface ResultSuccess { success: true; value: T; } -interface ResultFailure { +interface RequestTimeout { success: false; reason: "timeout"; } From 0568eeeec6fa77266a61bd8fae76051d1bf4148b Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 17:57:14 +1300 Subject: [PATCH 24/28] refactor: finish handling and testing results for runs --- .../await-remote-run.spec.ts.snap | 94 +++++++ src/await-remote-run.spec.ts | 263 ++++++++++++++++++ src/await-remote-run.ts | 29 +- 3 files changed, 379 insertions(+), 7 deletions(-) diff --git a/src/__snapshots__/await-remote-run.spec.ts.snap b/src/__snapshots__/await-remote-run.spec.ts.snap index 0ad918d..62ce46e 100644 --- a/src/__snapshots__/await-remote-run.spec.ts.snap +++ b/src/__snapshots__/await-remote-run.spec.ts.snap @@ -1,5 +1,99 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`await-remote-run > getWorkflowRunResult > retries on request failures 1`] = ` +[ + [ + "Failed to fetch run state, attempt 1...", + ], + [ + "Failed to fetch run state, attempt 2...", + ], +] +`; + +exports[`await-remote-run > getWorkflowRunResult > returns a failure if the status is unsupported 1`] = ` +[ + [ + "Run status is unsupported: weird", + ], +] +`; + +exports[`await-remote-run > getWorkflowRunResult > returns a failure if the status is unsupported 2`] = ` +[ + [ + "Please open an issue with this status value", + ], +] +`; + +exports[`await-remote-run > getWorkflowRunResult > returns a failure on an unsupported conclusion 1`] = ` +[ + [ + "Run has failed with unsupported conclusion: weird", + ], +] +`; + +exports[`await-remote-run > getWorkflowRunResult > returns a failure on an unsupported conclusion 2`] = ` +[ + [ + "Please open an issue with this conclusion value", + ], +] +`; + +exports[`await-remote-run > getWorkflowRunResult > returns a failure on timeout conclusion 1`] = ` +[ + [ + "Run has timeout out", + ], +] +`; + +exports[`await-remote-run > getWorkflowRunResult > returns a timeout 1`] = ` +[ + [ + "Run is in progress, attempt 1...", + ], + [ + "Run is in progress, attempt 2...", + ], + [ + "Run is in progress, attempt 3...", + ], + [ + "Run is in progress, attempt 4...", + ], + [ + "Run is in progress, attempt 5...", + ], + [ + "Run is in progress, attempt 6...", + ], + [ + "Run is in progress, attempt 7...", + ], + [ + "Run is in progress, attempt 8...", + ], + [ + "Run is in progress, attempt 9...", + ], + [ + "Run is in progress, attempt 10...", + ], +] +`; + +exports[`await-remote-run > getWorkflowRunResult > returns the conclusion if available 1`] = ` +[ + [ + "Run has failed with conclusion: skipped", + ], +] +`; + exports[`await-remote-run > handleActionFail > should fetch and log failed jobs from the remote run 2`] = ` "Job First Job: ID: 0 diff --git a/src/await-remote-run.spec.ts b/src/await-remote-run.spec.ts index d06106f..c8c5839 100644 --- a/src/await-remote-run.spec.ts +++ b/src/await-remote-run.spec.ts @@ -328,4 +328,267 @@ describe("await-remote-run", () => { expect(coreErrorLogMock.mock.calls[1]?.[0]).toMatchSnapshot(); }); }); + + describe("getWorkflowRunResult", () => { + let apiFetchWorkflowRunStateMock: MockInstance< + typeof api.fetchWorkflowRunState + >; + let apiRetryOnErrorMock: MockInstance; + + beforeEach(() => { + vi.useFakeTimers(); + + apiFetchWorkflowRunStateMock = vi.spyOn(api, "fetchWorkflowRunState"); + apiRetryOnErrorMock = vi.spyOn(api, "retryOnError"); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("succeeds on the completion of a run", async () => { + apiFetchWorkflowRunStateMock.mockResolvedValue({ + status: WorkflowRunStatus.Completed, + conclusion: WorkflowRunConclusion.Success, + }); + apiRetryOnErrorMock.mockImplementation(async (toTry) => ({ + success: true, + value: await toTry(), + })); + + // Behaviour + const getWorkflowRunResultPromise = getWorkflowRunResult({ + startTime: Date.now(), + pollIntervalMs: 100, + runId: 0, + runTimeoutMs: 10_000, + }); + await expect(getWorkflowRunResultPromise).resolves.not.toThrow(); + const result = await getWorkflowRunResultPromise; + expect(result).toStrictEqual({ + success: true, + value: { + conclusion: WorkflowRunConclusion.Success, + status: WorkflowRunStatus.Completed, + }, + }); + + // Logging + assertNoneCalled(); + }); + + it("retries on request failures", async () => { + const pollIntervalMs = 100; + apiFetchWorkflowRunStateMock.mockResolvedValue({ + status: WorkflowRunStatus.Completed, + conclusion: WorkflowRunConclusion.Success, + }); + apiRetryOnErrorMock + .mockImplementation(async (toTry) => ({ + success: true, + value: await toTry(), + })) + .mockResolvedValueOnce({ success: false, reason: "timeout" }) + .mockResolvedValueOnce({ success: false, reason: "timeout" }); + + // Behaviour + const getWorkflowRunResultPromise = getWorkflowRunResult({ + startTime: Date.now(), + pollIntervalMs: pollIntervalMs, + runId: 0, + runTimeoutMs: 10_000, + }); + + // First iteration + await vi.advanceTimersByTimeAsync(1); + expect(coreDebugLogMock).toHaveBeenCalledOnce(); + + // Second iteration + await vi.advanceTimersByTimeAsync(100); + expect(coreDebugLogMock).toHaveBeenCalledTimes(2); + + // Final iteration + await vi.advanceTimersByTimeAsync(100); + await expect(getWorkflowRunResultPromise).resolves.not.toThrow(); + const result = await getWorkflowRunResultPromise; + expect(result).toStrictEqual({ + success: true, + value: { + conclusion: WorkflowRunConclusion.Success, + status: WorkflowRunStatus.Completed, + }, + }); + + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toBeCalledTimes(2); + expect(coreDebugLogMock.mock.calls).toMatchSnapshot(); + }); + + it("returns the conclusion if available", async () => { + const expectedConclusion = WorkflowRunConclusion.Skipped; + apiFetchWorkflowRunStateMock.mockResolvedValue({ + status: WorkflowRunStatus.Completed, + conclusion: expectedConclusion, + }); + apiRetryOnErrorMock.mockImplementation(async (toTry) => ({ + success: true, + value: await toTry(), + })); + + // Behaviour + const getWorkflowRunResultPromise = getWorkflowRunResult({ + startTime: Date.now(), + pollIntervalMs: 100, + runId: 0, + runTimeoutMs: 10_000, + }); + await expect(getWorkflowRunResultPromise).resolves.not.toThrow(); + const result = await getWorkflowRunResultPromise; + expect(result).toStrictEqual({ + success: true, + value: { + conclusion: expectedConclusion, + status: WorkflowRunStatus.Completed, + }, + }); + + // Logging + assertOnlyCalled(coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.calls).toMatchSnapshot(); + }); + + it("returns a failure on timeout conclusion", async () => { + const expectedConclusion = WorkflowRunConclusion.TimedOut; + apiFetchWorkflowRunStateMock.mockResolvedValue({ + status: WorkflowRunStatus.Completed, + conclusion: expectedConclusion, + }); + apiRetryOnErrorMock.mockImplementation(async (toTry) => ({ + success: true, + value: await toTry(), + })); + + // Behaviour + const getWorkflowRunResultPromise = getWorkflowRunResult({ + startTime: Date.now(), + pollIntervalMs: 100, + runId: 0, + runTimeoutMs: 10_000, + }); + await expect(getWorkflowRunResultPromise).resolves.not.toThrow(); + const result = await getWorkflowRunResultPromise; + expect(result).toStrictEqual({ + success: false, + reason: "timeout", + }); + + // Logging + assertOnlyCalled(coreErrorLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.calls).toMatchSnapshot(); + }); + + it("returns a failure on an unsupported conclusion", async () => { + const expectedConclusion = "weird"; + apiFetchWorkflowRunStateMock.mockResolvedValue({ + status: WorkflowRunStatus.Completed, + conclusion: expectedConclusion as any, + }); + apiRetryOnErrorMock.mockImplementation(async (toTry) => ({ + success: true, + value: await toTry(), + })); + + // Behaviour + const getWorkflowRunResultPromise = getWorkflowRunResult({ + startTime: Date.now(), + pollIntervalMs: 100, + runId: 0, + runTimeoutMs: 10_000, + }); + await expect(getWorkflowRunResultPromise).resolves.not.toThrow(); + const result = await getWorkflowRunResultPromise; + expect(result).toStrictEqual({ + success: false, + reason: "unsupported", + value: expectedConclusion, + }); + + // Logging + assertOnlyCalled(coreErrorLogMock, coreInfoLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.calls).toMatchSnapshot(); + expect(coreInfoLogMock).toHaveBeenCalledOnce(); + expect(coreInfoLogMock.mock.calls).toMatchSnapshot(); + }); + + it("returns a failure if the status is unsupported", async () => { + const expectedStatus = "weird"; + apiFetchWorkflowRunStateMock.mockResolvedValue({ + status: expectedStatus as any, + conclusion: WorkflowRunConclusion.Failure, + }); + apiRetryOnErrorMock.mockImplementation(async (toTry) => ({ + success: true, + value: await toTry(), + })); + + // Behaviour + const getWorkflowRunResultPromise = getWorkflowRunResult({ + startTime: Date.now(), + pollIntervalMs: 100, + runId: 0, + runTimeoutMs: 10_000, + }); + await expect(getWorkflowRunResultPromise).resolves.not.toThrow(); + const result = await getWorkflowRunResultPromise; + expect(result).toStrictEqual({ + success: false, + reason: "unsupported", + value: "weird", + }); + + // Logging + assertOnlyCalled(coreErrorLogMock, coreInfoLogMock); + expect(coreErrorLogMock).toHaveBeenCalledOnce(); + expect(coreErrorLogMock.mock.calls).toMatchSnapshot(); + expect(coreInfoLogMock).toHaveBeenCalledOnce(); + expect(coreInfoLogMock.mock.calls).toMatchSnapshot(); + }); + + it("returns a timeout", async () => { + const pollIntervalMs = 100; + const runTimeoutMs = 1000; + const expectedIterations = runTimeoutMs / pollIntervalMs; + apiFetchWorkflowRunStateMock.mockResolvedValue({ + status: WorkflowRunStatus.InProgress, + conclusion: null, + }); + apiRetryOnErrorMock.mockImplementation(async (toTry) => ({ + success: true, + value: await toTry(), + })); + + // Behaviour + const getWorkflowRunResultPromise = getWorkflowRunResult({ + startTime: Date.now(), + pollIntervalMs: pollIntervalMs, + runId: 0, + runTimeoutMs: runTimeoutMs, + }); + await vi.advanceTimersByTimeAsync(1000); + await expect(getWorkflowRunResultPromise).resolves.not.toThrow(); + const result = await getWorkflowRunResultPromise; + expect(result).toStrictEqual({ + success: false, + reason: "timeout", + }); + + // Logging + assertOnlyCalled(coreDebugLogMock); + expect(coreDebugLogMock).toHaveBeenCalledTimes(expectedIterations); + expect(coreDebugLogMock.mock.calls).toMatchSnapshot(); + }); + }); }); diff --git a/src/await-remote-run.ts b/src/await-remote-run.ts index 3af8ecc..13fe786 100644 --- a/src/await-remote-run.ts +++ b/src/await-remote-run.ts @@ -128,15 +128,30 @@ export async function getWorkflowRunResult({ if (statusResult.success) { // We only get a conclusion should the status resolve, otherwise it is null. const conclusionResult = getWorkflowRunConclusionResult(conclusion); + if ( + conclusionResult.success || + conclusionResult.reason === "inconclusive" + ) { + return { + success: true, + value: { + status: statusResult.value, + conclusion: conclusionResult.value, + }, + }; + } + + if (conclusionResult.reason === "timeout") { + return { + success: false, + reason: "timeout", + }; + } return { - success: true, - value: { - status: statusResult.value, - conclusion: conclusionResult.success - ? conclusionResult.value - : undefined, - }, + success: false, + reason: "unsupported", + value: conclusionResult.value, }; } From ca18e0760103132913b083c9fb6740524e33dfb1 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 18:01:32 +1300 Subject: [PATCH 25/28] refactor: main should fail for non-success conclusions --- src/main.ts | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main.ts b/src/main.ts index 6c405a3..e935bfa 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,7 @@ import { getConfig } from "./action.ts"; import * as api from "./api.ts"; import { getWorkflowRunResult, handleActionFail } from "./await-remote-run.ts"; import * as constants from "./constants.ts"; -import { WorkflowRunConclusion, WorkflowRunStatus } from "./types.ts"; +import { WorkflowRunConclusion } from "./types.ts"; async function main(): Promise { try { @@ -29,21 +29,30 @@ async function main(): Promise { runId: config.runId, runTimeoutMs: config.runTimeoutSeconds * 1000, }); - if (result.success) { - core.info( - "Run Completed:\n" + - ` Run ID: ${config.runId}\n` + - ` Status: ${WorkflowRunStatus.Completed}\n` + - ` Conclusion: ${WorkflowRunConclusion.Success}`, - ); - } else { + if (!result.success) { const elapsedTime = Date.now() - startTime; const failureMsg = result.reason === "timeout" ? `Timeout exceeded while attempting to await run conclusion (${elapsedTime}ms)` : `An unsupported value was reached: ${result.value}`; await handleActionFail(failureMsg, config.runId); + return; + } + + const { status, conclusion } = result.value; + if (conclusion === WorkflowRunConclusion.Success) { + core.info( + "Run Completed:\n" + + ` Run ID: ${config.runId}\n` + + ` Status: ${status}\n` + + ` Conclusion: ${conclusion}`, + ); } + + await handleActionFail( + `Run has concluded with ${conclusion}`, + config.runId, + ); } catch (error) { if (error instanceof Error) { const failureMsg = `Failed: An unhandled error has occurred: ${error.message}`; From a4a6a867f9b91ecdea8e5824d13960b93e20d6d6 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 18:06:07 +1300 Subject: [PATCH 26/28] chore: bump dependencies --- package.json | 20 +- pnpm-lock.yaml | 881 ++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 705 insertions(+), 196 deletions(-) diff --git a/package.json b/package.json index 8527586..e109de2 100644 --- a/package.json +++ b/package.json @@ -32,29 +32,29 @@ "@actions/github": "^6.0.0" }, "devDependencies": { - "@eslint/compat": "^1.2.8", + "@eslint/compat": "^1.2.9", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.25.1", + "@eslint/js": "^9.26.0", "@opentf/std": "^0.13.0", "@total-typescript/ts-reset": "^0.6.1", "@types/eslint__js": "^9.14.0", "@types/node": "^20.17.32", - "@typescript-eslint/eslint-plugin": "^8.31.1", - "@typescript-eslint/parser": "^8.31.1", - "@vitest/coverage-v8": "^3.1.2", + "@typescript-eslint/eslint-plugin": "^8.32.0", + "@typescript-eslint/parser": "^8.32.0", + "@vitest/coverage-v8": "^3.1.3", "chalk": "^5.4.1", "changelogithub": "^13.13.0", "esbuild": "^0.25.3", - "eslint": "^9.25.1", + "eslint": "^9.26.0", "eslint-config-prettier": "^10.1.2", - "eslint-import-resolver-typescript": "^3.10.1", - "eslint-plugin-github": "^5.1.8", + "eslint-import-resolver-typescript": "^4.3.4", + "eslint-plugin-github": "^6.0.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-import-x": "^4.11.0", "knip": "^5.53.0", "prettier": "3.5.3", "typescript": "^5.8.3", - "typescript-eslint": "^8.31.1", - "vitest": "^3.1.2" + "typescript-eslint": "^8.32.0", + "vitest": "^3.1.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a197ad8..000c531 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,14 +16,14 @@ importers: version: 6.0.0 devDependencies: '@eslint/compat': - specifier: ^1.2.8 - version: 1.2.8(eslint@9.25.1(jiti@2.4.2)) + specifier: ^1.2.9 + version: 1.2.9(eslint@9.26.0(jiti@2.4.2)) '@eslint/eslintrc': specifier: ^3.3.1 version: 3.3.1 '@eslint/js': - specifier: ^9.25.1 - version: 9.25.1 + specifier: ^9.26.0 + version: 9.26.0 '@opentf/std': specifier: ^0.13.0 version: 0.13.0 @@ -37,14 +37,14 @@ importers: specifier: ^20.17.32 version: 20.17.32 '@typescript-eslint/eslint-plugin': - specifier: ^8.31.1 - version: 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + specifier: ^8.32.0 + version: 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/parser': - specifier: ^8.31.1 - version: 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + specifier: ^8.32.0 + version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) '@vitest/coverage-v8': - specifier: ^3.1.2 - version: 3.1.2(vitest@3.1.2(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1)) + specifier: ^3.1.3 + version: 3.1.3(vitest@3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1)) chalk: specifier: ^5.4.1 version: 5.4.1 @@ -55,23 +55,23 @@ importers: specifier: ^0.25.3 version: 0.25.3 eslint: - specifier: ^9.25.1 - version: 9.25.1(jiti@2.4.2) + specifier: ^9.26.0 + version: 9.26.0(jiti@2.4.2) eslint-config-prettier: specifier: ^10.1.2 - version: 10.1.2(eslint@9.25.1(jiti@2.4.2)) + version: 10.1.2(eslint@9.26.0(jiti@2.4.2)) eslint-import-resolver-typescript: - specifier: ^3.10.1 - version: 3.10.1(eslint-plugin-import-x@4.11.0(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.25.1(jiti@2.4.2)) + specifier: ^4.3.4 + version: 4.3.4(eslint-plugin-import-x@4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-github: - specifier: ^5.1.8 - version: 5.1.8(@types/eslint@9.6.1)(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + specifier: ^6.0.0 + version: 6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-import: specifier: ^2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)) + version: 2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-import-x: specifier: ^4.11.0 - version: 4.11.0(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + version: 4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) knip: specifier: ^5.53.0 version: 5.53.0(@types/node@20.17.32)(typescript@5.8.3) @@ -82,11 +82,11 @@ importers: specifier: ^5.8.3 version: 5.8.3 typescript-eslint: - specifier: ^8.31.1 - version: 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + specifier: ^8.32.0 + version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) vitest: - specifier: ^3.1.2 - version: 3.1.2(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) + specifier: ^3.1.3 + version: 3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) packages: @@ -295,12 +295,18 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.2.8': - resolution: {integrity: sha512-LqCYHdWL/QqKIJuZ/ucMAv8d4luKGs4oCPgpt8mWztQAtPrHfXKQ/XAUc8ljCHAfJCn6SvkpTcGt5Tsh8saowA==} + '@eslint/compat@1.2.9': + resolution: {integrity: sha512-gCdSY54n7k+driCadyMNv8JSPzYLeDVM/ikZRtvtROBpRdFSkS8W9A82MqsaY7lZuwL0wiapgD0NT1xT0hyJsA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.10.0 @@ -324,8 +330,8 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.25.1': - resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==} + '@eslint/js@9.26.0': + resolution: {integrity: sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': @@ -389,6 +395,10 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@modelcontextprotocol/sdk@1.11.0': + resolution: {integrity: sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==} + engines: {node: '>=18'} + '@napi-rs/wasm-runtime@0.2.9': resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} @@ -404,10 +414,6 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@nolyfill/is-core-module@1.0.39': - resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} - engines: {node: '>=12.4.0'} - '@octokit/auth-token@4.0.0': resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} engines: {node: '>= 18'} @@ -603,16 +609,16 @@ packages: '@types/node@20.17.32': resolution: {integrity: sha512-zeMXFn8zQ+UkjK4ws0RiOC9EWByyW1CcVmLe+2rQocXRsGEDxUCwPEIVgpsGcLHS/P8JkT0oa3839BRABS0oPw==} - '@typescript-eslint/eslint-plugin@8.31.1': - resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==} + '@typescript-eslint/eslint-plugin@8.32.0': + resolution: {integrity: sha512-/jU9ettcntkBFmWUzzGgsClEi2ZFiikMX5eEQsmxIAWMOn4H3D4rvHssstmAHGVvrYnaMqdWWWg0b5M6IN/MTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.31.1': - resolution: {integrity: sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==} + '@typescript-eslint/parser@8.32.0': + resolution: {integrity: sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -622,8 +628,12 @@ packages: resolution: {integrity: sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.31.1': - resolution: {integrity: sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==} + '@typescript-eslint/scope-manager@8.32.0': + resolution: {integrity: sha512-jc/4IxGNedXkmG4mx4nJTILb6TMjL66D41vyeaPWvDUmeYQzF3lKtN15WsAeTr65ce4mPxwopPSo1yUUAWw0hQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.32.0': + resolution: {integrity: sha512-t2vouuYQKEKSLtJaa5bB4jHeha2HJczQ6E5IXPDPgIty9EqcJxpr1QHQ86YyIPwDwxvUmLfP2YADQ5ZY4qddZg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -633,12 +643,22 @@ packages: resolution: {integrity: sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.32.0': + resolution: {integrity: sha512-O5Id6tGadAZEMThM6L9HmVf5hQUXNSxLVKeGJYWNhhVseps/0LddMkp7//VDkzwJ69lPL0UmZdcZwggj9akJaA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.31.1': resolution: {integrity: sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/typescript-estree@8.32.0': + resolution: {integrity: sha512-pU9VD7anSCOIoBFnhTGfOzlVFQIA1XXiQpH/CezqOBaDppRwTglJzCC6fUQGpfwey4T183NKhF1/mfatYmjRqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.31.1': resolution: {integrity: sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -646,10 +666,21 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.32.0': + resolution: {integrity: sha512-8S9hXau6nQ/sYVtC3D6ISIDoJzS1NsCK+gluVhLN2YkBPX+/1wkwyUiDKnxRh15579WoOIyVWnoyIf3yGI9REw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/visitor-keys@8.31.1': resolution: {integrity: sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.32.0': + resolution: {integrity: sha512-1rYQTCLFFzOI5Nl0c8LUpJT8HxpwVRn9E4CkMsYfuN6ctmQqExjSTzzSk0Tz2apmXy7WU6/6fyaZVVA/thPN+w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@unrs/resolver-binding-darwin-arm64@1.7.2': resolution: {integrity: sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==} cpu: [arm64] @@ -735,20 +766,20 @@ packages: cpu: [x64] os: [win32] - '@vitest/coverage-v8@3.1.2': - resolution: {integrity: sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ==} + '@vitest/coverage-v8@3.1.3': + resolution: {integrity: sha512-cj76U5gXCl3g88KSnf80kof6+6w+K4BjOflCl7t6yRJPDuCrHtVu0SgNYOUARJOL5TI8RScDbm5x4s1/P9bvpw==} peerDependencies: - '@vitest/browser': 3.1.2 - vitest: 3.1.2 + '@vitest/browser': 3.1.3 + vitest: 3.1.3 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.1.2': - resolution: {integrity: sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==} + '@vitest/expect@3.1.3': + resolution: {integrity: sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==} - '@vitest/mocker@3.1.2': - resolution: {integrity: sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==} + '@vitest/mocker@3.1.3': + resolution: {integrity: sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -758,20 +789,24 @@ packages: vite: optional: true - '@vitest/pretty-format@3.1.2': - resolution: {integrity: sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==} + '@vitest/pretty-format@3.1.3': + resolution: {integrity: sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==} - '@vitest/runner@3.1.2': - resolution: {integrity: sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==} + '@vitest/runner@3.1.3': + resolution: {integrity: sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==} - '@vitest/snapshot@3.1.2': - resolution: {integrity: sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==} + '@vitest/snapshot@3.1.3': + resolution: {integrity: sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==} - '@vitest/spy@3.1.2': - resolution: {integrity: sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==} + '@vitest/spy@3.1.3': + resolution: {integrity: sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==} - '@vitest/utils@3.1.2': - resolution: {integrity: sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==} + '@vitest/utils@3.1.3': + resolution: {integrity: sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -874,6 +909,10 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -893,6 +932,10 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + c12@1.11.2: resolution: {integrity: sha512-oBs8a4uvSDO9dm8b7OCFW7+dgtVrwmwnrVXYzLm43ta7ep2jCn/0MhoUFygIWtxhyy6+/MG7/agvpY0U1Iemew==} peerDependencies: @@ -999,9 +1042,29 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-gitmoji@0.1.5: resolution: {integrity: sha512-4wqOafJdk2tqZC++cjcbGcaJ13BZ3kwldf06PTiAQRAB76Z1KJwZNL1SaRZMi2w1FM9RYTgZ6QErS8NUl/GBmQ==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1068,6 +1131,10 @@ packages: defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + deprecation@2.3.1: resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} @@ -1089,6 +1156,9 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.129: resolution: {integrity: sha512-JlXUemX4s0+9f8mLqib/bHH8gOHf5elKS6KeWG3sk3xozb/JTq/RLXIv8OKUWiK4Ah00Wm88EFj5PYkFr4RUPA==} @@ -1098,6 +1168,10 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + enhanced-resolve@5.18.1: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} @@ -1114,8 +1188,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@1.6.0: - resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} @@ -1142,6 +1216,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -1159,9 +1236,9 @@ packages: eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - eslint-import-resolver-typescript@3.10.1: - resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} - engines: {node: ^14.18.0 || >=16.0.0} + eslint-import-resolver-typescript@4.3.4: + resolution: {integrity: sha512-buzw5z5VtiQMysYLH9iW9BV04YyZebsw+gPi+c4FCjfS9i6COYOrEWw9t3m3wA9PFBfqcBCqWf32qrXLbwafDw==} + engines: {node: ^16.17.0 || >=18.6.0} peerDependencies: eslint: '*' eslint-plugin-import: '*' @@ -1209,8 +1286,8 @@ packages: peerDependencies: eslint: '*' - eslint-plugin-github@5.1.8: - resolution: {integrity: sha512-A6q+R3EBMF7hxIViWpQsalqpu3O0POcQ9VpN1m9W2I8yGumw+SFxXZUTafBd9X9mgUJhaU4M9qSifC1q/39H3A==} + eslint-plugin-github@6.0.0: + resolution: {integrity: sha512-J8MvUoiR/TU/Y9NnEmg1AnbvMUj9R6IO260z47zymMLLvso7B4c80IKjd8diqmqtSmeXXlbIus4i0SvK84flag==} hasBin: true peerDependencies: eslint: ^8 || ^9 @@ -1276,8 +1353,8 @@ packages: resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.25.1: - resolution: {integrity: sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==} + eslint@9.26.0: + resolution: {integrity: sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1309,6 +1386,18 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.0.1: + resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.6: + resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==} + engines: {node: '>=18.0.0'} + execa@9.5.2: resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} @@ -1317,6 +1406,16 @@ packages: resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} + express-rate-limit@7.5.0: + resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + engines: {node: '>= 16'} + peerDependencies: + express: ^4.11 || 5 || ^5.0.0-beta.1 + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + exsolve@1.0.4: resolution: {integrity: sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw==} @@ -1359,6 +1458,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1378,6 +1481,14 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -1440,8 +1551,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + globals@16.0.0: + resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} engines: {node: '>=18'} globalthis@1.0.4: @@ -1488,10 +1599,18 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + human-signals@8.0.1: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1504,10 +1623,17 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -1593,6 +1719,9 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -1756,6 +1885,14 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1764,6 +1901,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + minimatch@10.0.1: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} @@ -1822,6 +1967,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + node-fetch-native@1.6.6: resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} @@ -1846,6 +1995,10 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -1879,6 +2032,10 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -1913,6 +2070,10 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1932,6 +2093,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -1956,6 +2121,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -1987,13 +2156,29 @@ packages: resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} engines: {node: '>=18'} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -2034,6 +2219,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + run-applescript@7.0.0: resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} engines: {node: '>=18'} @@ -2045,6 +2234,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} @@ -2053,6 +2245,9 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} @@ -2065,6 +2260,14 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -2077,6 +2280,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2122,6 +2328,10 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + std-env@3.8.1: resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==} @@ -2229,6 +2439,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -2249,6 +2463,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -2265,8 +2483,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.31.1: - resolution: {integrity: sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==} + typescript-eslint@8.32.0: + resolution: {integrity: sha512-UMq2kxdXCzinFFPsXc9o2ozIpYCCOiEC46MG3yEh5Vipq6BO27otTtEBZA1fQ66DulEUgE97ucQ/3YY66CPg0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2298,6 +2516,10 @@ packages: universal-user-agent@6.0.1: resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + unrs-resolver@1.7.2: resolution: {integrity: sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==} @@ -2310,8 +2532,12 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - vite-node@3.1.2: - resolution: {integrity: sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@3.1.3: + resolution: {integrity: sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -2355,16 +2581,16 @@ packages: yaml: optional: true - vitest@3.1.2: - resolution: {integrity: sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==} + vitest@3.1.3: + resolution: {integrity: sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.1.2 - '@vitest/ui': 3.1.2 + '@vitest/browser': 3.1.3 + '@vitest/ui': 3.1.3 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2440,6 +2666,11 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} + zod-to-json-schema@3.24.5: + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + zod-validation-error@3.4.0: resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==} engines: {node: '>=18.0.0'} @@ -2585,16 +2816,21 @@ snapshots: '@esbuild/win32-x64@0.25.3': optional: true - '@eslint-community/eslint-utils@4.5.1(eslint@9.25.1(jiti@2.4.2))': + '@eslint-community/eslint-utils@4.5.1(eslint@9.26.0(jiti@2.4.2))': + dependencies: + eslint: 9.26.0(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.7.0(eslint@9.26.0(jiti@2.4.2))': dependencies: - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.2.8(eslint@9.25.1(jiti@2.4.2))': + '@eslint/compat@1.2.9(eslint@9.26.0(jiti@2.4.2))': optionalDependencies: - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) '@eslint/config-array@0.20.0': dependencies: @@ -2624,7 +2860,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.25.1': {} + '@eslint/js@9.26.0': {} '@eslint/object-schema@2.1.6': {} @@ -2678,6 +2914,21 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@modelcontextprotocol/sdk@1.11.0': + dependencies: + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.6 + express: 5.1.0 + express-rate-limit: 7.5.0(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.24.4 + zod-to-json-schema: 3.24.5(zod@3.24.4) + transitivePeerDependencies: + - supports-color + '@napi-rs/wasm-runtime@0.2.9': dependencies: '@emnapi/core': 1.4.0 @@ -2697,8 +2948,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@nolyfill/is-core-module@1.0.39': {} - '@octokit/auth-token@4.0.0': {} '@octokit/core@5.2.1': @@ -2845,7 +3094,7 @@ snapshots: '@types/eslint__js@9.14.0': dependencies: - '@eslint/js': 9.25.1 + '@eslint/js': 9.26.0 '@types/estree@1.0.7': {} @@ -2857,15 +3106,15 @@ snapshots: dependencies: undici-types: 6.19.8 - '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/type-utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 - eslint: 9.25.1(jiti@2.4.2) + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/type-utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.0 + eslint: 9.26.0(jiti@2.4.2) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -2874,14 +3123,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.31.1 - '@typescript-eslint/types': 8.31.1 - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.31.1 + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.32.0 debug: 4.4.0 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -2891,12 +3140,17 @@ snapshots: '@typescript-eslint/types': 8.31.1 '@typescript-eslint/visitor-keys': 8.31.1 - '@typescript-eslint/type-utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/scope-manager@8.32.0': dependencies: - '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/visitor-keys': 8.32.0 + + '@typescript-eslint/type-utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) debug: 4.4.0 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -2904,6 +3158,8 @@ snapshots: '@typescript-eslint/types@8.31.1': {} + '@typescript-eslint/types@8.32.0': {} + '@typescript-eslint/typescript-estree@8.31.1(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 8.31.1 @@ -2918,13 +3174,38 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.32.0(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.25.1(jiti@2.4.2)) + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/visitor-keys': 8.32.0 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.5.1(eslint@9.26.0(jiti@2.4.2)) '@typescript-eslint/scope-manager': 8.31.1 '@typescript-eslint/types': 8.31.1 '@typescript-eslint/typescript-estree': 8.31.1(typescript@5.8.3) - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.26.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.32.0 + '@typescript-eslint/types': 8.32.0 + '@typescript-eslint/typescript-estree': 8.32.0(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -2934,6 +3215,11 @@ snapshots: '@typescript-eslint/types': 8.31.1 eslint-visitor-keys: 4.2.0 + '@typescript-eslint/visitor-keys@8.32.0': + dependencies: + '@typescript-eslint/types': 8.32.0 + eslint-visitor-keys: 4.2.0 + '@unrs/resolver-binding-darwin-arm64@1.7.2': optional: true @@ -2987,7 +3273,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.7.2': optional: true - '@vitest/coverage-v8@3.1.2(vitest@3.1.2(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1))': + '@vitest/coverage-v8@3.1.3(vitest@3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 @@ -3001,50 +3287,55 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.1.2(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) + vitest: 3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) transitivePeerDependencies: - supports-color - '@vitest/expect@3.1.2': + '@vitest/expect@3.1.3': dependencies: - '@vitest/spy': 3.1.2 - '@vitest/utils': 3.1.2 + '@vitest/spy': 3.1.3 + '@vitest/utils': 3.1.3 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.2(vite@6.2.4(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1))': + '@vitest/mocker@3.1.3(vite@6.2.4(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1))': dependencies: - '@vitest/spy': 3.1.2 + '@vitest/spy': 3.1.3 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: vite: 6.2.4(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) - '@vitest/pretty-format@3.1.2': + '@vitest/pretty-format@3.1.3': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.1.2': + '@vitest/runner@3.1.3': dependencies: - '@vitest/utils': 3.1.2 + '@vitest/utils': 3.1.3 pathe: 2.0.3 - '@vitest/snapshot@3.1.2': + '@vitest/snapshot@3.1.3': dependencies: - '@vitest/pretty-format': 3.1.2 + '@vitest/pretty-format': 3.1.3 magic-string: 0.30.17 pathe: 2.0.3 - '@vitest/spy@3.1.2': + '@vitest/spy@3.1.3': dependencies: tinyspy: 3.0.2 - '@vitest/utils@3.1.2': + '@vitest/utils@3.1.3': dependencies: - '@vitest/pretty-format': 3.1.2 + '@vitest/pretty-format': 3.1.3 loupe: 3.1.3 tinyrainbow: 2.0.0 + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.14.1): dependencies: acorn: 8.14.1 @@ -3147,6 +3438,20 @@ snapshots: binary-extensions@2.3.0: {} + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.0 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -3171,6 +3476,8 @@ snapshots: dependencies: run-applescript: 7.0.0 + bytes@3.1.2: {} + c12@1.11.2(magicast@0.3.5): dependencies: chokidar: 3.6.0 @@ -3317,8 +3624,23 @@ snapshots: consola@3.4.2: {} + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + convert-gitmoji@0.1.5: {} + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3380,6 +3702,8 @@ snapshots: defu@6.1.4: {} + depd@2.0.0: {} + deprecation@2.3.1: {} destr@2.0.3: {} @@ -3398,12 +3722,16 @@ snapshots: eastasianwidth@0.2.0: {} + ee-first@1.1.1: {} + electron-to-chromium@1.5.129: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} + encodeurl@2.0.0: {} + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 @@ -3467,7 +3795,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@1.6.0: {} + es-module-lexer@1.7.0: {} es-object-atoms@1.1.1: dependencies: @@ -3520,13 +3848,15 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.2(eslint@9.25.1(jiti@2.4.2)): + eslint-config-prettier@10.1.2(eslint@9.26.0(jiti@2.4.2)): dependencies: - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) eslint-import-resolver-node@0.3.9: dependencies: @@ -3536,94 +3866,93 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.11.0(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.25.1(jiti@2.4.2)): + eslint-import-resolver-typescript@4.3.4(eslint-plugin-import-x@4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)): dependencies: - '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) get-tsconfig: 4.10.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.13 unrs-resolver: 1.7.2 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)) - eslint-plugin-import-x: 4.11.0(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-import-x: 4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.25.1(jiti@2.4.2) + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.11.0(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.25.1(jiti@2.4.2)) + eslint-import-resolver-typescript: 4.3.4(eslint-plugin-import-x@4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - eslint-plugin-escompat@3.11.4(eslint@9.25.1(jiti@2.4.2)): + eslint-plugin-escompat@3.11.4(eslint@9.26.0(jiti@2.4.2)): dependencies: browserslist: 4.24.4 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) - eslint-plugin-eslint-comments@3.2.0(eslint@9.25.1(jiti@2.4.2)): + eslint-plugin-eslint-comments@3.2.0(eslint@9.26.0(jiti@2.4.2)): dependencies: escape-string-regexp: 1.0.5 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) ignore: 5.3.2 - eslint-plugin-filenames@1.3.2(eslint@9.25.1(jiti@2.4.2)): + eslint-plugin-filenames@1.3.2(eslint@9.26.0(jiti@2.4.2)): dependencies: - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) lodash.camelcase: 4.3.0 lodash.kebabcase: 4.1.1 lodash.snakecase: 4.1.1 lodash.upperfirst: 4.3.1 - eslint-plugin-github@5.1.8(@types/eslint@9.6.1)(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3): + eslint-plugin-github@6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)): dependencies: - '@eslint/compat': 1.2.8(eslint@9.25.1(jiti@2.4.2)) + '@eslint/compat': 1.2.9(eslint@9.26.0(jiti@2.4.2)) '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.25.1 + '@eslint/js': 9.26.0 '@github/browserslist-config': 1.0.0 - '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) aria-query: 5.3.2 - eslint: 9.25.1(jiti@2.4.2) - eslint-config-prettier: 10.1.2(eslint@9.25.1(jiti@2.4.2)) - eslint-plugin-escompat: 3.11.4(eslint@9.25.1(jiti@2.4.2)) - eslint-plugin-eslint-comments: 3.2.0(eslint@9.25.1(jiti@2.4.2)) - eslint-plugin-filenames: 1.3.2(eslint@9.25.1(jiti@2.4.2)) - eslint-plugin-i18n-text: 1.0.1(eslint@9.25.1(jiti@2.4.2)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.25.1(jiti@2.4.2)) + eslint: 9.26.0(jiti@2.4.2) + eslint-config-prettier: 10.1.2(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-escompat: 3.11.4(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-eslint-comments: 3.2.0(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-filenames: 1.3.2(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-i18n-text: 1.0.1(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-prettier: 5.2.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.2(eslint@9.25.1(jiti@2.4.2)))(eslint@9.25.1(jiti@2.4.2))(prettier@3.5.3) + eslint-plugin-prettier: 5.2.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(prettier@3.5.3) eslint-rule-documentation: 1.0.23 - globals: 15.15.0 + globals: 16.0.0 jsx-ast-utils: 3.3.5 prettier: 3.5.3 svg-element-attributes: 1.3.1 - typescript-eslint: 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + typescript: 5.8.3 + typescript-eslint: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) transitivePeerDependencies: - '@types/eslint' - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - - typescript - eslint-plugin-i18n-text@1.0.1(eslint@9.25.1(jiti@2.4.2)): + eslint-plugin-i18n-text@1.0.1(eslint@9.26.0(jiti@2.4.2)): dependencies: - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) - eslint-plugin-import-x@4.11.0(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3): + eslint-plugin-import-x@4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.31.1(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) comment-parser: 1.4.1 debug: 4.4.0 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 get-tsconfig: 4.10.0 is-glob: 4.0.3 @@ -3636,7 +3965,7 @@ snapshots: - supports-color - typescript - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -3645,9 +3974,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.25.1(jiti@2.4.2)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -3659,13 +3988,13 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.25.1(jiti@2.4.2)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.26.0(jiti@2.4.2)): dependencies: aria-query: 5.3.2 array-includes: 3.1.8 @@ -3675,7 +4004,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -3686,15 +4015,15 @@ snapshots: eslint-plugin-no-only-tests@3.3.0: {} - eslint-plugin-prettier@5.2.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.2(eslint@9.25.1(jiti@2.4.2)))(eslint@9.25.1(jiti@2.4.2))(prettier@3.5.3): + eslint-plugin-prettier@5.2.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(prettier@3.5.3): dependencies: - eslint: 9.25.1(jiti@2.4.2) + eslint: 9.26.0(jiti@2.4.2) prettier: 3.5.3 prettier-linter-helpers: 1.0.0 synckit: 0.10.3 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.2(eslint@9.25.1(jiti@2.4.2)) + eslint-config-prettier: 10.1.2(eslint@9.26.0(jiti@2.4.2)) eslint-rule-documentation@1.0.23: {} @@ -3707,19 +4036,20 @@ snapshots: eslint-visitor-keys@4.2.0: {} - eslint@9.25.1(jiti@2.4.2): + eslint@9.26.0(jiti@2.4.2): dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.25.1(jiti@2.4.2)) + '@eslint-community/eslint-utils': 4.5.1(eslint@9.26.0(jiti@2.4.2)) '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.20.0 '@eslint/config-helpers': 0.2.1 '@eslint/core': 0.13.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.25.1 + '@eslint/js': 9.26.0 '@eslint/plugin-kit': 0.2.8 '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.2 + '@modelcontextprotocol/sdk': 1.11.0 '@types/estree': 1.0.7 '@types/json-schema': 7.0.15 ajv: 6.12.6 @@ -3744,6 +4074,7 @@ snapshots: minimatch: 3.1.2 natural-compare: 1.4.0 optionator: 0.9.4 + zod: 3.24.4 optionalDependencies: jiti: 2.4.2 transitivePeerDependencies: @@ -3771,6 +4102,14 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + + eventsource-parser@3.0.1: {} + + eventsource@3.0.6: + dependencies: + eventsource-parser: 3.0.1 + execa@9.5.2: dependencies: '@sindresorhus/merge-streams': 4.0.0 @@ -3788,6 +4127,42 @@ snapshots: expect-type@1.2.1: {} + express-rate-limit@7.5.0(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + exsolve@1.0.4: {} fast-deep-equal@3.1.3: {} @@ -3826,6 +4201,17 @@ snapshots: dependencies: to-regex-range: 5.0.1 + finalhandler@2.1.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -3847,6 +4233,10 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + forwarded@0.2.0: {} + + fresh@2.0.0: {} + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -3938,7 +4328,7 @@ snapshots: globals@14.0.0: {} - globals@15.15.0: {} + globals@16.0.0: {} globalthis@1.0.4: dependencies: @@ -3975,8 +4365,20 @@ snapshots: html-escaper@2.0.2: {} + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + human-signals@8.0.1: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ignore@5.3.2: {} import-fresh@3.3.1: @@ -3986,12 +4388,16 @@ snapshots: imurmurhash@0.1.4: {} + inherits@2.0.4: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.1.0 + ipaddr.js@1.9.1: {} + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -4076,6 +4482,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-promise@4.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -4245,6 +4653,10 @@ snapshots: math-intrinsics@1.1.0: {} + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + merge2@1.4.1: {} micromatch@4.0.8: @@ -4252,6 +4664,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 @@ -4298,6 +4716,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + node-fetch-native@1.6.6: {} node-releases@2.0.19: {} @@ -4326,6 +4746,8 @@ snapshots: pkg-types: 2.1.0 tinyexec: 0.3.2 + object-assign@4.1.1: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -4369,6 +4791,10 @@ snapshots: ohash@2.0.11: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -4411,6 +4837,8 @@ snapshots: parse-ms@4.0.0: {} + parseurl@1.3.3: {} + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -4424,6 +4852,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + path-to-regexp@8.2.0: {} + pathe@1.1.2: {} pathe@2.0.3: {} @@ -4438,6 +4868,8 @@ snapshots: picomatch@4.0.2: {} + pkce-challenge@5.0.0: {} + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -4470,10 +4902,28 @@ snapshots: dependencies: parse-ms: 4.0.0 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + punycode@2.3.1: {} + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + queue-microtask@1.2.3: {} + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + rc9@2.1.2: dependencies: defu: 6.1.4 @@ -4543,6 +4993,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.38.0 fsevents: 2.3.3 + router@2.2.0: + dependencies: + debug: 4.4.0 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + run-applescript@7.0.0: {} run-parallel@1.2.0: @@ -4557,6 +5017,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.2.1: {} + safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 @@ -4568,12 +5030,39 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safer-buffer@2.1.2: {} + scule@1.3.0: {} semver@6.3.1: {} semver@7.7.1: {} + send@1.2.0: + dependencies: + debug: 4.4.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -4596,6 +5085,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setprototypeof@1.2.0: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -4642,6 +5133,8 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.1: {} + std-env@3.8.1: {} std-env@3.9.0: {} @@ -4752,6 +5245,8 @@ snapshots: dependencies: is-number: 7.0.0 + toidentifier@1.0.1: {} + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 @@ -4771,6 +5266,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -4804,12 +5305,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3): + typescript-eslint@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.31.1(eslint@9.25.1(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.25.1(jiti@2.4.2) + '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + '@typescript-eslint/utils': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) + eslint: 9.26.0(jiti@2.4.2) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -4835,6 +5336,8 @@ snapshots: universal-user-agent@6.0.1: {} + unpipe@1.0.0: {} + unrs-resolver@1.7.2: dependencies: napi-postinstall: 0.2.3 @@ -4867,11 +5370,13 @@ snapshots: dependencies: punycode: 2.3.1 - vite-node@3.1.2(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1): + vary@1.1.2: {} + + vite-node@3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1): dependencies: cac: 6.7.14 debug: 4.4.0 - es-module-lexer: 1.6.0 + es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 6.2.4(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) transitivePeerDependencies: @@ -4899,15 +5404,15 @@ snapshots: jiti: 2.4.2 yaml: 2.7.1 - vitest@3.1.2(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1): + vitest@3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1): dependencies: - '@vitest/expect': 3.1.2 - '@vitest/mocker': 3.1.2(vite@6.2.4(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1)) - '@vitest/pretty-format': 3.1.2 - '@vitest/runner': 3.1.2 - '@vitest/snapshot': 3.1.2 - '@vitest/spy': 3.1.2 - '@vitest/utils': 3.1.2 + '@vitest/expect': 3.1.3 + '@vitest/mocker': 3.1.3(vite@6.2.4(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1)) + '@vitest/pretty-format': 3.1.3 + '@vitest/runner': 3.1.3 + '@vitest/snapshot': 3.1.3 + '@vitest/spy': 3.1.3 + '@vitest/utils': 3.1.3 chai: 5.2.0 debug: 4.4.0 expect-type: 1.2.1 @@ -4920,7 +5425,7 @@ snapshots: tinypool: 1.0.2 tinyrainbow: 2.0.0 vite: 6.2.4(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) - vite-node: 3.1.2(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) + vite-node: 3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.17.32 @@ -5012,6 +5517,10 @@ snapshots: yoctocolors@2.1.1: {} + zod-to-json-schema@3.24.5(zod@3.24.4): + dependencies: + zod: 3.24.4 + zod-validation-error@3.4.0(zod@3.24.4): dependencies: zod: 3.24.4 From dc39e95397e33c8423713fedef011841129db78a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 11 Mar 2025 13:11:14 +1300 Subject: [PATCH 27/28] refactor: Use result type when fetching job URLs --- src/api.spec.ts | 32 ++++++++++++++++++++++++++------ src/api.ts | 6 +++--- src/main.ts | 20 +++++++++++++------- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/api.spec.ts b/src/api.spec.ts index 3f374d4..c5b72c9 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -451,8 +451,14 @@ describe("API", () => { vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); - const url = await urlPromise; - expect(url).toStrictEqual("Unable to fetch URL"); + const result = await urlPromise; + + if (result.success) { + expect.fail(); + } + + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("timeout"); // Logging assertOnlyCalled(coreDebugLogMock); @@ -506,8 +512,14 @@ describe("API", () => { vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); - const url = await urlPromise; - expect(url).toStrictEqual("Unable to fetch URL"); + const result = await urlPromise; + + if (result.success) { + expect.fail(); + } + + expect(result.success).toStrictEqual(false); + expect(result.reason).toStrictEqual("timeout"); // Logging assertOnlyCalled(coreDebugLogMock); @@ -542,8 +554,16 @@ describe("API", () => { vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); - const url = await urlPromise; - expect(url).toStrictEqual(inProgressMockData.jobs[0]?.html_url); + const result = await urlPromise; + + if (!result.success) { + expect.fail(); + } + + expect(result.success).toStrictEqual(true); + expect(result.value).toStrictEqual( + inProgressMockData.jobs[0]?.html_url, + ); // Logging assertOnlyCalled(coreDebugLogMock); diff --git a/src/api.ts b/src/api.ts index 5dd297d..59ca43a 100644 --- a/src/api.ts +++ b/src/api.ts @@ -210,14 +210,14 @@ export async function fetchWorkflowRunActiveJobUrl( export async function fetchWorkflowRunActiveJobUrlRetry( runId: number, timeout: number, -): Promise { +): Promise> { const startTime = Date.now(); let elapsedTime = Date.now() - startTime; while (elapsedTime < timeout) { const url = await fetchWorkflowRunActiveJobUrl(runId); if (url) { - return url; + return { success: true, value: url }; } core.debug( @@ -229,7 +229,7 @@ export async function fetchWorkflowRunActiveJobUrlRetry( } core.debug(`Timed out while trying to fetch URL for Workflow Run ${runId}`); - return "Unable to fetch URL"; + return { success: false, reason: "timeout" }; } export async function retryOnError( diff --git a/src/main.ts b/src/main.ts index e935bfa..451a562 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,33 +13,39 @@ async function main(): Promise { const config = getConfig(); api.init(config); - const activeJobUrl = await api.fetchWorkflowRunActiveJobUrlRetry( + const activeJobUrlResult = await api.fetchWorkflowRunActiveJobUrlRetry( config.runId, constants.WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS, ); + if (!activeJobUrlResult.success) { + const elapsedTime = Date.now() - startTime; + const failureMsg = `Timeout exceeded while attempting to find the active job run URL (${elapsedTime}ms)`; + await handleActionFail(failureMsg, config.runId); + return; + } core.info( `Awaiting completion of Workflow Run ${config.runId}...\n` + ` ID: ${config.runId}\n` + - ` URL: ${activeJobUrl}`, + ` URL: ${activeJobUrlResult.value}`, ); - const result = await getWorkflowRunResult({ + const runResult = await getWorkflowRunResult({ startTime, pollIntervalMs: config.pollIntervalMs, runId: config.runId, runTimeoutMs: config.runTimeoutSeconds * 1000, }); - if (!result.success) { + if (!runResult.success) { const elapsedTime = Date.now() - startTime; const failureMsg = - result.reason === "timeout" + runResult.reason === "timeout" ? `Timeout exceeded while attempting to await run conclusion (${elapsedTime}ms)` - : `An unsupported value was reached: ${result.value}`; + : `An unsupported value was reached: ${runResult.value}`; await handleActionFail(failureMsg, config.runId); return; } - const { status, conclusion } = result.value; + const { status, conclusion } = runResult.value; if (conclusion === WorkflowRunConclusion.Success) { core.info( "Run Completed:\n" + From 9fbf558dd515992d00f7ce9d05c3e92485b13b60 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 6 Apr 2025 12:43:00 +1200 Subject: [PATCH 28/28] chore: update eslint packages and config --- eslint.config.mjs | 39 +--- knip.ts | 4 - package.json | 7 - pnpm-lock.yaml | 484 ++++++++++------------------------------------ 4 files changed, 102 insertions(+), 432 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 13f11e6..8c2c0ea 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,35 +1,10 @@ // @ts-check -import { fixupPluginRules } from "@eslint/compat"; -import { FlatCompat } from "@eslint/eslintrc"; import jsEslint from "@eslint/js"; import eslintConfigPrettier from "eslint-config-prettier"; -import eslintPluginImportX from "eslint-plugin-import-x"; +import * as eslintPluginImportX from "eslint-plugin-import-x"; import * as tsEslint from "typescript-eslint"; -const compat = new FlatCompat({ - baseDirectory: import.meta.dirname, - recommendedConfig: jsEslint.configs.recommended, - allConfig: jsEslint.configs.all, -}); - -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -/** - * @param {string} name the pugin name - * @param {string} alias the plugin alias - * @returns {import("eslint").ESLint.Plugin} - */ -function legacyPlugin(name, alias = name) { - const plugin = compat.plugins(name)[0]?.plugins?.[alias]; - - if (!plugin) { - throw new Error(`Unable to resolve plugin ${name} and/or alias ${alias}`); - } - - return fixupPluginRules(plugin); -} -/* eslint-enable @typescript-eslint/explicit-function-return-type */ - export default tsEslint.config( jsEslint.configs.recommended, eslintPluginImportX.flatConfigs.recommended, @@ -56,10 +31,6 @@ export default tsEslint.config( ], }, { - plugins: { - github: legacyPlugin("eslint-plugin-github", "github"), // pending https://github.com/github/eslint-plugin-github/issues/513 - import: legacyPlugin("eslint-plugin-import", "import"), // Needed for above - }, rules: { "@typescript-eslint/await-thenable": "warn", "@typescript-eslint/explicit-function-return-type": "warn", @@ -80,11 +51,7 @@ export default tsEslint.config( allowNumber: true, }, ], - "github/array-foreach": "error", - "github/no-implicit-buggy-globals": "error", - "github/no-then": "error", - "github/no-dynamic-script-tag": "error", - "import/no-extraneous-dependencies": [ + "import-x/no-extraneous-dependencies": [ "error", { devDependencies: true, @@ -92,7 +59,7 @@ export default tsEslint.config( peerDependencies: true, }, ], - "import/order": [ + "import-x/order": [ "warn", { "newlines-between": "always", alphabetize: { order: "asc" } }, ], diff --git a/knip.ts b/knip.ts index ccacc2a..65e3dd9 100644 --- a/knip.ts +++ b/knip.ts @@ -3,11 +3,7 @@ import type { KnipConfig } from "knip"; const config: KnipConfig = { ignore: ["dist/**"], ignoreDependencies: [ - // Used in eslint.config.mjs - "eslint-plugin-github", - "eslint-plugin-import", // Required by eslint-plugin-import-x - "@typescript-eslint/parser", "eslint-import-resolver-typescript", ], }; diff --git a/package.json b/package.json index e109de2..bae07c4 100644 --- a/package.json +++ b/package.json @@ -32,15 +32,10 @@ "@actions/github": "^6.0.0" }, "devDependencies": { - "@eslint/compat": "^1.2.9", - "@eslint/eslintrc": "^3.3.1", "@eslint/js": "^9.26.0", "@opentf/std": "^0.13.0", "@total-typescript/ts-reset": "^0.6.1", - "@types/eslint__js": "^9.14.0", "@types/node": "^20.17.32", - "@typescript-eslint/eslint-plugin": "^8.32.0", - "@typescript-eslint/parser": "^8.32.0", "@vitest/coverage-v8": "^3.1.3", "chalk": "^5.4.1", "changelogithub": "^13.13.0", @@ -48,8 +43,6 @@ "eslint": "^9.26.0", "eslint-config-prettier": "^10.1.2", "eslint-import-resolver-typescript": "^4.3.4", - "eslint-plugin-github": "^6.0.0", - "eslint-plugin-import": "^2.31.0", "eslint-plugin-import-x": "^4.11.0", "knip": "^5.53.0", "prettier": "3.5.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 000c531..1c2e1c6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,12 +15,6 @@ importers: specifier: ^6.0.0 version: 6.0.0 devDependencies: - '@eslint/compat': - specifier: ^1.2.9 - version: 1.2.9(eslint@9.26.0(jiti@2.4.2)) - '@eslint/eslintrc': - specifier: ^3.3.1 - version: 3.3.1 '@eslint/js': specifier: ^9.26.0 version: 9.26.0 @@ -30,18 +24,9 @@ importers: '@total-typescript/ts-reset': specifier: ^0.6.1 version: 0.6.1 - '@types/eslint__js': - specifier: ^9.14.0 - version: 9.14.0 '@types/node': specifier: ^20.17.32 version: 20.17.32 - '@typescript-eslint/eslint-plugin': - specifier: ^8.32.0 - version: 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': - specifier: ^8.32.0 - version: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) '@vitest/coverage-v8': specifier: ^3.1.3 version: 3.1.3(vitest@3.1.3(@types/node@20.17.32)(jiti@2.4.2)(yaml@2.7.1)) @@ -63,12 +48,6 @@ importers: eslint-import-resolver-typescript: specifier: ^4.3.4 version: 4.3.4(eslint-plugin-import-x@4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-github: - specifier: ^6.0.0 - version: 6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-import: - specifier: ^2.31.0 - version: 2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) eslint-plugin-import-x: specifier: ^4.11.0 version: 4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) @@ -305,15 +284,6 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.2.9': - resolution: {integrity: sha512-gCdSY54n7k+driCadyMNv8JSPzYLeDVM/ikZRtvtROBpRdFSkS8W9A82MqsaY7lZuwL0wiapgD0NT1xT0hyJsA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^9.10.0 - peerDependenciesMeta: - eslint: - optional: true - '@eslint/config-array@0.20.0': resolution: {integrity: sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -346,9 +316,6 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - '@github/browserslist-config@1.0.0': - resolution: {integrity: sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw==} - '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -470,10 +437,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.2.0': - resolution: {integrity: sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rollup/rollup-android-arm-eabi@4.38.0': resolution: {integrity: sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==} cpu: [arm] @@ -590,13 +553,6 @@ packages: '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - - '@types/eslint__js@9.14.0': - resolution: {integrity: sha512-s0jepCjOJWB/GKcuba4jISaVpBudw3ClXJ3fUK4tugChUMQsp6kSwuA8Dcx6wFd/JsJqcY8n4rEpa5RTHs5ypA==} - deprecated: This is a stub types definition. @eslint/js provides its own type definitions, so you do not need this installed. - '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} @@ -848,10 +804,6 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -880,9 +832,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -891,14 +840,6 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.10.3: - resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} - engines: {node: '>=4'} - - axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -923,11 +864,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -972,9 +908,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001707: - resolution: {integrity: sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==} - chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -1069,9 +1002,6 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1159,9 +1089,6 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.129: - resolution: {integrity: sha512-JlXUemX4s0+9f8mLqib/bHH8gOHf5elKS6KeWG3sk3xozb/JTq/RLXIv8OKUWiK4Ah00Wm88EFj5PYkFr4RUPA==} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1212,17 +1139,9 @@ packages: engines: {node: '>=18'} hasBin: true - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1270,33 +1189,6 @@ packages: eslint-import-resolver-webpack: optional: true - eslint-plugin-escompat@3.11.4: - resolution: {integrity: sha512-j0ywwNnIufshOzgAu+PfIig1c7VRClKSNKzpniMT2vXQ4leL5q+e/SpMFQU0nrdL2WFFM44XmhSuwmxb3G0CJg==} - peerDependencies: - eslint: '>=5.14.1' - - eslint-plugin-eslint-comments@3.2.0: - resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} - engines: {node: '>=6.5.0'} - peerDependencies: - eslint: '>=4.19.1' - - eslint-plugin-filenames@1.3.2: - resolution: {integrity: sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==} - peerDependencies: - eslint: '*' - - eslint-plugin-github@6.0.0: - resolution: {integrity: sha512-J8MvUoiR/TU/Y9NnEmg1AnbvMUj9R6IO260z47zymMLLvso7B4c80IKjd8diqmqtSmeXXlbIus4i0SvK84flag==} - hasBin: true - peerDependencies: - eslint: ^8 || ^9 - - eslint-plugin-i18n-text@1.0.1: - resolution: {integrity: sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA==} - peerDependencies: - eslint: '>=5.0.0' - eslint-plugin-import-x@4.11.0: resolution: {integrity: sha512-NAaYY49342gj09QGvwnFFl5KcD5aLzjAz97Lo+upnN8MzjEGSIlmL5sxCYGqtIeMjw8fSRDFZIp2xjRLT+yl4Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1313,34 +1205,6 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-jsx-a11y@6.10.2: - resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - - eslint-plugin-no-only-tests@3.3.0: - resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} - engines: {node: '>=5.0.0'} - - eslint-plugin-prettier@5.2.5: - resolution: {integrity: sha512-IKKP8R87pJyMl7WWamLgPkloB16dagPIdd2FjBDbyRYPKo93wS/NbCOPh6gH+ieNLC+XZrhJt/kWj0PS/DFdmg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-rule-documentation@1.0.23: - resolution: {integrity: sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==} - engines: {node: '>=4.0.0'} - eslint-scope@8.3.0: resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1422,9 +1286,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} @@ -1551,10 +1412,6 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@16.0.0: - resolution: {integrity: sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==} - engines: {node: '>=18'} - globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -1820,10 +1677,6 @@ packages: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -1835,13 +1688,6 @@ packages: '@types/node': '>=18' typescript: '>=5.0.4' - language-subtag-registry@0.3.23: - resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} - - language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} - engines: {node: '>=0.10'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1850,21 +1696,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - - lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.snakecase@4.1.1: - resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - - lodash.upperfirst@4.3.1: - resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} @@ -1974,9 +1808,6 @@ packages: node-fetch-native@1.6.6: resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2143,10 +1974,6 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} - prettier@3.5.3: resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} engines: {node: '>=14'} @@ -2346,10 +2173,6 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} - string.prototype.includes@2.0.1: - resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} - engines: {node: '>= 0.4'} - string.prototype.trim@1.2.10: resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} @@ -2394,13 +2217,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svg-element-attributes@1.3.1: - resolution: {integrity: sha512-Bh05dSOnJBf3miNMqpsormfNtfidA/GxQVakhtn0T4DECWKeXQRQUceYjJ+OxYiiLdGe4Jo9iFV8wICFapFeIA==} - - synckit@0.10.3: - resolution: {integrity: sha512-R1urvuyiTaWfeCggqEvpDJwAlDVdsT9NM+IP//Tk2x7qHCkSvBk/fwFgw/TLAHzZlrAnnazMcRw0ZD8HlYFTEQ==} - engines: {node: ^14.18.0 || >=16.0.0} - tapable@2.2.1: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} @@ -2523,12 +2339,6 @@ packages: unrs-resolver@1.7.2: resolution: {integrity: sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -2828,10 +2638,6 @@ snapshots: '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.2.9(eslint@9.26.0(jiti@2.4.2))': - optionalDependencies: - eslint: 9.26.0(jiti@2.4.2) - '@eslint/config-array@0.20.0': dependencies: '@eslint/object-schema': 2.1.6 @@ -2871,8 +2677,6 @@ snapshots: '@fastify/busboy@2.1.1': {} - '@github/browserslist-config@1.0.0': {} - '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -3011,8 +2815,6 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.2.0': {} - '@rollup/rollup-android-arm-eabi@4.38.0': optional: true @@ -3073,7 +2875,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.38.0': optional: true - '@rtsao/scc@1.1.0': {} + '@rtsao/scc@1.1.0': + optional: true '@sec-ant/readable-stream@0.4.1': {} @@ -3086,21 +2889,12 @@ snapshots: tslib: 2.8.1 optional: true - '@types/eslint@9.6.1': - dependencies: - '@types/estree': 1.0.7 - '@types/json-schema': 7.0.15 - optional: true - - '@types/eslint__js@9.14.0': - dependencies: - '@eslint/js': 9.26.0 - '@types/estree@1.0.7': {} '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} + '@types/json5@0.0.29': + optional: true '@types/node@20.17.32': dependencies: @@ -3368,12 +3162,11 @@ snapshots: argparse@2.0.1: {} - aria-query@5.3.2: {} - array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 is-array-buffer: 3.0.5 + optional: true array-includes@3.1.8: dependencies: @@ -3383,6 +3176,7 @@ snapshots: es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 + optional: true array.prototype.findlastindex@1.2.6: dependencies: @@ -3393,6 +3187,7 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 + optional: true array.prototype.flat@1.3.3: dependencies: @@ -3400,6 +3195,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-shim-unscopables: 1.1.0 + optional: true array.prototype.flatmap@1.3.3: dependencies: @@ -3407,6 +3203,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-shim-unscopables: 1.1.0 + optional: true arraybuffer.prototype.slice@1.0.4: dependencies: @@ -3417,20 +3214,17 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + optional: true assertion-error@2.0.1: {} - ast-types-flow@0.0.8: {} - - async-function@1.0.0: {} + async-function@1.0.0: + optional: true available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 - - axe-core@4.10.3: {} - - axobject-query@4.1.0: {} + optional: true balanced-match@1.0.2: {} @@ -3465,13 +3259,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.4: - dependencies: - caniuse-lite: 1.0.30001707 - electron-to-chromium: 1.5.129 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.4) - bundle-name@4.1.0: dependencies: run-applescript: 7.0.0 @@ -3525,6 +3312,7 @@ snapshots: es-define-property: 1.0.1 get-intrinsic: 1.3.0 set-function-length: 1.2.2 + optional: true call-bound@1.0.4: dependencies: @@ -3533,8 +3321,6 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001707: {} - chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -3647,25 +3433,26 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - damerau-levenshtein@1.0.8: {} - data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 + optional: true data-view-byte-length@1.0.2: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 + optional: true data-view-byte-offset@1.0.1: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 + optional: true debug@3.2.7: dependencies: @@ -3691,6 +3478,7 @@ snapshots: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 + optional: true define-lazy-prop@3.0.0: {} @@ -3699,6 +3487,7 @@ snapshots: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 + optional: true defu@6.1.4: {} @@ -3711,6 +3500,7 @@ snapshots: doctrine@2.1.0: dependencies: esutils: 2.0.3 + optional: true dotenv@16.4.7: {} @@ -3724,8 +3514,6 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.129: {} - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -3790,6 +3578,7 @@ snapshots: typed-array-length: 1.0.7 unbox-primitive: 1.1.0 which-typed-array: 1.1.19 + optional: true es-define-property@1.0.1: {} @@ -3807,16 +3596,19 @@ snapshots: get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 + optional: true es-shim-unscopables@1.1.0: dependencies: hasown: 2.0.2 + optional: true es-to-primitive@1.3.0: dependencies: is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 + optional: true esbuild@0.25.3: optionalDependencies: @@ -3846,12 +3638,8 @@ snapshots: '@esbuild/win32-ia32': 0.25.3 '@esbuild/win32-x64': 0.25.3 - escalade@3.2.0: {} - escape-html@1.0.3: {} - escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} eslint-config-prettier@10.1.2(eslint@9.26.0(jiti@2.4.2)): @@ -3891,61 +3679,7 @@ snapshots: eslint-import-resolver-typescript: 4.3.4(eslint-plugin-import-x@4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-plugin-import@2.31.0)(eslint@9.26.0(jiti@2.4.2)) transitivePeerDependencies: - supports-color - - eslint-plugin-escompat@3.11.4(eslint@9.26.0(jiti@2.4.2)): - dependencies: - browserslist: 4.24.4 - eslint: 9.26.0(jiti@2.4.2) - - eslint-plugin-eslint-comments@3.2.0(eslint@9.26.0(jiti@2.4.2)): - dependencies: - escape-string-regexp: 1.0.5 - eslint: 9.26.0(jiti@2.4.2) - ignore: 5.3.2 - - eslint-plugin-filenames@1.3.2(eslint@9.26.0(jiti@2.4.2)): - dependencies: - eslint: 9.26.0(jiti@2.4.2) - lodash.camelcase: 4.3.0 - lodash.kebabcase: 4.1.1 - lodash.snakecase: 4.1.1 - lodash.upperfirst: 4.3.1 - - eslint-plugin-github@6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)): - dependencies: - '@eslint/compat': 1.2.9(eslint@9.26.0(jiti@2.4.2)) - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.26.0 - '@github/browserslist-config': 1.0.0 - '@typescript-eslint/eslint-plugin': 8.32.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - aria-query: 5.3.2 - eslint: 9.26.0(jiti@2.4.2) - eslint-config-prettier: 10.1.2(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-escompat: 3.11.4(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-eslint-comments: 3.2.0(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-filenames: 1.3.2(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-i18n-text: 1.0.1(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3))(eslint-import-resolver-typescript@4.3.4)(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.26.0(jiti@2.4.2)) - eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-prettier: 5.2.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(prettier@3.5.3) - eslint-rule-documentation: 1.0.23 - globals: 16.0.0 - jsx-ast-utils: 3.3.5 - prettier: 3.5.3 - svg-element-attributes: 1.3.1 - typescript: 5.8.3 - typescript-eslint: 8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3) - transitivePeerDependencies: - - '@types/eslint' - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-i18n-text@1.0.1(eslint@9.26.0(jiti@2.4.2)): - dependencies: - eslint: 9.26.0(jiti@2.4.2) + optional: true eslint-plugin-import-x@4.11.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): dependencies: @@ -3993,39 +3727,7 @@ snapshots: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - - eslint-plugin-jsx-a11y@6.10.2(eslint@9.26.0(jiti@2.4.2)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.8 - array.prototype.flatmap: 1.3.3 - ast-types-flow: 0.0.8 - axe-core: 4.10.3 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 9.26.0(jiti@2.4.2) - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - safe-regex-test: 1.1.0 - string.prototype.includes: 2.0.1 - - eslint-plugin-no-only-tests@3.3.0: {} - - eslint-plugin-prettier@5.2.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.2(eslint@9.26.0(jiti@2.4.2)))(eslint@9.26.0(jiti@2.4.2))(prettier@3.5.3): - dependencies: - eslint: 9.26.0(jiti@2.4.2) - prettier: 3.5.3 - prettier-linter-helpers: 1.0.0 - synckit: 0.10.3 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.2(eslint@9.26.0(jiti@2.4.2)) - - eslint-rule-documentation@1.0.23: {} + optional: true eslint-scope@8.3.0: dependencies: @@ -4167,8 +3869,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-diff@1.3.0: {} - fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4227,6 +3927,7 @@ snapshots: for-each@0.3.5: dependencies: is-callable: 1.2.7 + optional: true foreground-child@3.3.1: dependencies: @@ -4254,8 +3955,10 @@ snapshots: functions-have-names: 1.2.3 hasown: 2.0.2 is-callable: 1.2.7 + optional: true - functions-have-names@1.2.3: {} + functions-have-names@1.2.3: + optional: true get-intrinsic@1.3.0: dependencies: @@ -4285,6 +3988,7 @@ snapshots: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 + optional: true get-tsconfig@4.10.0: dependencies: @@ -4328,12 +4032,11 @@ snapshots: globals@14.0.0: {} - globals@16.0.0: {} - globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.2.0 + optional: true gopd@1.2.0: {} @@ -4341,23 +4044,27 @@ snapshots: graphemer@1.4.0: {} - has-bigints@1.1.0: {} + has-bigints@1.1.0: + optional: true has-flag@4.0.0: {} has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 + optional: true has-proto@1.2.0: dependencies: dunder-proto: 1.0.1 + optional: true has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: has-symbols: 1.1.0 + optional: true hasown@2.0.2: dependencies: @@ -4395,6 +4102,7 @@ snapshots: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.1.0 + optional: true ipaddr.js@1.9.1: {} @@ -4403,6 +4111,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 get-intrinsic: 1.3.0 + optional: true is-async-function@2.1.1: dependencies: @@ -4411,10 +4120,12 @@ snapshots: get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 + optional: true is-bigint@1.1.0: dependencies: has-bigints: 1.1.0 + optional: true is-binary-path@2.1.0: dependencies: @@ -4424,12 +4135,14 @@ snapshots: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 + optional: true is-bun-module@2.0.0: dependencies: semver: 7.7.1 - is-callable@1.2.7: {} + is-callable@1.2.7: + optional: true is-core-module@2.16.1: dependencies: @@ -4440,11 +4153,13 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 is-typed-array: 1.1.15 + optional: true is-date-object@1.1.0: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 + optional: true is-docker@3.0.0: {} @@ -4453,6 +4168,7 @@ snapshots: is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 + optional: true is-fullwidth-code-point@3.0.0: {} @@ -4462,6 +4178,7 @@ snapshots: get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 + optional: true is-glob@4.0.3: dependencies: @@ -4471,12 +4188,14 @@ snapshots: dependencies: is-docker: 3.0.0 - is-map@2.0.3: {} + is-map@2.0.3: + optional: true is-number-object@1.1.1: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 + optional: true is-number@7.0.0: {} @@ -4490,12 +4209,15 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 hasown: 2.0.2 + optional: true - is-set@2.0.3: {} + is-set@2.0.3: + optional: true is-shared-array-buffer@1.0.4: dependencies: call-bound: 1.0.4 + optional: true is-stream@4.0.1: {} @@ -4503,35 +4225,42 @@ snapshots: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 + optional: true is-symbol@1.1.1: dependencies: call-bound: 1.0.4 has-symbols: 1.1.0 safe-regex-test: 1.1.0 + optional: true is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.19 + optional: true is-unicode-supported@2.1.0: {} - is-weakmap@2.0.2: {} + is-weakmap@2.0.2: + optional: true is-weakref@1.1.1: dependencies: call-bound: 1.0.4 + optional: true is-weakset@2.0.4: dependencies: call-bound: 1.0.4 get-intrinsic: 1.3.0 + optional: true is-wsl@3.1.0: dependencies: is-inside-container: 1.0.0 - isarray@2.0.5: {} + isarray@2.0.5: + optional: true isexe@2.0.0: {} @@ -4579,13 +4308,7 @@ snapshots: json5@1.0.2: dependencies: minimist: 1.2.8 - - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.8 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 + optional: true keyv@4.5.4: dependencies: @@ -4608,12 +4331,6 @@ snapshots: zod: 3.24.4 zod-validation-error: 3.4.0(zod@3.24.4) - language-subtag-registry@0.3.23: {} - - language-tags@1.0.9: - dependencies: - language-subtag-registry: 0.3.23 - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -4623,16 +4340,8 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.camelcase@4.3.0: {} - - lodash.kebabcase@4.1.1: {} - lodash.merge@4.6.2: {} - lodash.snakecase@4.1.1: {} - - lodash.upperfirst@4.3.1: {} - loupe@3.1.3: {} lru-cache@10.4.3: {} @@ -4720,8 +4429,6 @@ snapshots: node-fetch-native@1.6.6: {} - node-releases@2.0.19: {} - normalize-path@3.0.0: {} npm-run-path@6.0.0: @@ -4750,7 +4457,8 @@ snapshots: object-inspect@1.13.4: {} - object-keys@1.1.1: {} + object-keys@1.1.1: + optional: true object.assign@4.1.7: dependencies: @@ -4760,6 +4468,7 @@ snapshots: es-object-atoms: 1.1.1 has-symbols: 1.1.0 object-keys: 1.1.1 + optional: true object.fromentries@2.0.8: dependencies: @@ -4767,12 +4476,14 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-object-atoms: 1.1.1 + optional: true object.groupby@1.0.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 + optional: true object.values@1.2.1: dependencies: @@ -4780,6 +4491,7 @@ snapshots: call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 + optional: true ofetch@1.4.1: dependencies: @@ -4820,6 +4532,7 @@ snapshots: get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 + optional: true p-limit@3.1.0: dependencies: @@ -4882,7 +4595,8 @@ snapshots: exsolve: 1.0.4 pathe: 2.0.3 - possible-typed-array-names@1.1.0: {} + possible-typed-array-names@1.1.0: + optional: true postcss@8.5.3: dependencies: @@ -4892,10 +4606,6 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: - dependencies: - fast-diff: 1.3.0 - prettier@3.5.3: {} pretty-ms@9.2.0: @@ -4945,6 +4655,7 @@ snapshots: get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 + optional: true regexp.prototype.flags@1.5.4: dependencies: @@ -4954,6 +4665,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 set-function-name: 2.0.2 + optional: true resolve-from@4.0.0: {} @@ -5016,6 +4728,7 @@ snapshots: get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 + optional: true safe-buffer@5.2.1: {} @@ -5023,18 +4736,21 @@ snapshots: dependencies: es-errors: 1.3.0 isarray: 2.0.5 + optional: true safe-regex-test@1.1.0: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-regex: 1.2.1 + optional: true safer-buffer@2.1.2: {} scule@1.3.0: {} - semver@6.3.1: {} + semver@6.3.1: + optional: true semver@7.7.1: {} @@ -5071,6 +4787,7 @@ snapshots: get-intrinsic: 1.3.0 gopd: 1.2.0 has-property-descriptors: 1.0.2 + optional: true set-function-name@2.0.2: dependencies: @@ -5078,12 +4795,14 @@ snapshots: es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + optional: true set-proto@1.0.0: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 + optional: true setprototypeof@1.2.0: {} @@ -5151,12 +4870,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.0 - string.prototype.includes@2.0.1: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 @@ -5166,6 +4879,7 @@ snapshots: es-abstract: 1.23.9 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 + optional: true string.prototype.trimend@1.0.9: dependencies: @@ -5173,12 +4887,14 @@ snapshots: call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 + optional: true string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-object-atoms: 1.1.1 + optional: true strip-ansi@6.0.1: dependencies: @@ -5188,7 +4904,8 @@ snapshots: dependencies: ansi-regex: 6.1.0 - strip-bom@3.0.0: {} + strip-bom@3.0.0: + optional: true strip-final-newline@4.0.0: {} @@ -5202,13 +4919,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svg-element-attributes@1.3.1: {} - - synckit@0.10.3: - dependencies: - '@pkgr/core': 0.2.0 - tslib: 2.8.1 - tapable@2.2.1: {} tar@6.2.1: @@ -5257,6 +4967,7 @@ snapshots: json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 + optional: true tslib@2.8.1: {} @@ -5277,6 +4988,7 @@ snapshots: call-bound: 1.0.4 es-errors: 1.3.0 is-typed-array: 1.1.15 + optional: true typed-array-byte-length@1.0.3: dependencies: @@ -5285,6 +4997,7 @@ snapshots: gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 + optional: true typed-array-byte-offset@1.0.4: dependencies: @@ -5295,6 +5008,7 @@ snapshots: has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 + optional: true typed-array-length@1.0.7: dependencies: @@ -5304,6 +5018,7 @@ snapshots: is-typed-array: 1.1.15 possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 + optional: true typescript-eslint@8.32.0(eslint@9.26.0(jiti@2.4.2))(typescript@5.8.3): dependencies: @@ -5325,6 +5040,7 @@ snapshots: has-bigints: 1.1.0 has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + optional: true undici-types@6.19.8: {} @@ -5360,12 +5076,6 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.7.2 '@unrs/resolver-binding-win32-x64-msvc': 1.7.2 - update-browserslist-db@1.1.3(browserslist@4.24.4): - dependencies: - browserslist: 4.24.4 - escalade: 3.2.0 - picocolors: 1.1.1 - uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -5450,6 +5160,7 @@ snapshots: is-number-object: 1.1.1 is-string: 1.1.1 is-symbol: 1.1.1 + optional: true which-builtin-type@1.2.1: dependencies: @@ -5466,6 +5177,7 @@ snapshots: which-boxed-primitive: 1.1.1 which-collection: 1.0.2 which-typed-array: 1.1.19 + optional: true which-collection@1.0.2: dependencies: @@ -5473,6 +5185,7 @@ snapshots: is-set: 2.0.3 is-weakmap: 2.0.2 is-weakset: 2.0.4 + optional: true which-typed-array@1.1.19: dependencies: @@ -5483,6 +5196,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-tostringtag: 1.0.2 + optional: true which@2.0.2: dependencies: