diff --git a/.changeset/plenty-pots-tell.md b/.changeset/plenty-pots-tell.md new file mode 100644 index 00000000..ae12a947 --- /dev/null +++ b/.changeset/plenty-pots-tell.md @@ -0,0 +1,5 @@ +--- +"@open-pioneer/core": minor +--- + +Add `destroyResources(resources)` to destroy an array of resources. diff --git a/src/packages/core/index.ts b/src/packages/core/index.ts index 01251191..30747019 100644 --- a/src/packages/core/index.ts +++ b/src/packages/core/index.ts @@ -9,6 +9,6 @@ export { rethrowAbortError } from "./error"; export { EventEmitter, type EventSource, type EventNames } from "./events"; -export { destroyResource, type Resource } from "./resources"; +export { destroyResource, destroyResources, type Resource } from "./resources"; export { createLogger, type Logger, type LogLevel, type LogMethod } from "./Logger"; export { createManualPromise, type ManualPromise } from "./utils"; diff --git a/src/packages/core/resources.test.ts b/src/packages/core/resources.test.ts new file mode 100644 index 00000000..07ea82fe --- /dev/null +++ b/src/packages/core/resources.test.ts @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer) +// SPDX-License-Identifier: Apache-2.0 +import { expect, it } from "vitest"; +import { destroyResources, Resource } from "./resources"; + +it("destroys all resources in the array", () => { + const events: string[] = []; + const resources: Resource[] = [ + { + destroy() { + events.push("destroyed 1"); + } + }, + { + destroy() { + events.push("destroyed 2"); + } + } + ]; + + destroyResources(resources); + expect(events).toEqual(["destroyed 2", "destroyed 1"]); // reverse order +}); + +it("handles cycles correctly", () => { + const events: string[] = []; + const resources: Resource[] = [ + { + destroy() { + events.push("destroyed 1"); + destroyResources(resources); // still only destroys each resource only once + } + }, + { + destroy() { + events.push("destroyed 2"); + } + } + ]; + + destroyResources(resources); + expect(events).toEqual(["destroyed 2", "destroyed 1"]); +}); diff --git a/src/packages/core/resources.ts b/src/packages/core/resources.ts index 6bbcd6a7..c69d3a1a 100644 --- a/src/packages/core/resources.ts +++ b/src/packages/core/resources.ts @@ -27,3 +27,19 @@ export function destroyResource(resource: R | undefined): un resource?.destroy(); return undefined; } + +/** + * A helper function that invokes `destroy()` on each resource in the given array. + * + * This function destroys the resources in reverse order (starting from the last element). + * This is done to reverse the order of construction. + * + * The array will be cleared by this function. + */ +export function destroyResources(resources: R[]): void { + // `destroy()` might call us in a cycle, so we modify the array in place. + let resource: Resource | undefined; + while ((resource = resources.pop())) { + resource.destroy(); + } +}