/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import type { MaybePromise } from "../util/Promises.js"; import { File } from "./File.js"; import { FilesystemNode } from "./FilesystemNode.js"; /** * Abstract handle to a directory. * * A Directory is a handle -- the underlying entry need not exist yet. */ export abstract class Directory extends FilesystemNode { readonly kind = "directory"; /** * Return the native filesystem path for this directory, if available. Throws by default; platform-specific * subclasses (e.g. NodeJsDirectory) override this. */ get path(): string { throw new Error("This directory does not expose a native path"); } abstract entries(): AsyncIterable; abstract file(name: string): File; abstract directory(name: string): Directory; abstract mkdir(): Promise; /** * Recursively copy a filesystem node into this directory. String arguments are resolved relative to this directory. */ abstract copy(source: string | FilesystemNode, target: string | FilesystemNode): Promise; /** * Open an existing entry at a relative path. Returns a {@link File} or {@link Directory} based on what exists, * or throws {@link FileNotFoundError} if the path does not exist. */ async open(path: string): Promise { const segments = path.split(/[/\\]/).filter(s => s !== ""); if (segments.length === 0) { return this; } let current: Directory = this; for (let i = 0; i < segments.length - 1; i++) { current = current.directory(segments[i]); } const lastName = segments[segments.length - 1]; const handle = current.file(lastName); const s = await handle.stat(); if (s.type === "directory") { return current.directory(lastName); } return handle; } /** * Get a {@link File} handle at a relative path. The file need not exist yet. */ create(path: string): File { const segments = path.split(/[/\\]/).filter(s => s !== ""); let current: Directory = this; for (let i = 0; i < segments.length - 1; i++) { current = current.directory(segments[i]); } return current.file(segments[segments.length - 1]); } /** * List file names in this directory. */ async files(): Promise { const result = Array(); for await (const entry of this.entries()) { if (entry.kind === "file") { result.push(entry.name); } } return result; } /** * List subdirectory names in this directory. */ async directories(): Promise { const result = Array(); for await (const entry of this.entries()) { if (entry.kind === "directory") { result.push(entry.name); } } return result; } /** * Acquire an exclusive lock on this directory. Returns a release function. * * The default implementation is a no-op. Platform-specific subclasses (e.g. NodeJsDirectory) override this to * provide real locking with stale-lock detection. */ lock(): MaybePromise<() => MaybePromise> { return () => {}; } } export namespace Directory { export type Entry = File | Directory; }