From 42fc03d0f80ce9cf484da83680c08f49f0348870 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:34:07 +1300 Subject: [PATCH 01/32] 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 175e8a302651d031295e7101e432d926109304ee Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:35:54 +1300 Subject: [PATCH 02/32] 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 43d601cfc19b8a60ac43b14e83a3b756921b08a8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:46:02 +1300 Subject: [PATCH 03/32] 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 035f884998859b0ac20dec273ba86b92f2014735 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:50:55 +1300 Subject: [PATCH 04/32] 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 c5f913f..b5c754a 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"; @@ -68,7 +68,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", @@ -82,7 +82,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); }); @@ -97,8 +97,8 @@ 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}`, ); }); @@ -192,7 +192,7 @@ describe("API", () => { }); }); - describe("getWorkflowRunJobs", () => { + describe("fetchWorkflowRunJobs", () => { const mockData = { total_count: 1, jobs: [ @@ -220,7 +220,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, @@ -233,7 +233,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); @@ -256,8 +256,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}`, ); }); @@ -274,7 +274,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); @@ -285,7 +285,7 @@ describe("API", () => { }); }); - describe("getWorkflowRunActiveJobUrl", () => { + describe("fetchWorkflowRunActiveJobUrl", () => { let inProgressMockData: any; beforeEach(() => { @@ -313,7 +313,7 @@ describe("API", () => { }), ); - const url = await getWorkflowRunActiveJobUrl(123456); + const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(mockData.jobs[0]?.html_url); }); @@ -331,7 +331,7 @@ describe("API", () => { }), ); - const url = await getWorkflowRunActiveJobUrl(123456); + const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(mockData.jobs[0]?.html_url); }); @@ -348,8 +348,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}`, ); }); @@ -367,7 +367,7 @@ describe("API", () => { }), ); - const url = await getWorkflowRunActiveJobUrl(123456); + const url = await fetchWorkflowRunActiveJobUrl(123456); expect(url).toStrictEqual(undefined); }); @@ -385,11 +385,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(); }); @@ -412,7 +412,7 @@ describe("API", () => { }), ); - const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 100); + const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 100); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); @@ -451,7 +451,7 @@ describe("API", () => { }), ); - const urlPromise = getWorkflowRunActiveJobUrlRetry(123456, 200); + const urlPromise = fetchWorkflowRunActiveJobUrlRetry(123456, 200); vi.advanceTimersByTime(400); await vi.advanceTimersByTimeAsync(400); @@ -471,7 +471,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 78731e2..425533c 100644 --- a/src/api.ts +++ b/src/api.ts @@ -35,7 +35,7 @@ export interface WorkflowRunState { conclusion: WorkflowRunConclusion | null; } -export async function getWorkflowRunState( +export async function fetchWorkflowRunState( runId: number, ): Promise { try { @@ -54,7 +54,7 @@ export async function getWorkflowRunState( 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}`, ); } @@ -73,7 +73,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); @@ -103,7 +103,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 @@ -117,18 +117,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", ); @@ -179,7 +179,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); @@ -188,11 +188,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", ); @@ -217,7 +217,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); @@ -226,7 +226,7 @@ export async function getWorkflowRunActiveJobUrl( } } -export async function getWorkflowRunActiveJobUrlRetry( +export async function fetchWorkflowRunActiveJobUrlRetry( runId: number, timeout: number, ): Promise { @@ -239,7 +239,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 1c62ce0bc60920301943c7010716f622387e945d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:54:52 +1300 Subject: [PATCH 05/32] chore: resolve some lint warnings --- src/api.ts | 12 +++++------- src/await-remote-run.ts | 3 +-- src/constants.ts | 5 ----- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/api.ts b/src/api.ts index 425533c..7a8f105 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"; @@ -75,8 +77,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; } @@ -114,7 +115,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}`, @@ -181,8 +181,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; } @@ -219,8 +218,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 119f3c47dfd4adca142ef566f6dd6e66b7b010fc Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:56:20 +1300 Subject: [PATCH 06/32] 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 a8843079b544c5f94149c7ba954cfba06c70535d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:59:11 +1300 Subject: [PATCH 07/32] refactor: add ts-reset --- package.json | 1 + pnpm-lock.yaml | 29 ++++++++++++----------------- src/reset.d.ts | 2 ++ tsconfig.json | 2 +- 4 files changed, 16 insertions(+), 18 deletions(-) create mode 100644 src/reset.d.ts diff --git a/package.json b/package.json index cf4be4c..918f19c 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.39.1", "@octokit/types": "^16.0.0", + "@total-typescript/ts-reset": "^0.6.1", "@types/eslint__js": "^8.42.3", "@types/node": "^20.19.25", "@typescript-eslint/eslint-plugin": "^8.48.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 661ff4d..88b6523 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,6 +27,9 @@ importers: '@octokit/types': specifier: ^16.0.0 version: 16.0.0 + '@total-typescript/ts-reset': + specifier: ^0.6.1 + version: 0.6.1 '@types/eslint__js': specifier: ^8.42.3 version: 8.42.3 @@ -729,6 +732,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.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -794,10 +800,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.45.0': - resolution: {integrity: sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.48.0': resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2250,11 +2252,6 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -3101,6 +3098,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@total-typescript/ts-reset@0.6.1': {} + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -3190,8 +3189,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.45.0': {} - '@typescript-eslint/types@8.48.0': {} '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': @@ -3969,14 +3966,14 @@ snapshots: eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): dependencies: - '@typescript-eslint/types': 8.45.0 + '@typescript-eslint/types': 8.48.0 comment-parser: 1.4.1 debug: 4.4.3 eslint: 9.39.1(jiti@2.6.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 minimatch: 10.0.3 - semver: 7.7.2 + semver: 7.7.3 stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: @@ -4359,7 +4356,7 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 is-callable@1.2.7: {} @@ -4567,7 +4564,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.2 + semver: 7.7.3 math-intrinsics@1.1.0: {} @@ -4887,8 +4884,6 @@ snapshots: semver@6.3.1: {} - semver@7.7.2: {} - semver@7.7.3: {} set-function-length@1.2.2: 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 171d441c021da9a6dcd3faa77a3859114b9aea5c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 5 Oct 2024 18:59:23 +1300 Subject: [PATCH 08/32] 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 d061cc4cc7ee939859b764b18019aa10325ff147 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 6 Oct 2024 17:03:48 +1300 Subject: [PATCH 09/32] refactor: start using result types for retryOnError --- 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 + 6 files changed, 246 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/src/api.spec.ts b/src/api.spec.ts index b5c754a..47fbdf3 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 { @@ -19,6 +19,7 @@ import { retryOnError, } from "./api.ts"; import { clearEtags } from "./etags.ts"; +import { mockLoggingFunctions } from "./test-utils/logging.mock.ts"; vi.mock("@actions/core"); vi.mock("@actions/github"); @@ -57,6 +58,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 @@ -65,7 +73,7 @@ describe("API", () => { }); afterEach(() => { - vi.restoreAllMocks(); + vi.resetAllMocks(); }); describe("fetchWorkflowRunState", () => { @@ -483,50 +491,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>() @@ -535,13 +585,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 7a8f105..f446239 100644 --- a/src/api.ts +++ b/src/api.ts @@ -5,6 +5,8 @@ import * as github from "@actions/github"; import { type ActionConfig, getConfig } from "./action.ts"; import { withEtag } from "./etags.ts"; +import type { Result } from "./types.ts"; +import { sleep } from "./utils.ts"; type Octokit = ReturnType<(typeof github)["getOctokit"]>; @@ -251,32 +253,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 d65fd39e63a8a4185b0da50dbb9f014aff5171de Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 7 Oct 2024 11:28:11 +1300 Subject: [PATCH 10/32] 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 244fd88..ecea48a 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 f446239..f87392d 100644 --- a/src/api.ts +++ b/src/api.ts @@ -51,7 +51,7 @@ export async function fetchWorkflowRunState( run_id: runId, }, async (params) => { - // 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 return octokit.rest.actions.getWorkflowRun(params); }, ); @@ -109,7 +109,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 e89630e987b08572f5f82a9d1781de0b4d8c8178 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 7 Oct 2024 11:44:19 +1300 Subject: [PATCH 11/32] chore: add knip and address findings --- .github/workflows/test.yml | 13 ++ .prettierignore | 1 - eslint.config.mjs | 9 +- knip.ts | 15 ++ package.json | 10 +- pnpm-lock.yaml | 380 +++++++++++++++++++++++++++++++++++-- src/api.ts | 6 +- src/await-remote-run.ts | 9 +- 8 files changed, 406 insertions(+), 37 deletions(-) create mode 100644 knip.ts diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 53a827a..4924cc7 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 918f19c..15ed79e 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", @@ -36,10 +36,9 @@ "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.39.1", "@octokit/types": "^16.0.0", + "@opentf/std": "^0.13.0", "@total-typescript/ts-reset": "^0.6.1", - "@types/eslint__js": "^8.42.3", "@types/node": "^20.19.25", - "@typescript-eslint/eslint-plugin": "^8.48.0", "@typescript-eslint/parser": "^8.48.0", "@vitest/coverage-v8": "^3.2.4", "chalk": "^5.6.2", @@ -51,6 +50,7 @@ "eslint-plugin-github": "^5.1.8", "eslint-plugin-import": "^2.32.0", "eslint-plugin-import-x": "^4.16.1", + "knip": "5.71.0", "prettier": "3.7.3", "typescript": "^5.9.3", "typescript-eslint": "^8.48.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 88b6523..082540b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,18 +27,15 @@ importers: '@octokit/types': specifier: ^16.0.0 version: 16.0.0 + '@opentf/std': + specifier: ^0.13.0 + version: 0.13.0 '@total-typescript/ts-reset': specifier: ^0.6.1 version: 0.6.1 - '@types/eslint__js': - specifier: ^8.42.3 - version: 8.42.3 '@types/node': specifier: ^20.19.25 version: 20.19.25 - '@typescript-eslint/eslint-plugin': - specifier: ^8.48.0 - version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^8.48.0 version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) @@ -72,6 +69,9 @@ importers: eslint-plugin-import-x: specifier: ^4.16.1 version: 4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + knip: + specifier: 5.71.0 + version: 5.71.0(@types/node@20.19.25)(typescript@5.9.3) prettier: specifier: 3.7.3 version: 3.7.3 @@ -127,11 +127,11 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@emnapi/core@1.5.0': - resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -550,6 +550,21 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@napi-rs/wasm-runtime@1.1.0': + resolution: {integrity: sha512-Fq6DJW+Bb5jaWE69/qOE0D1TUN9+6uWhCeZpdnSBk14pjLcCWR7Q8n49PTSPHazM37JqrsdpEthXy2xn6jWWiA==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@octokit/auth-token@4.0.0': resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} engines: {node: '>= 18'} @@ -604,6 +619,110 @@ packages: '@octokit/types@16.0.0': resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + '@opentf/std@0.13.0': + resolution: {integrity: sha512-VG9vn7oML5prxWipDvod1X7z9+3fyyCbw+SuD5F7cWx9F1bXFZdAYGIKGqqHLtfxz3mrXZWHcxnm8d0YwQ7tKQ==} + engines: {node: '>=16.20.2'} + + '@oxc-resolver/binding-android-arm-eabi@11.15.0': + resolution: {integrity: sha512-Q+lWuFfq7whNelNJIP1dhXaVz4zO9Tu77GcQHyxDWh3MaCoO2Bisphgzmsh4ZoUe2zIchQh6OvQL99GlWHg9Tw==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.15.0': + resolution: {integrity: sha512-vbdBttesHR0W1oJaxgWVTboyMUuu+VnPsHXJ6jrXf4czELzB6GIg5DrmlyhAmFBhjwov+yJH/DfTnHS+2sDgOw==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.15.0': + resolution: {integrity: sha512-R67lsOe1UzNjqVBCwCZX1rlItTsj/cVtBw4Uy19CvTicqEWvwaTn8t34zLD75LQwDDPCY3C8n7NbD+LIdw+ZoA==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.15.0': + resolution: {integrity: sha512-77mya5F8WV0EtCxI0MlVZcqkYlaQpfNwl/tZlfg4jRsoLpFbaTeWv75hFm6TE84WULVlJtSgvf7DhoWBxp9+ZQ==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.15.0': + resolution: {integrity: sha512-X1Sz7m5PC+6D3KWIDXMUtux+0Imj6HfHGdBStSvgdI60OravzI1t83eyn6eN0LPTrynuPrUgjk7tOnOsBzSWHw==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.15.0': + resolution: {integrity: sha512-L1x/wCaIRre+18I4cH/lTqSAymlV0k4HqfSYNNuI9oeL28Ks86lI6O5VfYL6sxxWYgjuWB98gNGo7tq7d4GarQ==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.15.0': + resolution: {integrity: sha512-abGXd/zMGa0tH8nKlAXdOnRy4G7jZmkU0J85kMKWns161bxIgGn/j7zxqh3DKEW98wAzzU9GofZMJ0P5YCVPVw==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.15.0': + resolution: {integrity: sha512-SVjjjtMW66Mza76PBGJLqB0KKyFTBnxmtDXLJPbL6ZPGSctcXVmujz7/WAc0rb9m2oV0cHQTtVjnq6orQnI/jg==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-musl@11.15.0': + resolution: {integrity: sha512-JDv2/AycPF2qgzEiDeMJCcSzKNDm3KxNg0KKWipoKEMDFqfM7LxNwwSVyAOGmrYlE4l3dg290hOMsr9xG7jv9g==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.15.0': + resolution: {integrity: sha512-zbu9FhvBLW4KJxo7ElFvZWbSt4vP685Qc/Gyk/Ns3g2gR9qh2qWXouH8PWySy+Ko/qJ42+HJCLg+ZNcxikERfg==} + cpu: [ppc64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.15.0': + resolution: {integrity: sha512-Kfleehe6B09C2qCnyIU01xLFqFXCHI4ylzkicfX/89j+gNHh9xyNdpEvit88Kq6i5tTGdavVnM6DQfOE2qNtlg==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-musl@11.15.0': + resolution: {integrity: sha512-J7LPiEt27Tpm8P+qURDwNc8q45+n+mWgyys4/V6r5A8v5gDentHRGUx3iVk5NxdKhgoGulrzQocPTZVosq25Eg==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-s390x-gnu@11.15.0': + resolution: {integrity: sha512-+8/d2tAScPjVJNyqa7GPGnqleTB/XW9dZJQ2D/oIM3wpH3TG+DaFEXBbk4QFJ9K9AUGBhvQvWU2mQyhK/yYn3Q==} + cpu: [s390x] + os: [linux] + + '@oxc-resolver/binding-linux-x64-gnu@11.15.0': + resolution: {integrity: sha512-xtvSzH7Nr5MCZI2FKImmOdTl9kzuQ51RPyLh451tvD2qnkg3BaqI9Ox78bTk57YJhlXPuxWSOL5aZhKAc9J6qg==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-linux-x64-musl@11.15.0': + resolution: {integrity: sha512-14YL1zuXj06+/tqsuUZuzL0T425WA/I4nSVN1kBXeC5WHxem6lQ+2HGvG+crjeJEqHgZUT62YIgj88W+8E7eyg==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-openharmony-arm64@11.15.0': + resolution: {integrity: sha512-/7Qli+1Wk93coxnrQaU8ySlICYN8HsgyIrzqjgIkQEpI//9eUeaeIHZptNl2fMvBGeXa7k2QgLbRNaBRgpnvMw==} + cpu: [arm64] + os: [openharmony] + + '@oxc-resolver/binding-wasm32-wasi@11.15.0': + resolution: {integrity: sha512-q5rn2eIMQLuc/AVGR2rQKb2EVlgreATGG8xXg8f4XbbYCVgpxaq+dgMbiPStyNywW1MH8VU2T09UEm30UtOQvg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.15.0': + resolution: {integrity: sha512-yCAh2RWjU/8wWTxQDgGPgzV9QBv0/Ojb5ej1c/58iOjyTuy/J1ZQtYi2SpULjKmwIxLJdTiCHpMilauWimE31w==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-ia32-msvc@11.15.0': + resolution: {integrity: sha512-lmXKb6lvA6M6QIbtYfgjd+AryJqExZVSY2bfECC18OPu7Lv1mHFF171Mai5l9hG3r4IhHPPIwT10EHoilSCYeA==} + cpu: [ia32] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.15.0': + resolution: {integrity: sha512-HZsfne0s/tGOcJK9ZdTGxsNU2P/dH0Shf0jqrPvsC6wX0Wk+6AyhSpHFLQCnLOuFQiHHU0ePfM8iYsoJb5hHpQ==} + cpu: [x64] + os: [win32] + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -747,9 +866,6 @@ 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/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1535,12 +1651,22 @@ packages: 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'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fd-package-json@2.0.0: + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1581,6 +1707,11 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + formatly@0.3.0: + resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} + engines: {node: '>=18.3.0'} + hasBin: true + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -1913,6 +2044,14 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + knip@5.71.0: + resolution: {integrity: sha512-hwgdqEJ+7DNJ5jE8BCPu7b57TY7vUwP6MzWYgCgPpg6iPCee/jKPShDNIlFER2koti4oz5xF88VJbKCb4Wl71g==} + engines: {node: '>=18.18.0'} + hasBin: true + peerDependencies: + '@types/node': '>=18' + typescript: '>=5.0.4 <7' + language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} @@ -1963,6 +2102,14 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + minimatch@10.0.3: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} @@ -2093,6 +2240,9 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + oxc-resolver@11.15.0: + resolution: {integrity: sha512-Hk2J8QMYwmIO9XTCUiOH00+Xk2/+aBxRUnhrSlANDyCnLYc32R1WSIq1sU2yEdlqd53FfMpPEpnBYIKQMzliJw==} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -2193,6 +2343,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + rc9@2.1.2: resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} @@ -2224,6 +2377,10 @@ packages: engines: {node: '>= 0.4'} hasBin: true + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rollup@4.52.3: resolution: {integrity: sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2233,6 +2390,9 @@ packages: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + safe-array-concat@1.1.3: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} @@ -2300,6 +2460,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + smol-toml@1.5.2: + resolution: {integrity: sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==} + engines: {node: '>= 18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -2362,6 +2526,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-json-comments@5.0.3: + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} + engines: {node: '>=14.16'} + strip-literal@3.1.0: resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} @@ -2571,6 +2739,10 @@ packages: jsdom: optional: true + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -2632,6 +2804,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} + snapshots: '@actions/core@1.11.1': @@ -2680,13 +2855,13 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@emnapi/core@1.5.0': + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.5.0': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true @@ -2952,11 +3127,30 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.5.0 - '@emnapi/runtime': 1.5.0 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@napi-rs/wasm-runtime@1.1.0': + dependencies: + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + '@octokit/auth-token@4.0.0': {} '@octokit/core@5.2.2': @@ -3021,6 +3215,70 @@ snapshots: dependencies: '@octokit/openapi-types': 27.0.0 + '@opentf/std@0.13.0': {} + + '@oxc-resolver/binding-android-arm-eabi@11.15.0': + optional: true + + '@oxc-resolver/binding-android-arm64@11.15.0': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.15.0': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.15.0': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.15.0': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.15.0': + optional: true + + '@oxc-resolver/binding-openharmony-arm64@11.15.0': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.15.0': + dependencies: + '@napi-rs/wasm-runtime': 1.1.0 + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.15.0': + optional: true + + '@oxc-resolver/binding-win32-ia32-msvc@11.15.0': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.15.0': + optional: true + '@pkgjs/parseargs@0.11.0': optional: true @@ -3115,10 +3373,7 @@ snapshots: dependencies: '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 - - '@types/eslint__js@8.42.3': - dependencies: - '@types/eslint': 9.6.1 + optional: true '@types/estree@1.0.8': {} @@ -4139,10 +4394,26 @@ snapshots: fast-diff@1.3.0: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fd-package-json@2.0.0: + dependencies: + walk-up-path: 4.0.0 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -4180,6 +4451,10 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + formatly@0.3.0: + dependencies: + fd-package-json: 2.0.0 + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -4523,6 +4798,23 @@ snapshots: dependencies: json-buffer: 3.0.1 + knip@5.71.0(@types/node@20.19.25)(typescript@5.9.3): + dependencies: + '@nodelib/fs.walk': 1.2.8 + '@types/node': 20.19.25 + fast-glob: 3.3.3 + formatly: 0.3.0 + jiti: 2.6.1 + js-yaml: 4.1.1 + minimist: 1.2.8 + oxc-resolver: 11.15.0 + picocolors: 1.1.1 + picomatch: 4.0.3 + smol-toml: 1.5.2 + strip-json-comments: 5.0.3 + typescript: 5.9.3 + zod: 4.1.13 + language-subtag-registry@0.3.23: {} language-tags@1.0.9: @@ -4568,6 +4860,13 @@ snapshots: math-intrinsics@1.1.0: {} + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + minimatch@10.0.3: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -4711,6 +5010,29 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxc-resolver@11.15.0: + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.15.0 + '@oxc-resolver/binding-android-arm64': 11.15.0 + '@oxc-resolver/binding-darwin-arm64': 11.15.0 + '@oxc-resolver/binding-darwin-x64': 11.15.0 + '@oxc-resolver/binding-freebsd-x64': 11.15.0 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.15.0 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.15.0 + '@oxc-resolver/binding-linux-arm64-gnu': 11.15.0 + '@oxc-resolver/binding-linux-arm64-musl': 11.15.0 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.15.0 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.15.0 + '@oxc-resolver/binding-linux-riscv64-musl': 11.15.0 + '@oxc-resolver/binding-linux-s390x-gnu': 11.15.0 + '@oxc-resolver/binding-linux-x64-gnu': 11.15.0 + '@oxc-resolver/binding-linux-x64-musl': 11.15.0 + '@oxc-resolver/binding-openharmony-arm64': 11.15.0 + '@oxc-resolver/binding-wasm32-wasi': 11.15.0 + '@oxc-resolver/binding-win32-arm64-msvc': 11.15.0 + '@oxc-resolver/binding-win32-ia32-msvc': 11.15.0 + '@oxc-resolver/binding-win32-x64-msvc': 11.15.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -4790,6 +5112,8 @@ snapshots: punycode@2.3.1: {} + queue-microtask@1.2.3: {} + rc9@2.1.2: dependencies: defu: 6.1.4 @@ -4831,6 +5155,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} + rollup@4.52.3: dependencies: '@types/estree': 1.0.8 @@ -4861,6 +5187,10 @@ snapshots: run-applescript@7.1.0: {} + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 @@ -4946,6 +5276,8 @@ snapshots: signal-exit@4.1.0: {} + smol-toml@1.5.2: {} + source-map-js@1.2.1: {} stable-hash-x@0.2.0: {} @@ -5014,6 +5346,8 @@ snapshots: strip-json-comments@3.1.1: {} + strip-json-comments@5.0.3: {} + strip-literal@3.1.0: dependencies: js-tokens: 9.0.1 @@ -5261,6 +5595,8 @@ snapshots: - tsx - yaml + walk-up-path@4.0.0: {} + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -5338,3 +5674,5 @@ snapshots: yocto-queue@0.1.0: {} yoctocolors@2.1.2: {} + + zod@4.1.13: {} diff --git a/src/api.ts b/src/api.ts index f87392d..32c2af9 100644 --- a/src/api.ts +++ b/src/api.ts @@ -34,7 +34,7 @@ export function init(cfg?: ActionConfig): void { octokit = github.getOctokit(config.token); } -export interface WorkflowRunState { +interface WorkflowRunState { status: WorkflowRunStatus | null; conclusion: WorkflowRunConclusion | null; } @@ -85,7 +85,7 @@ export async function fetchWorkflowRunState( } } -export interface WorkflowRunJob { +interface WorkflowRunJob { id: number; name: string; status: "queued" | "in_progress" | "completed" | "waiting"; @@ -94,7 +94,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 e8dbbbfeeb82d6d546d2b32f8d1079c41df2aed3 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 13:48:43 +1300 Subject: [PATCH 12/32] 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 32c2af9..9c7325e 100644 --- a/src/api.ts +++ b/src/api.ts @@ -13,9 +13,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", } @@ -26,6 +35,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 71da7c2098d145fbb27cd70d6a8efb29d04ed25c Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 13:51:50 +1300 Subject: [PATCH 13/32] 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 4272509965f3ce79f22659dd72924ac9dc437451 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:34:08 +1300 Subject: [PATCH 14/32] 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 9c7325e..b3b004b 100644 --- a/src/api.ts +++ b/src/api.ts @@ -245,17 +245,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 1cb8cf32573513ef59bdef731dffab48dc2c33db Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:35:30 +1300 Subject: [PATCH 15/32] 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 b3b004b..0214bd2 100644 --- a/src/api.ts +++ b/src/api.ts @@ -180,11 +180,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 f0c4a51e18a904b893d6f33b2948ef35920b23b8 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:42:54 +1300 Subject: [PATCH 16/32] 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 47fbdf3..6f2cd8f 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -58,8 +58,13 @@ describe("API", () => { pollIntervalMs: 2500, }; - const { coreWarningLogMock, assertOnlyCalled, assertNoneCalled } = - mockLoggingFunctions(); + const { + coreErrorLogMock, + coreWarningLogMock, + coreDebugLogMock, + assertOnlyCalled, + assertNoneCalled, + } = mockLoggingFunctions(); afterAll(() => { vi.restoreAllMocks(); @@ -90,9 +95,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 () => { @@ -105,9 +122,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(); }); it("should send the previous etag in the If-None-Match header", async () => { @@ -241,14 +267,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 () => { @@ -264,9 +329,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 () => { @@ -282,14 +356,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]" + `); }); }); @@ -339,8 +425,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 () => { @@ -356,9 +453,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 () => { @@ -375,8 +481,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 () => { @@ -393,8 +512,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", () => { @@ -420,12 +552,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 () => { @@ -459,12 +610,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 () => { @@ -479,12 +647,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)]" + `); }); }); }); @@ -504,14 +683,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(); }); @@ -520,13 +700,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(` @@ -537,32 +718,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(` @@ -571,9 +756,6 @@ describe("API", () => { error: some error" `); coreWarningLogMock.mockReset(); - - // Clean up promise - await retryPromise; }); it("should return a timeout result", async () => { @@ -585,10 +767,9 @@ describe("API", () => { throw new Error(errorMsg); }); + // Behaviour const retryPromise = retryOnError(() => testFunc(), 500); - await vi.advanceTimersByTimeAsync(2000); - const result = await retryPromise; if (result.success) { @@ -597,6 +778,8 @@ describe("API", () => { expect(result.success).toStrictEqual(false); expect(result.reason).toStrictEqual("timeout"); + + // Logging assertNoneCalled(); }); }); From eefadb5830d4e204073f6a8a103c7d1a356c90eb Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:49:58 +1300 Subject: [PATCH 17/32] 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 226657f1246bb1095a89f16c6dfe23d0e7bcc2f5 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 14:51:49 +1300 Subject: [PATCH 18/32] 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 2e41f9f621b76230c121e70bcd69f05cd1948135 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sat, 26 Oct 2024 15:05:27 +1300 Subject: [PATCH 19/32] 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 33f65802adaf3494c44a5dfebdb424a2a0074197 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 28 Oct 2024 12:09:18 +1300 Subject: [PATCH 20/32] 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 bfa5205733c0dbe4aab5acecac14e431ea8f6db7 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 7 Nov 2024 07:30:09 +1300 Subject: [PATCH 21/32] 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 34940495bddf41c390194de14e64bad40ea26c7d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Thu, 7 Nov 2024 07:33:24 +1300 Subject: [PATCH 22/32] 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 0214bd2..e44eb30 100644 --- a/src/api.ts +++ b/src/api.ts @@ -5,7 +5,11 @@ import * as github from "@actions/github"; import { type ActionConfig, getConfig } from "./action.ts"; import { withEtag } from "./etags.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"]>; @@ -13,33 +17,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 c65e6647d834f40e07f1c51fc6de68b3ee82462a Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 17:40:17 +1300 Subject: [PATCH 23/32] 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 04eb99d6f3f34fe4b9c2f37655f1d44a1c557a56 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 17:57:14 +1300 Subject: [PATCH 24/32] 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 16baac1128c058c485a12f266b81c3248ccb3faa Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 18:01:32 +1300 Subject: [PATCH 25/32] 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 3c116e3f64b7f0f009a3164640ac6f0a7944c95d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 24 Nov 2024 18:06:07 +1300 Subject: [PATCH 26/32] chore: bump dependencies --- package.json | 14 +- pnpm-lock.yaml | 473 ++++++++++++++++++++++++++----------------------- 2 files changed, 255 insertions(+), 232 deletions(-) diff --git a/package.json b/package.json index 15ed79e..2fbdc54 100644 --- a/package.json +++ b/package.json @@ -32,28 +32,28 @@ "@actions/github": "^6.0.1" }, "devDependencies": { - "@eslint/compat": "^1.4.1", + "@eslint/compat": "^2.0.0", "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.39.1", "@octokit/types": "^16.0.0", "@opentf/std": "^0.13.0", "@total-typescript/ts-reset": "^0.6.1", "@types/node": "^20.19.25", - "@typescript-eslint/parser": "^8.48.0", + "@typescript-eslint/parser": "^8.48.1", "@vitest/coverage-v8": "^3.2.4", "chalk": "^5.6.2", - "changelogithub": "^13.16.1", - "esbuild": "^0.27.0", + "changelogithub": "^14.0.0", + "esbuild": "^0.27.1", "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-github": "^5.1.8", + "eslint-plugin-github": "^6.0.0", "eslint-plugin-import": "^2.32.0", "eslint-plugin-import-x": "^4.16.1", "knip": "5.71.0", - "prettier": "3.7.3", + "prettier": "3.7.4", "typescript": "^5.9.3", - "typescript-eslint": "^8.48.0", + "typescript-eslint": "^8.48.1", "vitest": "^3.2.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 082540b..a4e2943 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,8 +16,8 @@ importers: version: 6.0.1 devDependencies: '@eslint/compat': - specifier: ^1.4.1 - version: 1.4.1(eslint@9.39.1(jiti@2.6.1)) + specifier: ^2.0.0 + version: 2.0.0(eslint@9.39.1(jiti@2.6.1)) '@eslint/eslintrc': specifier: ^3.3.3 version: 3.3.3 @@ -37,8 +37,8 @@ importers: specifier: ^20.19.25 version: 20.19.25 '@typescript-eslint/parser': - specifier: ^8.48.0 - version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + specifier: ^8.48.1 + version: 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) '@vitest/coverage-v8': specifier: ^3.2.4 version: 3.2.4(vitest@3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1)) @@ -46,11 +46,11 @@ importers: specifier: ^5.6.2 version: 5.6.2 changelogithub: - specifier: ^13.16.1 - version: 13.16.1(magicast@0.3.5) + specifier: ^14.0.0 + version: 14.0.0(magicast@0.3.5) esbuild: - specifier: ^0.27.0 - version: 0.27.0 + specifier: ^0.27.1 + version: 0.27.1 eslint: specifier: ^9.39.1 version: 9.39.1(jiti@2.6.1) @@ -59,28 +59,28 @@ importers: version: 10.1.8(eslint@9.39.1(jiti@2.6.1)) eslint-import-resolver-typescript: specifier: ^4.4.4 - version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-github: - specifier: ^5.1.8 - version: 5.1.8(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + specifier: ^6.0.0 + version: 6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) + version: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-import-x: specifier: ^4.16.1 - version: 4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + version: 4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) knip: specifier: 5.71.0 version: 5.71.0(@types/node@20.19.25)(typescript@5.9.3) prettier: - specifier: 3.7.3 - version: 3.7.3 + specifier: 3.7.4 + version: 3.7.4 typescript: specifier: ^5.9.3 version: 5.9.3 typescript-eslint: - specifier: ^8.48.0 - version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + specifier: ^8.48.1 + version: 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) vitest: specifier: ^3.2.4 version: 3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) @@ -142,8 +142,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.0': - resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} + '@esbuild/aix-ppc64@0.27.1': + resolution: {integrity: sha512-HHB50pdsBX6k47S4u5g/CaLjqS3qwaOVE5ILsq64jyzgMhLuCuZ8rGzM9yhsAjfjkbgUPMzZEPa7DAp7yz6vuA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -154,8 +154,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.0': - resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} + '@esbuild/android-arm64@0.27.1': + resolution: {integrity: sha512-45fuKmAJpxnQWixOGCrS+ro4Uvb4Re9+UTieUY2f8AEc+t7d4AaZ6eUJ3Hva7dtrxAAWHtlEFsXFMAgNnGU9uQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -166,8 +166,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.0': - resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + '@esbuild/android-arm@0.27.1': + resolution: {integrity: sha512-kFqa6/UcaTbGm/NncN9kzVOODjhZW8e+FRdSeypWe6j33gzclHtwlANs26JrupOntlcWmB0u8+8HZo8s7thHvg==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -178,8 +178,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.0': - resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} + '@esbuild/android-x64@0.27.1': + resolution: {integrity: sha512-LBEpOz0BsgMEeHgenf5aqmn/lLNTFXVfoWMUox8CtWWYK9X4jmQzWjoGoNb8lmAYml/tQ/Ysvm8q7szu7BoxRQ==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -190,8 +190,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.0': - resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} + '@esbuild/darwin-arm64@0.27.1': + resolution: {integrity: sha512-veg7fL8eMSCVKL7IW4pxb54QERtedFDfY/ASrumK/SbFsXnRazxY4YykN/THYqFnFwJ0aVjiUrVG2PwcdAEqQQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -202,8 +202,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.0': - resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + '@esbuild/darwin-x64@0.27.1': + resolution: {integrity: sha512-+3ELd+nTzhfWb07Vol7EZ+5PTbJ/u74nC6iv4/lwIU99Ip5uuY6QoIf0Hn4m2HoV0qcnRivN3KSqc+FyCHjoVQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -214,8 +214,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.0': - resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} + '@esbuild/freebsd-arm64@0.27.1': + resolution: {integrity: sha512-/8Rfgns4XD9XOSXlzUDepG8PX+AVWHliYlUkFI3K3GB6tqbdjYqdhcb4BKRd7C0BhZSoaCxhv8kTcBrcZWP+xg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -226,8 +226,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.0': - resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} + '@esbuild/freebsd-x64@0.27.1': + resolution: {integrity: sha512-GITpD8dK9C+r+5yRT/UKVT36h/DQLOHdwGVwwoHidlnA168oD3uxA878XloXebK4Ul3gDBBIvEdL7go9gCUFzQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -238,8 +238,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.0': - resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} + '@esbuild/linux-arm64@0.27.1': + resolution: {integrity: sha512-W9//kCrh/6in9rWIBdKaMtuTTzNj6jSeG/haWBADqLLa9P8O5YSRDzgD5y9QBok4AYlzS6ARHifAb75V6G670Q==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -250,8 +250,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.0': - resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} + '@esbuild/linux-arm@0.27.1': + resolution: {integrity: sha512-ieMID0JRZY/ZeCrsFQ3Y3NlHNCqIhTprJfDgSB3/lv5jJZ8FX3hqPyXWhe+gvS5ARMBJ242PM+VNz/ctNj//eA==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -262,8 +262,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.0': - resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + '@esbuild/linux-ia32@0.27.1': + resolution: {integrity: sha512-VIUV4z8GD8rtSVMfAj1aXFahsi/+tcoXXNYmXgzISL+KB381vbSTNdeZHHHIYqFyXcoEhu9n5cT+05tRv13rlw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -274,8 +274,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.0': - resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} + '@esbuild/linux-loong64@0.27.1': + resolution: {integrity: sha512-l4rfiiJRN7sTNI//ff65zJ9z8U+k6zcCg0LALU5iEWzY+a1mVZ8iWC1k5EsNKThZ7XCQ6YWtsZ8EWYm7r1UEsg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -286,8 +286,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.0': - resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} + '@esbuild/linux-mips64el@0.27.1': + resolution: {integrity: sha512-U0bEuAOLvO/DWFdygTHWY8C067FXz+UbzKgxYhXC0fDieFa0kDIra1FAhsAARRJbvEyso8aAqvPdNxzWuStBnA==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -298,8 +298,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.0': - resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} + '@esbuild/linux-ppc64@0.27.1': + resolution: {integrity: sha512-NzdQ/Xwu6vPSf/GkdmRNsOfIeSGnh7muundsWItmBsVpMoNPVpM61qNzAVY3pZ1glzzAxLR40UyYM23eaDDbYQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -310,8 +310,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.0': - resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} + '@esbuild/linux-riscv64@0.27.1': + resolution: {integrity: sha512-7zlw8p3IApcsN7mFw0O1Z1PyEk6PlKMu18roImfl3iQHTnr/yAfYv6s4hXPidbDoI2Q0pW+5xeoM4eTCC0UdrQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -322,8 +322,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.0': - resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + '@esbuild/linux-s390x@0.27.1': + resolution: {integrity: sha512-cGj5wli+G+nkVQdZo3+7FDKC25Uh4ZVwOAK6A06Hsvgr8WqBBuOy/1s+PUEd/6Je+vjfm6stX0kmib5b/O2Ykw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -334,8 +334,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.0': - resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} + '@esbuild/linux-x64@0.27.1': + resolution: {integrity: sha512-z3H/HYI9MM0HTv3hQZ81f+AKb+yEoCRlUby1F80vbQ5XdzEMyY/9iNlAmhqiBKw4MJXwfgsh7ERGEOhrM1niMA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -346,8 +346,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.0': - resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} + '@esbuild/netbsd-arm64@0.27.1': + resolution: {integrity: sha512-wzC24DxAvk8Em01YmVXyjl96Mr+ecTPyOuADAvjGg+fyBpGmxmcr2E5ttf7Im8D0sXZihpxzO1isus8MdjMCXQ==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -358,8 +358,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.0': - resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} + '@esbuild/netbsd-x64@0.27.1': + resolution: {integrity: sha512-1YQ8ybGi2yIXswu6eNzJsrYIGFpnlzEWRl6iR5gMgmsrR0FcNoV1m9k9sc3PuP5rUBLshOZylc9nqSgymI+TYg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -370,8 +370,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.0': - resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} + '@esbuild/openbsd-arm64@0.27.1': + resolution: {integrity: sha512-5Z+DzLCrq5wmU7RDaMDe2DVXMRm2tTDvX2KU14JJVBN2CT/qov7XVix85QoJqHltpvAOZUAc3ndU56HSMWrv8g==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -382,8 +382,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.0': - resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} + '@esbuild/openbsd-x64@0.27.1': + resolution: {integrity: sha512-Q73ENzIdPF5jap4wqLtsfh8YbYSZ8Q0wnxplOlZUOyZy7B4ZKW8DXGWgTCZmF8VWD7Tciwv5F4NsRf6vYlZtqg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -394,8 +394,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.0': - resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} + '@esbuild/openharmony-arm64@0.27.1': + resolution: {integrity: sha512-ajbHrGM/XiK+sXM0JzEbJAen+0E+JMQZ2l4RR4VFwvV9JEERx+oxtgkpoKv1SevhjavK2z2ReHk32pjzktWbGg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -406,8 +406,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.0': - resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} + '@esbuild/sunos-x64@0.27.1': + resolution: {integrity: sha512-IPUW+y4VIjuDVn+OMzHc5FV4GubIwPnsz6ubkvN8cuhEqH81NovB53IUlrlBkPMEPxvNnf79MGBoz8rZ2iW8HA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -418,8 +418,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.0': - resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} + '@esbuild/win32-arm64@0.27.1': + resolution: {integrity: sha512-RIVRWiljWA6CdVu8zkWcRmGP7iRRIIwvhDKem8UMBjPql2TXM5PkDVvvrzMtj1V+WFPB4K7zkIGM7VzRtFkjdg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -430,8 +430,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.0': - resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} + '@esbuild/win32-ia32@0.27.1': + resolution: {integrity: sha512-2BR5M8CPbptC1AK5JbJT1fWrHLvejwZidKx3UMSF0ecHMa+smhi16drIrCEggkgviBwLYd5nwrFLSl5Kho96RQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -442,8 +442,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.0': - resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} + '@esbuild/win32-x64@0.27.1': + resolution: {integrity: sha512-d5X6RMYv6taIymSk8JBP+nxv8DQAMY6A51GPgusqLdK9wBz5wWIXy1KjTck6HnjE9hqJzJRdk+1p/t5soSbCtw==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -467,6 +467,15 @@ packages: eslint: optional: true + '@eslint/compat@2.0.0': + resolution: {integrity: sha512-T9AfE1G1uv4wwq94ozgTGio5EUQBqAVe1X9qsQtSNVEYW6j3hvtZVm8Smr4qL1qDPFg+lOB2cL5RxTRMzq4CTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + '@eslint/config-array@0.21.1': resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -479,6 +488,10 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@1.0.0': + resolution: {integrity: sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/eslintrc@3.3.3': resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -878,63 +891,63 @@ packages: '@types/node@20.19.25': resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} - '@typescript-eslint/eslint-plugin@8.48.0': - resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==} + '@typescript-eslint/eslint-plugin@8.48.1': + resolution: {integrity: sha512-X63hI1bxl5ohelzr0LY5coufyl0LJNthld+abwxpCoo6Gq+hSqhKwci7MUWkXo67mzgUK6YFByhmaHmUcuBJmA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.48.0 + '@typescript-eslint/parser': ^8.48.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.48.0': - resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==} + '@typescript-eslint/parser@8.48.1': + resolution: {integrity: sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.48.0': - resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==} + '@typescript-eslint/project-service@8.48.1': + resolution: {integrity: sha512-HQWSicah4s9z2/HifRPQ6b6R7G+SBx64JlFQpgSSHWPKdvCZX57XCbszg/bapbRsOEv42q5tayTYcEFpACcX1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.48.0': - resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==} + '@typescript-eslint/scope-manager@8.48.1': + resolution: {integrity: sha512-rj4vWQsytQbLxC5Bf4XwZ0/CKd362DkWMUkviT7DCS057SK64D5lH74sSGzhI6PDD2HCEq02xAP9cX68dYyg1w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.48.0': - resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==} + '@typescript-eslint/tsconfig-utils@8.48.1': + resolution: {integrity: sha512-k0Jhs4CpEffIBm6wPaCXBAD7jxBtrHjrSgtfCjUvPp9AZ78lXKdTR8fxyZO5y4vWNlOvYXRtngSZNSn+H53Jkw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.48.0': - resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==} + '@typescript-eslint/type-utils@8.48.1': + resolution: {integrity: sha512-1jEop81a3LrJQLTf/1VfPQdhIY4PlGDBc/i67EVWObrtvcziysbLN3oReexHOM6N3jyXgCrkBsZpqwH0hiDOQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.48.0': - resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} + '@typescript-eslint/types@8.48.1': + resolution: {integrity: sha512-+fZ3LZNeiELGmimrujsDCT4CRIbq5oXdHe7chLiW8qzqyPMnn1puNstCrMNVAqwcl2FdIxkuJ4tOs/RFDBVc/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.48.0': - resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==} + '@typescript-eslint/typescript-estree@8.48.1': + resolution: {integrity: sha512-/9wQ4PqaefTK6POVTjJaYS0bynCgzh6ClJHGSBj06XEHjkfylzB+A3qvyaXnErEZSaxhIo4YdyBgq6j4RysxDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.48.0': - resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==} + '@typescript-eslint/utils@8.48.1': + resolution: {integrity: sha512-fAnhLrDjiVfey5wwFRwrweyRlCmdz5ZxXz2G/4cLn0YDLjTapmN4gcCsTBR1N2rWnZSDeWpYtgLDsJt+FpmcwA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.48.0': - resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==} + '@typescript-eslint/visitor-keys@8.48.1': + resolution: {integrity: sha512-BmxxndzEWhE4TIEEMBs8lP3MBWN3jFPs/p6gPm/wkv02o41hI6cq9AuSmGAaTTHPtA1FTi2jBre4A9rm5ZmX+Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -1252,8 +1265,8 @@ packages: resolution: {integrity: sha512-cTZXBcJMl3pudE40WENOakXkcVtrbBpbkmSkM20NdRiUqa4+VYRdXdEsgQ0BNQ6JBE2YymTNWtPKVF7UCTN5+g==} hasBin: true - changelogithub@13.16.1: - resolution: {integrity: sha512-h4etOmEM/wtqBWKPbnHoqv2C8moRCCEGTckwwWpvRgr/t1tY0MbyjQbZKy1ETQ7gn1UTQMJkCSRQ4KxiQ+HfSQ==} + changelogithub@14.0.0: + resolution: {integrity: sha512-VBuDqqU2si6tNjraFIuUEIVP++y77w+jt5CMnLCXjdP6OEBWUcbNYgIPq+aKQH4Yp2KkGKAjqP8dDmJutICk6g==} engines: {node: '>=12.0.0'} hasBin: true @@ -1442,8 +1455,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.0: - resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + esbuild@0.27.1: + resolution: {integrity: sha512-yY35KZckJJuVVPXpvjgxiCuVEJT67F6zDeVTv4rizyPrfGBUpZQsvmxnN+C371c2esD/hNMjj4tpBhuueLN7aA==} engines: {node: '>=18'} hasBin: true @@ -1527,8 +1540,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 @@ -1778,8 +1791,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} globalthis@1.0.4: @@ -2216,8 +2229,8 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - ofetch@1.4.1: - resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} ohash@1.1.6: resolution: {integrity: sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==} @@ -2330,8 +2343,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier@3.7.3: - resolution: {integrity: sha512-QgODejq9K3OzoBbuyobZlUhznP5SKwPqp+6Q6xw6o8gnhr4O85L2U915iM2IDcfF2NPXVaM9zlo9tdwipnYwzg==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -2621,8 +2634,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.48.0: - resolution: {integrity: sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==} + typescript-eslint@8.48.1: + resolution: {integrity: sha512-FbOKN1fqNoXp1hIl5KYpObVrp0mCn+CLgn479nmu2IsRMrx2vyv74MmsBLVlhg8qVwNFGbXSp8fh1zp8pEoC2A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2874,157 +2887,157 @@ snapshots: '@esbuild/aix-ppc64@0.25.10': optional: true - '@esbuild/aix-ppc64@0.27.0': + '@esbuild/aix-ppc64@0.27.1': optional: true '@esbuild/android-arm64@0.25.10': optional: true - '@esbuild/android-arm64@0.27.0': + '@esbuild/android-arm64@0.27.1': optional: true '@esbuild/android-arm@0.25.10': optional: true - '@esbuild/android-arm@0.27.0': + '@esbuild/android-arm@0.27.1': optional: true '@esbuild/android-x64@0.25.10': optional: true - '@esbuild/android-x64@0.27.0': + '@esbuild/android-x64@0.27.1': optional: true '@esbuild/darwin-arm64@0.25.10': optional: true - '@esbuild/darwin-arm64@0.27.0': + '@esbuild/darwin-arm64@0.27.1': optional: true '@esbuild/darwin-x64@0.25.10': optional: true - '@esbuild/darwin-x64@0.27.0': + '@esbuild/darwin-x64@0.27.1': optional: true '@esbuild/freebsd-arm64@0.25.10': optional: true - '@esbuild/freebsd-arm64@0.27.0': + '@esbuild/freebsd-arm64@0.27.1': optional: true '@esbuild/freebsd-x64@0.25.10': optional: true - '@esbuild/freebsd-x64@0.27.0': + '@esbuild/freebsd-x64@0.27.1': optional: true '@esbuild/linux-arm64@0.25.10': optional: true - '@esbuild/linux-arm64@0.27.0': + '@esbuild/linux-arm64@0.27.1': optional: true '@esbuild/linux-arm@0.25.10': optional: true - '@esbuild/linux-arm@0.27.0': + '@esbuild/linux-arm@0.27.1': optional: true '@esbuild/linux-ia32@0.25.10': optional: true - '@esbuild/linux-ia32@0.27.0': + '@esbuild/linux-ia32@0.27.1': optional: true '@esbuild/linux-loong64@0.25.10': optional: true - '@esbuild/linux-loong64@0.27.0': + '@esbuild/linux-loong64@0.27.1': optional: true '@esbuild/linux-mips64el@0.25.10': optional: true - '@esbuild/linux-mips64el@0.27.0': + '@esbuild/linux-mips64el@0.27.1': optional: true '@esbuild/linux-ppc64@0.25.10': optional: true - '@esbuild/linux-ppc64@0.27.0': + '@esbuild/linux-ppc64@0.27.1': optional: true '@esbuild/linux-riscv64@0.25.10': optional: true - '@esbuild/linux-riscv64@0.27.0': + '@esbuild/linux-riscv64@0.27.1': optional: true '@esbuild/linux-s390x@0.25.10': optional: true - '@esbuild/linux-s390x@0.27.0': + '@esbuild/linux-s390x@0.27.1': optional: true '@esbuild/linux-x64@0.25.10': optional: true - '@esbuild/linux-x64@0.27.0': + '@esbuild/linux-x64@0.27.1': optional: true '@esbuild/netbsd-arm64@0.25.10': optional: true - '@esbuild/netbsd-arm64@0.27.0': + '@esbuild/netbsd-arm64@0.27.1': optional: true '@esbuild/netbsd-x64@0.25.10': optional: true - '@esbuild/netbsd-x64@0.27.0': + '@esbuild/netbsd-x64@0.27.1': optional: true '@esbuild/openbsd-arm64@0.25.10': optional: true - '@esbuild/openbsd-arm64@0.27.0': + '@esbuild/openbsd-arm64@0.27.1': optional: true '@esbuild/openbsd-x64@0.25.10': optional: true - '@esbuild/openbsd-x64@0.27.0': + '@esbuild/openbsd-x64@0.27.1': optional: true '@esbuild/openharmony-arm64@0.25.10': optional: true - '@esbuild/openharmony-arm64@0.27.0': + '@esbuild/openharmony-arm64@0.27.1': optional: true '@esbuild/sunos-x64@0.25.10': optional: true - '@esbuild/sunos-x64@0.27.0': + '@esbuild/sunos-x64@0.27.1': optional: true '@esbuild/win32-arm64@0.25.10': optional: true - '@esbuild/win32-arm64@0.27.0': + '@esbuild/win32-arm64@0.27.1': optional: true '@esbuild/win32-ia32@0.25.10': optional: true - '@esbuild/win32-ia32@0.27.0': + '@esbuild/win32-ia32@0.27.1': optional: true '@esbuild/win32-x64@0.25.10': optional: true - '@esbuild/win32-x64@0.27.0': + '@esbuild/win32-x64@0.27.1': optional: true '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': @@ -3040,6 +3053,12 @@ snapshots: optionalDependencies: eslint: 9.39.1(jiti@2.6.1) + '@eslint/compat@2.0.0(eslint@9.39.1(jiti@2.6.1))': + dependencies: + '@eslint/core': 1.0.0 + optionalDependencies: + eslint: 9.39.1(jiti@2.6.1) + '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 @@ -3056,6 +3075,10 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@1.0.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 @@ -3385,14 +3408,14 @@ snapshots: dependencies: undici-types: 6.21.0 - '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 + '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.48.1 + '@typescript-eslint/type-utils': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.1 eslint: 9.39.1(jiti@2.6.1) graphemer: 1.4.0 ignore: 7.0.5 @@ -3402,41 +3425,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 + '@typescript-eslint/scope-manager': 8.48.1 + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.1 debug: 4.4.3 eslint: 9.39.1(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.48.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.48.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) - '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) + '@typescript-eslint/types': 8.48.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.48.0': + '@typescript-eslint/scope-manager@8.48.1': dependencies: - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/visitor-keys': 8.48.0 + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/visitor-keys': 8.48.1 - '@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.48.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.1(jiti@2.6.1) ts-api-utils: 2.1.0(typescript@5.9.3) @@ -3444,14 +3467,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.48.0': {} + '@typescript-eslint/types@8.48.1': {} - '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.48.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.48.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/visitor-keys': 8.48.0 + '@typescript-eslint/project-service': 8.48.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.48.1(typescript@5.9.3) + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/visitor-keys': 8.48.1 debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 @@ -3461,20 +3484,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.48.1 + '@typescript-eslint/types': 8.48.1 + '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.48.0': + '@typescript-eslint/visitor-keys@8.48.1': dependencies: - '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/types': 8.48.1 eslint-visitor-keys: 4.2.1 '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -3814,7 +3837,7 @@ snapshots: convert-gitmoji: 0.1.5 mri: 1.2.0 node-fetch-native: 1.6.7 - ofetch: 1.4.1 + ofetch: 1.5.1 open: 10.2.0 pathe: 1.1.2 pkg-types: 1.3.1 @@ -3825,7 +3848,7 @@ snapshots: transitivePeerDependencies: - magicast - changelogithub@13.16.1(magicast@0.3.5): + changelogithub@14.0.0(magicast@0.3.5): dependencies: ansis: 4.2.0 c12: 3.3.2(magicast@0.3.5) @@ -3833,7 +3856,7 @@ snapshots: changelogen: 0.5.7(magicast@0.3.5) convert-gitmoji: 0.1.5 execa: 9.6.0 - ofetch: 1.4.1 + ofetch: 1.5.1 semver: 7.7.3 tinyglobby: 0.2.15 transitivePeerDependencies: @@ -4083,34 +4106,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.10 '@esbuild/win32-x64': 0.25.10 - esbuild@0.27.0: + esbuild@0.27.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.0 - '@esbuild/android-arm': 0.27.0 - '@esbuild/android-arm64': 0.27.0 - '@esbuild/android-x64': 0.27.0 - '@esbuild/darwin-arm64': 0.27.0 - '@esbuild/darwin-x64': 0.27.0 - '@esbuild/freebsd-arm64': 0.27.0 - '@esbuild/freebsd-x64': 0.27.0 - '@esbuild/linux-arm': 0.27.0 - '@esbuild/linux-arm64': 0.27.0 - '@esbuild/linux-ia32': 0.27.0 - '@esbuild/linux-loong64': 0.27.0 - '@esbuild/linux-mips64el': 0.27.0 - '@esbuild/linux-ppc64': 0.27.0 - '@esbuild/linux-riscv64': 0.27.0 - '@esbuild/linux-s390x': 0.27.0 - '@esbuild/linux-x64': 0.27.0 - '@esbuild/netbsd-arm64': 0.27.0 - '@esbuild/netbsd-x64': 0.27.0 - '@esbuild/openbsd-arm64': 0.27.0 - '@esbuild/openbsd-x64': 0.27.0 - '@esbuild/openharmony-arm64': 0.27.0 - '@esbuild/sunos-x64': 0.27.0 - '@esbuild/win32-arm64': 0.27.0 - '@esbuild/win32-ia32': 0.27.0 - '@esbuild/win32-x64': 0.27.0 + '@esbuild/aix-ppc64': 0.27.1 + '@esbuild/android-arm': 0.27.1 + '@esbuild/android-arm64': 0.27.1 + '@esbuild/android-x64': 0.27.1 + '@esbuild/darwin-arm64': 0.27.1 + '@esbuild/darwin-x64': 0.27.1 + '@esbuild/freebsd-arm64': 0.27.1 + '@esbuild/freebsd-x64': 0.27.1 + '@esbuild/linux-arm': 0.27.1 + '@esbuild/linux-arm64': 0.27.1 + '@esbuild/linux-ia32': 0.27.1 + '@esbuild/linux-loong64': 0.27.1 + '@esbuild/linux-mips64el': 0.27.1 + '@esbuild/linux-ppc64': 0.27.1 + '@esbuild/linux-riscv64': 0.27.1 + '@esbuild/linux-s390x': 0.27.1 + '@esbuild/linux-x64': 0.27.1 + '@esbuild/netbsd-arm64': 0.27.1 + '@esbuild/netbsd-x64': 0.27.1 + '@esbuild/openbsd-arm64': 0.27.1 + '@esbuild/openbsd-x64': 0.27.1 + '@esbuild/openharmony-arm64': 0.27.1 + '@esbuild/sunos-x64': 0.27.1 + '@esbuild/win32-arm64': 0.27.1 + '@esbuild/win32-ia32': 0.27.1 + '@esbuild/win32-x64': 0.27.1 escalade@3.2.0: {} @@ -4137,7 +4160,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 4.4.3 eslint: 9.39.1(jiti@2.6.1) @@ -4148,19 +4171,19 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -4183,14 +4206,14 @@ snapshots: 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@4.4.4)(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + eslint-plugin-github@6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@eslint/compat': 1.4.1(eslint@9.39.1(jiti@2.6.1)) '@eslint/eslintrc': 3.3.3 '@eslint/js': 9.39.1 '@github/browserslist-config': 1.0.0 - '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) aria-query: 5.3.2 eslint: 9.39.1(jiti@2.6.1) eslint-config-prettier: 10.1.8(eslint@9.39.1(jiti@2.6.1)) @@ -4198,30 +4221,30 @@ snapshots: eslint-plugin-eslint-comments: 3.2.0(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-filenames: 1.3.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-i18n-text: 1.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.3) + eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.4) eslint-rule-documentation: 1.0.23 - globals: 15.15.0 + globals: 16.5.0 jsx-ast-utils: 3.3.5 - prettier: 3.7.3 + prettier: 3.7.4 svg-element-attributes: 1.3.1 - typescript-eslint: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + typescript: 5.9.3 + typescript-eslint: 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - '@types/eslint' - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - - typescript eslint-plugin-i18n-text@1.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) - eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): dependencies: - '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/types': 8.48.1 comment-parser: 1.4.1 debug: 4.4.3 eslint: 9.39.1(jiti@2.6.1) @@ -4232,12 +4255,12 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -4248,7 +4271,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -4260,7 +4283,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -4287,10 +4310,10 @@ snapshots: eslint-plugin-no-only-tests@3.3.0: {} - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.3): + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.4): dependencies: eslint: 9.39.1(jiti@2.6.1) - prettier: 3.7.3 + prettier: 3.7.4 prettier-linter-helpers: 1.0.0 synckit: 0.11.11 optionalDependencies: @@ -4548,7 +4571,7 @@ snapshots: globals@14.0.0: {} - globals@15.15.0: {} + globals@16.5.0: {} globalthis@1.0.4: dependencies: @@ -4974,7 +4997,7 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 - ofetch@1.4.1: + ofetch@1.5.1: dependencies: destr: 2.0.5 node-fetch-native: 1.6.7 @@ -5104,7 +5127,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.7.3: {} + prettier@3.7.4: {} pretty-ms@9.3.0: dependencies: @@ -5453,12 +5476,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.48.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: From d74ab9c4078e71279ad4c49ba2d71b0753a2461d Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Tue, 11 Mar 2025 13:11:14 +1300 Subject: [PATCH 27/32] 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 6f2cd8f..fe7c603 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -557,8 +557,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); @@ -615,8 +621,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); @@ -652,8 +664,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 e44eb30..d2a1739 100644 --- a/src/api.ts +++ b/src/api.ts @@ -217,14 +217,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( @@ -236,7 +236,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 e2c3ab46e710f977e26f09cbd4dbdc541c1b67c7 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Sun, 6 Apr 2025 12:43:00 +1200 Subject: [PATCH 28/32] chore: update eslint packages and config --- eslint.config.mjs | 39 +--- knip.ts | 4 - package.json | 4 - pnpm-lock.yaml | 547 ++++++++++++---------------------------------- 4 files changed, 140 insertions(+), 454 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 2fbdc54..95b882f 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,6 @@ "@actions/github": "^6.0.1" }, "devDependencies": { - "@eslint/compat": "^2.0.0", - "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.39.1", "@octokit/types": "^16.0.0", "@opentf/std": "^0.13.0", @@ -47,8 +45,6 @@ "eslint": "^9.39.1", "eslint-config-prettier": "^10.1.8", "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-github": "^6.0.0", - "eslint-plugin-import": "^2.32.0", "eslint-plugin-import-x": "^4.16.1", "knip": "5.71.0", "prettier": "3.7.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a4e2943..2ccf9aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,12 +15,6 @@ importers: specifier: ^6.0.1 version: 6.0.1 devDependencies: - '@eslint/compat': - specifier: ^2.0.0 - version: 2.0.0(eslint@9.39.1(jiti@2.6.1)) - '@eslint/eslintrc': - specifier: ^3.3.3 - version: 3.3.3 '@eslint/js': specifier: ^9.39.1 version: 9.39.1 @@ -60,12 +54,6 @@ importers: eslint-import-resolver-typescript: specifier: ^4.4.4 version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-github: - specifier: ^6.0.0 - version: 6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: - specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-import-x: specifier: ^4.16.1 version: 4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) @@ -458,24 +446,6 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.4.1': - resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.40 || 9 - peerDependenciesMeta: - eslint: - optional: true - - '@eslint/compat@2.0.0': - resolution: {integrity: sha512-T9AfE1G1uv4wwq94ozgTGio5EUQBqAVe1X9qsQtSNVEYW6j3hvtZVm8Smr4qL1qDPFg+lOB2cL5RxTRMzq4CTA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - peerDependencies: - eslint: ^8.40 || 9 - peerDependenciesMeta: - eslint: - optional: true - '@eslint/config-array@0.21.1': resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -488,10 +458,6 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@1.0.0': - resolution: {integrity: sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/eslintrc@3.3.3': resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -512,9 +478,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'} @@ -740,10 +703,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@rollup/rollup-android-arm-eabi@4.52.3': resolution: {integrity: sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==} cpu: [arm] @@ -876,9 +835,6 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1123,10 +1079,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'} @@ -1155,9 +1107,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - ast-v8-to-istanbul@0.3.5: resolution: {integrity: sha512-9SdXjNheSiE8bALAQCQQuT6fgQaoxJh7IRYrRGZ8/9nv8WhJeC1aXAwN8TbaOssGOukUvyvnkgD9+Yuykvl1aA==} @@ -1169,21 +1118,9 @@ 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==} - baseline-browser-mapping@2.8.9: - resolution: {integrity: sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==} - hasBin: true - before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} @@ -1201,11 +1138,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.26.2: - resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} - 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'} @@ -1246,9 +1178,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001746: - resolution: {integrity: sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==} - chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} @@ -1323,9 +1252,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'} @@ -1410,9 +1336,6 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.228: - resolution: {integrity: sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1460,14 +1383,6 @@ packages: engines: {node: '>=18'} hasBin: true - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - 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'} @@ -1524,33 +1439,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.16.1: resolution: {integrity: sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1574,34 +1462,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.5.4: - resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} - 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.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1661,9 +1521,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'} @@ -1791,10 +1648,6 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@16.5.0: - resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} - engines: {node: '>=18'} - globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -2050,10 +1903,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==} @@ -2065,13 +1914,6 @@ packages: '@types/node': '>=18' typescript: '>=5.0.4 <7' - 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'} @@ -2080,21 +1922,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.2.1: resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} @@ -2184,9 +2014,6 @@ packages: node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - node-releases@2.0.21: - resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2339,10 +2166,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.7.4: resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} @@ -2503,10 +2326,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'} @@ -2554,13 +2373,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.11.11: - resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} - engines: {node: ^14.18.0 || >=16.0.0} - tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} @@ -2670,12 +2482,6 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - 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==} @@ -3047,18 +2853,6 @@ snapshots: '@eslint-community/regexpp@4.12.1': {} - '@eslint/compat@1.4.1(eslint@9.39.1(jiti@2.6.1))': - dependencies: - '@eslint/core': 0.17.0 - optionalDependencies: - eslint: 9.39.1(jiti@2.6.1) - - '@eslint/compat@2.0.0(eslint@9.39.1(jiti@2.6.1))': - dependencies: - '@eslint/core': 1.0.0 - optionalDependencies: - eslint: 9.39.1(jiti@2.6.1) - '@eslint/config-array@0.21.1': dependencies: '@eslint/object-schema': 2.1.7 @@ -3075,10 +2869,6 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/core@1.0.0': - dependencies: - '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 @@ -3104,8 +2894,6 @@ snapshots: '@fastify/busboy@2.1.1': {} - '@github/browserslist-config@1.0.0': {} - '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -3305,8 +3093,6 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.2.9': {} - '@rollup/rollup-android-arm-eabi@4.52.3': optional: true @@ -3373,7 +3159,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.52.3': optional: true - '@rtsao/scc@1.1.0': {} + '@rtsao/scc@1.1.0': + optional: true '@sec-ant/readable-stream@0.4.1': {} @@ -3392,17 +3179,12 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/eslint@9.6.1': - dependencies: - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - optional: true - '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} - '@types/json5@0.0.29': {} + '@types/json5@0.0.29': + optional: true '@types/node@20.19.25': dependencies: @@ -3652,12 +3434,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.9: dependencies: @@ -3669,6 +3450,7 @@ snapshots: get-intrinsic: 1.3.0 is-string: 1.1.1 math-intrinsics: 1.1.0 + optional: true array.prototype.findlastindex@1.2.6: dependencies: @@ -3679,6 +3461,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: @@ -3686,6 +3469,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.0 es-shim-unscopables: 1.1.0 + optional: true array.prototype.flatmap@1.3.3: dependencies: @@ -3693,6 +3477,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.0 es-shim-unscopables: 1.1.0 + optional: true arraybuffer.prototype.slice@1.0.4: dependencies: @@ -3703,31 +3488,26 @@ 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: {} - ast-v8-to-istanbul@0.3.5: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 js-tokens: 9.0.1 - 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: {} - baseline-browser-mapping@2.8.9: {} - before-after-hook@2.2.3: {} binary-extensions@2.3.0: {} @@ -3745,14 +3525,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.26.2: - dependencies: - baseline-browser-mapping: 2.8.9 - caniuse-lite: 1.0.30001746 - electron-to-chromium: 1.5.228 - node-releases: 2.0.21 - update-browserslist-db: 1.1.3(browserslist@4.26.2) - bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -3797,6 +3569,7 @@ snapshots: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 + optional: true call-bind@1.0.8: dependencies: @@ -3804,16 +3577,16 @@ 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: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + optional: true callsites@3.1.0: {} - caniuse-lite@1.0.30001746: {} - chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -3912,29 +3685,31 @@ 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: ms: 2.1.3 + optional: true debug@4.4.3: dependencies: @@ -3956,6 +3731,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: {} @@ -3964,6 +3740,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: {} @@ -3974,6 +3751,7 @@ snapshots: doctrine@2.1.0: dependencies: esutils: 2.0.3 + optional: true dotenv@16.6.1: {} @@ -3984,11 +3762,10 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 + optional: true eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.228: {} - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -4049,16 +3826,20 @@ 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: {} + es-define-property@1.0.1: + optional: true - es-errors@1.3.0: {} + es-errors@1.3.0: + optional: true es-module-lexer@1.7.0: {} es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 + optional: true es-set-tostringtag@2.1.0: dependencies: @@ -4066,16 +3847,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.10: optionalDependencies: @@ -4135,10 +3919,6 @@ snapshots: '@esbuild/win32-ia32': 0.27.1 '@esbuild/win32-x64': 0.27.1 - escalade@3.2.0: {} - - escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)): @@ -4159,6 +3939,7 @@ snapshots: resolve: 1.22.10 transitivePeerDependencies: - supports-color + optional: true eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): dependencies: @@ -4186,61 +3967,7 @@ snapshots: eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color - - eslint-plugin-escompat@3.11.4(eslint@9.39.1(jiti@2.6.1)): - dependencies: - browserslist: 4.26.2 - eslint: 9.39.1(jiti@2.6.1) - - eslint-plugin-eslint-comments@3.2.0(eslint@9.39.1(jiti@2.6.1)): - dependencies: - escape-string-regexp: 1.0.5 - eslint: 9.39.1(jiti@2.6.1) - ignore: 5.3.2 - - eslint-plugin-filenames@1.3.2(eslint@9.39.1(jiti@2.6.1)): - dependencies: - eslint: 9.39.1(jiti@2.6.1) - 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.4.4)(eslint@9.39.1(jiti@2.6.1)): - dependencies: - '@eslint/compat': 1.4.1(eslint@9.39.1(jiti@2.6.1)) - '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.1 - '@github/browserslist-config': 1.0.0 - '@typescript-eslint/eslint-plugin': 8.48.1(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - aria-query: 5.3.2 - eslint: 9.39.1(jiti@2.6.1) - eslint-config-prettier: 10.1.8(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-escompat: 3.11.4(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-eslint-comments: 3.2.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-filenames: 1.3.2(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-i18n-text: 1.0.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.4) - eslint-rule-documentation: 1.0.23 - globals: 16.5.0 - jsx-ast-utils: 3.3.5 - prettier: 3.7.4 - svg-element-attributes: 1.3.1 - typescript: 5.9.3 - typescript-eslint: 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - transitivePeerDependencies: - - '@types/eslint' - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-i18n-text@1.0.1(eslint@9.39.1(jiti@2.6.1)): - dependencies: - eslint: 9.39.1(jiti@2.6.1) + optional: true eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): dependencies: @@ -4288,39 +4015,7 @@ snapshots: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - - eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.1(jiti@2.6.1)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.9 - 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.39.1(jiti@2.6.1) - 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.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.7.4): - dependencies: - eslint: 9.39.1(jiti@2.6.1) - prettier: 3.7.4 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@9.39.1(jiti@2.6.1)) - - eslint-rule-documentation@1.0.23: {} + optional: true eslint-scope@8.4.0: dependencies: @@ -4415,8 +4110,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 @@ -4468,6 +4161,7 @@ snapshots: for-each@0.3.5: dependencies: is-callable: 1.2.7 + optional: true foreground-child@3.3.1: dependencies: @@ -4485,7 +4179,8 @@ snapshots: fsevents@2.3.3: optional: true - function-bind@1.1.2: {} + function-bind@1.1.2: + optional: true function.prototype.name@1.1.8: dependencies: @@ -4495,10 +4190,13 @@ 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 - generator-function@2.0.1: {} + generator-function@2.0.1: + optional: true get-intrinsic@1.3.0: dependencies: @@ -4512,11 +4210,13 @@ snapshots: has-symbols: 1.1.0 hasown: 2.0.2 math-intrinsics: 1.1.0 + optional: true get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + optional: true get-stream@9.0.1: dependencies: @@ -4528,6 +4228,7 @@ snapshots: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 + optional: true get-tsconfig@4.10.1: dependencies: @@ -4571,38 +4272,44 @@ snapshots: globals@14.0.0: {} - globals@16.5.0: {} - globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.2.0 + optional: true - gopd@1.2.0: {} + gopd@1.2.0: + optional: true 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-symbols@1.1.0: + optional: true has-tostringtag@1.0.2: dependencies: has-symbols: 1.1.0 + optional: true hasown@2.0.2: dependencies: function-bind: 1.1.2 + optional: true html-escaper@2.0.2: {} @@ -4624,12 +4331,14 @@ snapshots: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.1.0 + optional: true is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 get-intrinsic: 1.3.0 + optional: true is-async-function@2.1.1: dependencies: @@ -4638,10 +4347,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: @@ -4651,27 +4362,32 @@ snapshots: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 + optional: true is-bun-module@2.0.0: dependencies: semver: 7.7.3 - is-callable@1.2.7: {} + is-callable@1.2.7: + optional: true is-core-module@2.16.1: dependencies: hasown: 2.0.2 + optional: true is-data-view@1.0.2: dependencies: 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: {} @@ -4680,6 +4396,7 @@ snapshots: is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 + optional: true is-fullwidth-code-point@3.0.0: {} @@ -4690,6 +4407,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: @@ -4699,14 +4417,17 @@ snapshots: dependencies: is-docker: 3.0.0 - is-map@2.0.3: {} + is-map@2.0.3: + optional: true - is-negative-zero@2.0.3: {} + is-negative-zero@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: {} @@ -4718,12 +4439,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: {} @@ -4731,35 +4455,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: {} @@ -4809,13 +4540,7 @@ snapshots: json5@1.0.2: dependencies: minimist: 1.2.8 - - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.9 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 + optional: true keyv@4.5.4: dependencies: @@ -4838,12 +4563,6 @@ snapshots: typescript: 5.9.3 zod: 4.1.13 - 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 @@ -4853,16 +4572,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.2.1: {} lru-cache@10.4.3: {} @@ -4881,7 +4592,8 @@ snapshots: dependencies: semver: 7.7.3 - math-intrinsics@1.1.0: {} + math-intrinsics@1.1.0: + optional: true merge2@1.4.1: {} @@ -4938,8 +4650,6 @@ snapshots: node-fetch-native@1.6.7: {} - node-releases@2.0.21: {} - normalize-path@3.0.0: {} npm-run-path@6.0.0: @@ -4964,9 +4674,11 @@ snapshots: pkg-types: 2.3.0 tinyexec: 1.0.1 - object-inspect@1.13.4: {} + object-inspect@1.13.4: + optional: true - object-keys@1.1.1: {} + object-keys@1.1.1: + optional: true object.assign@4.1.7: dependencies: @@ -4976,6 +4688,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: @@ -4983,12 +4696,14 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.0 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.24.0 + optional: true object.values@1.2.1: dependencies: @@ -4996,6 +4711,7 @@ snapshots: call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 + optional: true ofetch@1.5.1: dependencies: @@ -5032,6 +4748,7 @@ snapshots: get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 + optional: true oxc-resolver@11.15.0: optionalDependencies: @@ -5078,7 +4795,8 @@ snapshots: path-key@4.0.0: {} - path-parse@1.0.7: {} + path-parse@1.0.7: + optional: true path-scurry@1.11.1: dependencies: @@ -5113,7 +4831,8 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 - possible-typed-array-names@1.1.0: {} + possible-typed-array-names@1.1.0: + optional: true postcss@8.5.6: dependencies: @@ -5123,10 +4842,6 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: - dependencies: - fast-diff: 1.3.0 - prettier@3.7.4: {} pretty-ms@9.3.0: @@ -5158,6 +4873,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: @@ -5167,6 +4883,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 set-function-name: 2.0.2 + optional: true resolve-from@4.0.0: {} @@ -5177,6 +4894,7 @@ snapshots: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + optional: true reusify@1.1.0: {} @@ -5221,21 +4939,25 @@ snapshots: get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 + optional: true safe-push-apply@1.0.0: 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 scule@1.3.0: {} - semver@6.3.1: {} + semver@6.3.1: + optional: true semver@7.7.3: {} @@ -5247,6 +4969,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: @@ -5254,12 +4977,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 shebang-command@2.0.0: dependencies: @@ -5271,6 +4996,7 @@ snapshots: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 + optional: true side-channel-map@1.0.1: dependencies: @@ -5278,6 +5004,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 object-inspect: 1.13.4 + optional: true side-channel-weakmap@1.0.2: dependencies: @@ -5286,6 +5013,7 @@ snapshots: get-intrinsic: 1.3.0 object-inspect: 1.13.4 side-channel-map: 1.0.1 + optional: true side-channel@1.1.0: dependencies: @@ -5294,6 +5022,7 @@ snapshots: side-channel-list: 1.0.0 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 + optional: true siginfo@2.0.0: {} @@ -5313,6 +5042,7 @@ snapshots: dependencies: es-errors: 1.3.0 internal-slot: 1.1.0 + optional: true string-width@4.2.3: dependencies: @@ -5326,12 +5056,6 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.1.2 - string.prototype.includes@2.0.1: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 @@ -5341,6 +5065,7 @@ snapshots: es-abstract: 1.24.0 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 + optional: true string.prototype.trimend@1.0.9: dependencies: @@ -5348,12 +5073,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: @@ -5363,7 +5090,8 @@ snapshots: dependencies: ansi-regex: 6.2.2 - strip-bom@3.0.0: {} + strip-bom@3.0.0: + optional: true strip-final-newline@4.0.0: {} @@ -5379,13 +5107,8 @@ snapshots: dependencies: has-flag: 4.0.0 - supports-preserve-symlinks-flag@1.0.0: {} - - svg-element-attributes@1.3.1: {} - - synckit@0.11.11: - dependencies: - '@pkgr/core': 0.2.9 + supports-preserve-symlinks-flag@1.0.0: + optional: true tar@6.2.1: dependencies: @@ -5433,6 +5156,7 @@ snapshots: json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 + optional: true tslib@2.8.1: optional: true @@ -5448,6 +5172,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: @@ -5456,6 +5181,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: @@ -5466,6 +5192,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: @@ -5475,6 +5202,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.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): dependencies: @@ -5497,6 +5225,7 @@ snapshots: has-bigints: 1.1.0 has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + optional: true undici-types@6.21.0: {} @@ -5532,12 +5261,6 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.3(browserslist@4.26.2): - dependencies: - browserslist: 4.26.2 - escalade: 3.2.0 - picocolors: 1.1.1 - uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -5627,6 +5350,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: @@ -5643,6 +5367,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: @@ -5650,6 +5375,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: @@ -5660,6 +5386,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-tostringtag: 1.0.2 + optional: true which@2.0.2: dependencies: From e66b3593f5c08cf82de9ed7e45649f1ee11a1ecc Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Mon, 30 Jun 2025 00:29:27 +1200 Subject: [PATCH 29/32] test: fix incorrect mock --- src/test-utils/logging.mock.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test-utils/logging.mock.ts b/src/test-utils/logging.mock.ts index 2e28586..e9e1279 100644 --- a/src/test-utils/logging.mock.ts +++ b/src/test-utils/logging.mock.ts @@ -23,7 +23,7 @@ export function mockLoggingFunctions(): MockedLoggingFunctions { const coreInfoLogMock: MockInstance = vi .spyOn(core, "info") .mockImplementation(() => undefined); - const coreWarningLogMock: MockInstance = vi.spyOn( + const coreWarningLogMock: MockInstance = vi.spyOn( core, "warning", ); From d09ac1463f1fa9ea85f28a60b63e799a5200b92b Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 5 Dec 2025 22:49:30 +1300 Subject: [PATCH 30/32] chore: fix api changes since rebase --- src/api.spec.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/api.spec.ts b/src/api.spec.ts index fe7c603..d45c964 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -175,11 +175,11 @@ describe("API", () => { // Behaviour // First API call will return 200 with an etag response header - const state = await getWorkflowRunState(123456); + const state = await fetchWorkflowRunState(123456); expect(state.conclusion).toStrictEqual("cancelled"); expect(state.status).toStrictEqual("completed"); // Second API call with same parameters should pass the If-None-Match header - const state2 = await getWorkflowRunState(123456); + const state2 = await fetchWorkflowRunState(123456); expect(state2.conclusion).toStrictEqual(mockData.conclusion); expect(state2.status).toStrictEqual(mockData.status); }); @@ -216,11 +216,11 @@ describe("API", () => { // Behaviour // First API call will return 200 with an etag response header - const state = await getWorkflowRunState(123456); + const state = await fetchWorkflowRunState(123456); expect(state.conclusion).toStrictEqual("cancelled"); expect(state.status).toStrictEqual("completed"); // Second API call, without If-None-Match header because of different parameters - const state2 = await getWorkflowRunState(123457); + const state2 = await fetchWorkflowRunState(123457); expect(state2.conclusion).toStrictEqual("cancelled"); expect(state2.status).toStrictEqual("completed"); }); @@ -296,6 +296,7 @@ describe("API", () => { "listJobsForWorkflowRun", ).mockReturnValue( Promise.resolve({ + headers: {}, data: { total_count: 0, jobs: [], From 4d42a4ee56c8cd1c446ce22719aa76230f51459f Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 5 Dec 2025 23:15:23 +1300 Subject: [PATCH 31/32] feat: upgrade to vitest v4 --- package.json | 8 +- pnpm-lock.yaml | 531 ++++++++++++++---------------------------------- src/api.spec.ts | 2 +- 3 files changed, 156 insertions(+), 385 deletions(-) diff --git a/package.json b/package.json index 95b882f..e2ce974 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,9 @@ "license": "MIT", "type": "module", "scripts": { - "build": "pnpm run build:types && pnpm run build:bundle", + "build": "pnpm run check:types && pnpm run build:bundle", "build:bundle": "node ./esbuild.config.mjs", - "build:types": "tsc", + "check:types": "tsc", "format:check": "prettier --check **/*.*", "format": "pnpm run format:check --write", "lint": "eslint .", @@ -38,7 +38,7 @@ "@total-typescript/ts-reset": "^0.6.1", "@types/node": "^20.19.25", "@typescript-eslint/parser": "^8.48.1", - "@vitest/coverage-v8": "^3.2.4", + "@vitest/coverage-v8": "4.0.15", "chalk": "^5.6.2", "changelogithub": "^14.0.0", "esbuild": "^0.27.1", @@ -50,6 +50,6 @@ "prettier": "3.7.4", "typescript": "^5.9.3", "typescript-eslint": "^8.48.1", - "vitest": "^3.2.4" + "vitest": "4.0.15" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ccf9aa..c11a2a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,8 +34,8 @@ importers: specifier: ^8.48.1 version: 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) '@vitest/coverage-v8': - specifier: ^3.2.4 - version: 3.2.4(vitest@3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1)) + specifier: 4.0.15 + version: 4.0.15(vitest@4.0.15(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1)) chalk: specifier: ^5.6.2 version: 5.6.2 @@ -70,8 +70,8 @@ importers: specifier: ^8.48.1 version: 8.48.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) vitest: - specifier: ^3.2.4 - version: 3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) + specifier: 4.0.15 + version: 4.0.15(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) packages: @@ -90,10 +90,6 @@ packages: '@actions/io@1.1.3': resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -102,15 +98,28 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.28.4': resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/types@7.28.4': resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -502,17 +511,6 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -699,10 +697,6 @@ packages: cpu: [x64] os: [win32] - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.52.3': resolution: {integrity: sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==} cpu: [arm] @@ -823,6 +817,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@total-typescript/ts-reset@0.6.1': resolution: {integrity: sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==} @@ -1001,43 +998,43 @@ packages: cpu: [x64] os: [win32] - '@vitest/coverage-v8@3.2.4': - resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} + '@vitest/coverage-v8@4.0.15': + resolution: {integrity: sha512-FUJ+1RkpTFW7rQITdgTi93qOCWJobWhBirEPCeXh2SW2wsTlFxy51apDz5gzG+ZEYt/THvWeNmhdAoS9DTwpCw==} peerDependencies: - '@vitest/browser': 3.2.4 - vitest: 3.2.4 + '@vitest/browser': 4.0.15 + vitest: 4.0.15 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@vitest/expect@4.0.15': + resolution: {integrity: sha512-Gfyva9/GxPAWXIWjyGDli9O+waHDC0Q0jaLdFP1qPAUUfo1FEXPXUfUkp3eZA0sSq340vPycSyOlYUeM15Ft1w==} - '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + '@vitest/mocker@4.0.15': + resolution: {integrity: sha512-CZ28GLfOEIFkvCFngN8Sfx5h+Se0zN+h4B7yOsPVCcgtiO7t5jt9xQh2E1UkFep+eb9fjyMfuC5gBypwb07fvQ==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + '@vitest/pretty-format@4.0.15': + resolution: {integrity: sha512-SWdqR8vEv83WtZcrfLNqlqeQXlQLh2iilO1Wk1gv4eiHKjEzvgHb2OVc3mIPyhZE6F+CtfYjNlDJwP5MN6Km7A==} - '@vitest/runner@3.2.4': - resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/runner@4.0.15': + resolution: {integrity: sha512-+A+yMY8dGixUhHmNdPUxOh0la6uVzun86vAbuMT3hIDxMrAOmn5ILBHm8ajrqHE0t8R9T1dGnde1A5DTnmi3qw==} - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + '@vitest/snapshot@4.0.15': + resolution: {integrity: sha512-A7Ob8EdFZJIBjLjeO0DZF4lqR6U7Ydi5/5LIZ0xcI+23lYlsYJAfGn8PrIWTYdZQRNnSRlzhg0zyGu37mVdy5g==} - '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + '@vitest/spy@4.0.15': + resolution: {integrity: sha512-+EIjOJmnY6mIfdXtE/bnozKEvTC4Uczg19yeZ2vtCz5Yyb0QQ31QWVQ8hswJ3Ysx/K2EqaNsVanjr//2+P3FHw==} - '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@vitest/utils@4.0.15': + resolution: {integrity: sha512-HXjPW2w5dxhTD0dLwtYHDnelK3j8sR8cWIaLxr22evTyY6q8pRCjZSmhRWVjBaOVXChQd6AwMzi9pucorXCPZA==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -1052,22 +1049,10 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - ansis@4.2.0: resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} engines: {node: '>=14'} @@ -1103,12 +1088,8 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - ast-v8-to-istanbul@0.3.5: - resolution: {integrity: sha512-9SdXjNheSiE8bALAQCQQuT6fgQaoxJh7IRYrRGZ8/9nv8WhJeC1aXAwN8TbaOssGOukUvyvnkgD9+Yuykvl1aA==} + ast-v8-to-istanbul@0.3.8: + resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} @@ -1178,8 +1159,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} engines: {node: '>=18'} chalk@4.1.2: @@ -1199,10 +1180,6 @@ packages: engines: {node: '>=12.0.0'} hasBin: true - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} - chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -1281,10 +1258,6 @@ packages: supports-color: optional: true - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -1333,15 +1306,6 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - es-abstract@1.24.0: resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} engines: {node: '>= 0.4'} @@ -1573,10 +1537,6 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - formatly@0.3.0: resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} engines: {node: '>=18.3.0'} @@ -1640,10 +1600,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -1765,10 +1721,6 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -1872,9 +1824,6 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true @@ -1925,18 +1874,15 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - magic-string@0.30.19: - resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -1975,10 +1921,6 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} @@ -2056,6 +1998,9 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -2091,9 +2036,6 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2117,20 +2059,12 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} - perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -2311,6 +2245,9 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -2318,14 +2255,6 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - string.prototype.trim@1.2.10: resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} engines: {node: '>= 0.4'} @@ -2338,14 +2267,6 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} @@ -2362,9 +2283,6 @@ packages: resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} engines: {node: '>=14.16'} - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2377,10 +2295,6 @@ packages: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - test-exclude@7.0.1: - resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} - engines: {node: '>=18'} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -2390,20 +2304,16 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} - - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} to-regex-range@5.0.1: @@ -2485,11 +2395,6 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - vite@7.1.7: resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2530,26 +2435,32 @@ packages: yaml: optional: true - vitest@3.2.4: - resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vitest@4.0.15: + resolution: {integrity: sha512-n1RxDp8UJm6N0IbJLQo+yzLZ2sQCDyl1o0LeugbPWf8+8Fttp29GghsQBjYJVmWq3gBFfe9Hs1spR44vovn2wA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.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.2.4 - '@vitest/ui': 3.2.4 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.15 + '@vitest/browser-preview': 4.0.15 + '@vitest/browser-webdriverio': 4.0.15 + '@vitest/ui': 4.0.15 happy-dom: '*' jsdom: '*' peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/debug': + '@opentelemetry/api': optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': optional: true '@vitest/ui': optional: true @@ -2592,14 +2503,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -2654,23 +2557,32 @@ snapshots: '@actions/io@1.1.3': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.27.1': + optional: true + + '@babel/helper-validator-identifier@7.28.5': {} '@babel/parser@7.28.4': dependencies: '@babel/types': 7.28.4 + optional: true + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 '@babel/types@7.28.4': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 + optional: true + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@bcoe/v8-coverage@1.0.2': {} @@ -2911,22 +2823,6 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} @@ -3090,9 +2986,6 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.15.0': optional: true - '@pkgjs/parseargs@0.11.0': - optional: true - '@rollup/rollup-android-arm-eabi@4.52.3': optional: true @@ -3166,6 +3059,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.0.0': {} + '@total-typescript/ts-reset@0.6.1': {} '@tybys/wasm-util@0.10.1': @@ -3341,66 +3236,61 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitest/coverage-v8@3.2.4(vitest@3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1))': + '@vitest/coverage-v8@4.0.15(vitest@4.0.15(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1))': dependencies: - '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - ast-v8-to-istanbul: 0.3.5 - debug: 4.4.3 + '@vitest/utils': 4.0.15 + ast-v8-to-istanbul: 0.3.8 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.2.0 - magic-string: 0.30.19 - magicast: 0.3.5 - std-env: 3.9.0 - test-exclude: 7.0.1 - tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) + magicast: 0.5.1 + obug: 2.1.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.15(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@vitest/expect@3.2.4': + '@vitest/expect@4.0.15': dependencies: + '@standard-schema/spec': 1.0.0 '@types/chai': 5.2.2 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - tinyrainbow: 2.0.0 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + chai: 6.2.1 + tinyrainbow: 3.0.3 - '@vitest/mocker@3.2.4(vite@7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1))': + '@vitest/mocker@4.0.15(vite@7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1))': dependencies: - '@vitest/spy': 3.2.4 + '@vitest/spy': 4.0.15 estree-walker: 3.0.3 - magic-string: 0.30.19 + magic-string: 0.30.21 optionalDependencies: vite: 7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) - '@vitest/pretty-format@3.2.4': + '@vitest/pretty-format@4.0.15': dependencies: - tinyrainbow: 2.0.0 + tinyrainbow: 3.0.3 - '@vitest/runner@3.2.4': + '@vitest/runner@4.0.15': dependencies: - '@vitest/utils': 3.2.4 + '@vitest/utils': 4.0.15 pathe: 2.0.3 - strip-literal: 3.1.0 - '@vitest/snapshot@3.2.4': + '@vitest/snapshot@4.0.15': dependencies: - '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.19 + '@vitest/pretty-format': 4.0.15 + magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@3.2.4': - dependencies: - tinyspy: 4.0.4 + '@vitest/spy@4.0.15': {} - '@vitest/utils@3.2.4': + '@vitest/utils@4.0.15': dependencies: - '@vitest/pretty-format': 3.2.4 - loupe: 3.2.1 - tinyrainbow: 2.0.0 + '@vitest/pretty-format': 4.0.15 + tinyrainbow: 3.0.3 acorn-jsx@5.3.2(acorn@8.15.0): dependencies: @@ -3415,16 +3305,10 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.3: {} - ansis@4.2.0: {} anymatch@3.1.3: @@ -3490,9 +3374,7 @@ snapshots: is-array-buffer: 3.0.5 optional: true - assertion-error@2.0.1: {} - - ast-v8-to-istanbul@0.3.5: + ast-v8-to-istanbul@0.3.8: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -3587,13 +3469,7 @@ snapshots: callsites@3.1.0: {} - chai@5.3.3: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 + chai@6.2.1: {} chalk@4.1.2: dependencies: @@ -3635,8 +3511,6 @@ snapshots: transitivePeerDependencies: - magicast - check-error@2.1.1: {} - chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -3715,8 +3589,6 @@ snapshots: dependencies: ms: 2.1.3 - deep-eql@5.0.2: {} - deep-is@0.1.4: {} default-browser-id@5.0.0: {} @@ -3764,12 +3636,6 @@ snapshots: gopd: 1.2.0 optional: true - eastasianwidth@0.2.0: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - es-abstract@1.24.0: dependencies: array-buffer-byte-length: 1.0.2 @@ -4163,11 +4029,6 @@ snapshots: is-callable: 1.2.7 optional: true - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - formatly@0.3.0: dependencies: fd-package-json: 2.0.0 @@ -4261,15 +4122,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - globals@14.0.0: {} globalthis@1.0.4: @@ -4398,8 +4250,6 @@ snapshots: call-bound: 1.0.4 optional: true - is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -4515,12 +4365,6 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jiti@1.21.7: {} jiti@2.6.1: {} @@ -4574,11 +4418,7 @@ snapshots: lodash.merge@4.6.2: {} - loupe@3.2.1: {} - - lru-cache@10.4.3: {} - - magic-string@0.30.19: + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4587,6 +4427,13 @@ snapshots: '@babel/parser': 7.28.4 '@babel/types': 7.28.4 source-map-js: 1.2.1 + optional: true + + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 make-dir@4.0.0: dependencies: @@ -4622,8 +4469,6 @@ snapshots: minipass@5.0.0: {} - minipass@7.1.2: {} - minizlib@2.1.2: dependencies: minipass: 3.3.6 @@ -4713,6 +4558,8 @@ snapshots: es-object-atoms: 1.1.1 optional: true + obug@2.1.1: {} + ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -4781,8 +4628,6 @@ snapshots: dependencies: p-limit: 3.1.0 - package-json-from-dist@1.0.1: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -4798,17 +4643,10 @@ snapshots: path-parse@1.0.7: optional: true - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - pathe@1.1.2: {} pathe@2.0.3: {} - pathval@2.0.1: {} - perfect-debounce@1.0.0: {} perfect-debounce@2.0.0: {} @@ -5036,6 +4874,8 @@ snapshots: stackback@0.0.2: {} + std-env@3.10.0: {} + std-env@3.9.0: {} stop-iteration-iterator@1.1.0: @@ -5044,18 +4884,6 @@ snapshots: internal-slot: 1.1.0 optional: true - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.2 - string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 @@ -5082,14 +4910,6 @@ snapshots: es-object-atoms: 1.1.1 optional: true - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - strip-bom@3.0.0: optional: true @@ -5099,10 +4919,6 @@ snapshots: strip-json-comments@5.0.3: {} - strip-literal@3.1.0: - dependencies: - js-tokens: 9.0.1 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -5119,28 +4935,20 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - test-exclude@7.0.1: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 10.4.5 - minimatch: 9.0.5 - tinybench@2.9.0: {} tinyexec@0.3.2: {} tinyexec@1.0.1: {} + tinyexec@1.0.2: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tinypool@1.1.1: {} - - tinyrainbow@2.0.0: {} - - tinyspy@4.0.4: {} + tinyrainbow@3.0.3: {} to-regex-range@5.0.1: dependencies: @@ -5265,27 +5073,6 @@ snapshots: dependencies: punycode: 2.3.1 - vite-node@3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1): - dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - vite@7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1): dependencies: esbuild: 0.25.10 @@ -5300,30 +5087,27 @@ snapshots: jiti: 2.6.1 yaml: 2.8.1 - vitest@3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1): + vitest@4.0.15(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1): dependencies: - '@types/chai': 5.2.2 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3 + '@vitest/expect': 4.0.15 + '@vitest/mocker': 4.0.15(vite@7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.15 + '@vitest/runner': 4.0.15 + '@vitest/snapshot': 4.0.15 + '@vitest/spy': 4.0.15 + '@vitest/utils': 4.0.15 + es-module-lexer: 1.7.0 expect-type: 1.2.2 - magic-string: 0.30.19 + magic-string: 0.30.21 + obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.9.0 + std-env: 3.10.0 tinybench: 2.9.0 - tinyexec: 0.3.2 + tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 + tinyrainbow: 3.0.3 vite: 7.1.7(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@20.19.25)(jiti@2.6.1)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.19.25 @@ -5336,7 +5120,6 @@ snapshots: - sass-embedded - stylus - sugarss - - supports-color - terser - tsx - yaml @@ -5399,18 +5182,6 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.1.2 - wrappy@1.0.2: {} wsl-utils@0.1.0: diff --git a/src/api.spec.ts b/src/api.spec.ts index d45c964..8010b99 100644 --- a/src/api.spec.ts +++ b/src/api.spec.ts @@ -733,7 +733,7 @@ describe("API", () => { expect(coreWarningLogMock).toHaveBeenCalledOnce(); expect(coreWarningLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot(` "retryOnError: An unexpected error has occurred: - name: spy + name: Mock error: some error" `); expect(coreWarningLogMock.mock.calls[0]?.[0]).toContain(testFunc.name); From e6719df7d589052ded04c9c713d5c81430e9b3d3 Mon Sep 17 00:00:00 2001 From: Alex Miller Date: Fri, 5 Dec 2025 23:15:51 +1300 Subject: [PATCH 32/32] progress --- src/main.spec.ts | 286 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.ts | 4 +- 2 files changed, 289 insertions(+), 1 deletion(-) create mode 100644 src/main.spec.ts diff --git a/src/main.spec.ts b/src/main.spec.ts new file mode 100644 index 0000000..eda8cfa --- /dev/null +++ b/src/main.spec.ts @@ -0,0 +1,286 @@ +import * as core from "@actions/core"; +import { + afterAll, + afterEach, + beforeEach, + describe, + expect, + it, + vi, + type MockInstance, +} from "vitest"; + +import * as action from "./action.ts"; +import * as api from "./api.ts"; +import * as awaitRemoteRun from "./await-remote-run.ts"; +import { main } from "./main.ts"; +import { mockLoggingFunctions } from "./test-utils/logging.mock.ts"; +import type { Result } from "./types.ts"; +// import * as utils from "./utils.ts"; + +vi.mock("@actions/core"); +vi.mock("./action.ts"); +vi.mock("./api.ts"); +vi.mock("./await-remote-run.ts"); +// vi.mock("./utils.ts"); + +describe("main", () => { + const { + coreDebugLogMock, + coreErrorLogMock, + coreInfoLogMock, + assertOnlyCalled, + } = mockLoggingFunctions(); + const testCfg: action.ActionConfig = { + token: "secret", + repo: "repository", + owner: "owner", + runId: 123456, + runTimeoutSeconds: 300, + pollIntervalMs: 2500, + } satisfies Partial as action.ActionConfig; + + // Core + let coreSetFailedMock: MockInstance; + + // Action + let actionGetConfigMock: MockInstance; + + // API + let apiFetchWorkflowRunActiveJobUrlRetry: MockInstance< + typeof api.fetchWorkflowRunActiveJobUrlRetry + >; + let apiInitMock: MockInstance; + + // Utils + // let utilsSleepMock: MockInstance; + + // Await Remote Run + let awaitRemoteRunGetWorkflowRunStatusResult: MockInstance< + typeof awaitRemoteRun.getWorkflowRunStatusResult + >; + let awaitRemoteRunGetWorkflowRunConclusionResult: MockInstance< + typeof awaitRemoteRun.getWorkflowRunConclusionResult + >; + let awaitRemoteRunHandleActionFail: MockInstance< + typeof awaitRemoteRun.handleActionFail + >; + let awaitRemoteRunGetWorkflowRunResult: MockInstance< + typeof awaitRemoteRun.getWorkflowRunResult + >; + + afterAll(() => { + vi.restoreAllMocks(); + }); + + beforeEach(() => { + vi.useFakeTimers(); + + coreSetFailedMock = vi.spyOn(core, "setFailed"); + + actionGetConfigMock = vi + .spyOn(action, "getConfig") + .mockReturnValue(testCfg); + + apiFetchWorkflowRunActiveJobUrlRetry = vi.spyOn( + api, + "fetchWorkflowRunActiveJobUrlRetry", + ); + apiInitMock = vi.spyOn(api, "init"); + + // utilsGetBranchNameMock = vi.spyOn(utils, "getBranchName"); + // utilsLogInfoForBranchNameResult = vi.spyOn( + // utils, + // "logInfoForBranchNameResult", + // ); + // utilsCreateDistinctIdRegexMock = vi.spyOn(utils, "createDistinctIdRegex"); + + awaitRemoteRunGetWorkflowRunStatusResult = vi.spyOn( + awaitRemoteRun, + "getWorkflowRunStatusResult", + ); + awaitRemoteRunGetWorkflowRunConclusionResult = vi.spyOn( + awaitRemoteRun, + "getWorkflowRunConclusionResult", + ); + awaitRemoteRunHandleActionFail = vi.spyOn( + awaitRemoteRun, + "handleActionFail", + ); + awaitRemoteRunGetWorkflowRunResult = vi.spyOn( + awaitRemoteRun, + "getWorkflowRunResult", + ); + }); + + afterEach(() => { + vi.useRealTimers(); + vi.resetAllMocks(); + }); + + it("should successfully complete", async () => { + // const distinctIdRegex = new RegExp(testCfg.distinctId); + // const returnDispatchSuccessResult = { + // success: true, + // value: { + // id: 0, + // url: "test-url", + // }, + // } as const; + + // utilsCreateDistinctIdRegexMock.mockReturnValue(distinctIdRegex); + // returnDispatchGetWorkflowIdMock.mockResolvedValue(0); + // returnDispatchGetRunIdAndUrlMock.mockResolvedValue( + // returnDispatchSuccessResult, + // ); + const apiFetchWorkflowRunActiveJobUrlRetryResult: Result = { + success: true, + value: "test-url", + }; + apiFetchWorkflowRunActiveJobUrlRetry.mockResolvedValue( + apiFetchWorkflowRunActiveJobUrlRetryResult, + ); + + await main(); + + // Behaviour + // Setup + expect(actionGetConfigMock).toHaveBeenCalledOnce(); + expect(apiInitMock).toHaveBeenCalledOnce(); + expect(apiInitMock).toHaveBeenCalledWith(testCfg); + + // Active Job URL + expect(apiFetchWorkflowRunActiveJobUrlRetry).toHaveBeenCalledOnce(); + + // Workflow ID + // expect(returnDispatchGetWorkflowIdMock).toHaveBeenCalledOnce(); + // expect(returnDispatchGetWorkflowIdMock).toHaveBeenCalledWith( + // testCfg.workflow, + // ); + + // Dispatch + // expect(apiDispatchWorkflowMock).toHaveBeenCalledOnce(); + // expect(apiDispatchWorkflowMock).toHaveBeenCalledWith(testCfg.distinctId); + + // Branch name + // expect(utilsGetBranchNameMock).toHaveBeenCalledOnce(); + // expect(utilsGetBranchNameMock).toHaveBeenCalledWith(testCfg.ref); + // expect(utilsLogInfoForBranchNameResult).toHaveBeenCalledOnce(); + // expect(utilsLogInfoForBranchNameResult).toHaveBeenCalledWith( + // testBranch, + // testCfg.ref, + // ); + // expect(utilsCreateDistinctIdRegexMock).toHaveBeenCalledOnce(); + // expect(utilsCreateDistinctIdRegexMock).toHaveBeenCalledWith( + // testCfg.distinctId, + // ); + + // Get run ID + // expect(returnDispatchGetRunIdAndUrlMock).toHaveBeenCalledOnce(); + // expect(returnDispatchGetRunIdAndUrlMock).toHaveBeenCalledWith({ + // startTime: Date.now(), + // branch: testBranch, + // distinctIdRegex: distinctIdRegex, + // workflowId: 0, + // workflowTimeoutMs: testCfg.workflowTimeoutSeconds * 1000, + // workflowJobStepsRetryMs: testCfg.workflowJobStepsRetrySeconds * 1000, + // }); + + // Result + expect(coreSetFailedMock).not.toHaveBeenCalled(); + // expect(returnDispatchHandleFailMock).not.toHaveBeenCalled(); + // expect(returnDispatchHandleSuccessMock).toHaveBeenCalledOnce(); + // expect(returnDispatchHandleSuccessMock).toHaveBeenCalledWith( + // returnDispatchSuccessResult.value.id, + // returnDispatchSuccessResult.value.url, + // ); + + // Logging + assertOnlyCalled(coreInfoLogMock, coreDebugLogMock); + expect(coreInfoLogMock).toHaveBeenCalledTimes(2); + expect(coreInfoLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + `"Attempt to extract branch name from ref..."`, + ); + expect(coreInfoLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot( + `"Attempting to identify run ID from steps..."`, + ); + expect(coreDebugLogMock).toHaveBeenCalledTimes(2); + expect(coreDebugLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + `"Attempting to identify run ID for test-workflow (0)"`, + ); + expect(coreDebugLogMock.mock.calls[1]?.[0]).toMatchInlineSnapshot( + `"Completed (0ms)"`, + ); + }); + + // it("should fail for an unhandled error", async () => { + // const testError = new Error("test error"); + // actionGetConfigMock.mockImplementation(() => { + // throw testError; + // }); + + // await main(); + + // // Behaviour + // expect(actionGetConfigMock).toHaveBeenCalledOnce(); + + // expect(apiInitMock).not.toHaveBeenCalled(); + // expect(returnDispatchGetWorkflowIdMock).not.toHaveBeenCalled(); + // expect(apiDispatchWorkflowMock).not.toHaveBeenCalled(); + // expect(utilsGetBranchNameMock).not.toHaveBeenCalled(); + // expect(utilsLogInfoForBranchNameResult).not.toHaveBeenCalled(); + // expect(returnDispatchGetRunIdAndUrlMock).not.toHaveBeenCalled(); + // expect(returnDispatchHandleFailMock).not.toHaveBeenCalled(); + // expect(returnDispatchHandleSuccessMock).not.toHaveBeenCalled(); + + // expect(coreSetFailedMock).toHaveBeenCalledOnce(); + // expect(coreSetFailedMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + // `"Failed: An unhandled error has occurred: test error"`, + // ); + + // // Logging + // assertOnlyCalled(coreDebugLogMock, coreErrorLogMock); + // expect(coreErrorLogMock).toHaveBeenCalledOnce(); + // expect(coreErrorLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + // `"Failed: An unhandled error has occurred: test error"`, + // ); + // expect(coreDebugLogMock).toHaveBeenCalledOnce(); + // expect(coreDebugLogMock.mock.calls[0]?.[0]).toStrictEqual(testError.stack); + // }); + + // it("should fail for an unhandled unknown", async () => { + // const testError = "some other error"; + // actionGetConfigMock.mockImplementation(() => { + // // eslint-disable-next-line @typescript-eslint/only-throw-error + // throw testError; + // }); + + // await main(); + + // // Behaviour + // expect(actionGetConfigMock).toHaveBeenCalledOnce(); + + // expect(apiInitMock).not.toHaveBeenCalled(); + // expect(returnDispatchGetWorkflowIdMock).not.toHaveBeenCalled(); + // expect(apiDispatchWorkflowMock).not.toHaveBeenCalled(); + // expect(utilsGetBranchNameMock).not.toHaveBeenCalled(); + // expect(utilsLogInfoForBranchNameResult).not.toHaveBeenCalled(); + // expect(returnDispatchGetRunIdAndUrlMock).not.toHaveBeenCalled(); + // expect(returnDispatchHandleFailMock).not.toHaveBeenCalled(); + // expect(returnDispatchHandleSuccessMock).not.toHaveBeenCalled(); + + // expect(coreSetFailedMock).toHaveBeenCalledOnce(); + // expect(coreSetFailedMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + // `"Failed: An unknown error has occurred: some other error"`, + // ); + + // // Logging + // assertOnlyCalled(coreDebugLogMock, coreErrorLogMock); + // expect(coreErrorLogMock).toHaveBeenCalledOnce(); + // expect(coreErrorLogMock.mock.calls[0]?.[0]).toMatchInlineSnapshot( + // `"Failed: An unknown error has occurred: some other error"`, + // ); + // expect(coreDebugLogMock).toHaveBeenCalledOnce(); + // expect(coreDebugLogMock.mock.calls[0]?.[0]).toStrictEqual(testError); + // }); +}); diff --git a/src/main.ts b/src/main.ts index 451a562..4692536 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,13 +6,14 @@ import { getWorkflowRunResult, handleActionFail } from "./await-remote-run.ts"; import * as constants from "./constants.ts"; import { WorkflowRunConclusion } from "./types.ts"; -async function main(): Promise { +export async function main(): Promise { try { const startTime = Date.now(); const config = getConfig(); api.init(config); + // Attempt to get the active job URL const activeJobUrlResult = await api.fetchWorkflowRunActiveJobUrlRetry( config.runId, constants.WORKFLOW_RUN_ACTIVE_JOB_TIMEOUT_MS, @@ -29,6 +30,7 @@ async function main(): Promise { ` URL: ${activeJobUrlResult.value}`, ); + // Await the result const runResult = await getWorkflowRunResult({ startTime, pollIntervalMs: config.pollIntervalMs,