@ecopages/file-system 0.2.0-alpha.0 → 0.2.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@ecopages/file-system` are documented here.
4
+
5
+ > **Note:** Changelog tracking begins at version `0.2.0`. Changes prior to this release are not recorded here but are available in the git history.
6
+
7
+ ## [UNRELEASED] — TBD
8
+
9
+ ### Refactoring
10
+
11
+ - `package.json` updated to add `fs` as an explicit peer dependency, reflecting the package's role in the runtime-agnostic file abstraction layer used by the Node adapter.
package/package.json CHANGED
@@ -1,60 +1,59 @@
1
1
  {
2
- "name": "@ecopages/file-system",
3
- "version": "0.2.0-alpha.0",
4
- "description": "Runtime-agnostic file system utilities for Ecopages with Bun and Node adapters",
5
- "keywords": [
6
- "ecopages",
7
- "fs",
8
- "file-system",
9
- "bun",
10
- "node"
11
- ],
12
- "license": "MIT",
13
- "main": "./src/index.ts",
14
- "type": "module",
15
- "files": [
16
- "src"
17
- ],
18
- "repository": {
19
- "type": "git",
20
- "url": "https://github.com/ecopages/ecopages.git",
21
- "directory": "packages/file-system"
22
- },
23
- "scripts": {
24
- "typecheck": "tsc --noEmit",
25
- "test": "bun test",
26
- "benchmark": "bun run test/benchmark.ts",
27
- "release:jsr": "bunx jsr publish"
28
- },
29
- "exports": {
30
- ".": {
31
- "import": "./src/index.ts",
32
- "require": "./src/index.ts",
33
- "types": "./src/types.ts"
34
- },
35
- "./bun": {
36
- "import": "./src/adapters/bun.ts",
37
- "types": "./src/adapters/bun.ts"
38
- },
39
- "./node": {
40
- "import": "./src/adapters/node.ts",
41
- "types": "./src/adapters/node.ts"
42
- }
43
- },
44
- "dependencies": {
45
- "fast-glob": "^3.3.2"
46
- },
47
- "devDependencies": {
48
- "@types/bun": "^1.3.9",
49
- "@types/node": "^24",
50
- "mitata": "^1.0.34"
51
- },
52
- "peerDependencies": {
53
- "bun": ">=1.0.0"
54
- },
55
- "peerDependenciesMeta": {
56
- "bun": {
57
- "optional": true
58
- }
59
- }
2
+ "name": "@ecopages/file-system",
3
+ "version": "0.2.0-alpha.2",
4
+ "description": "Runtime-agnostic file system utilities for Ecopages with Bun and Node adapters",
5
+ "keywords": [
6
+ "ecopages",
7
+ "fs",
8
+ "file-system",
9
+ "bun",
10
+ "node"
11
+ ],
12
+ "license": "MIT",
13
+ "main": "./src/index.js",
14
+ "type": "module",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/ecopages/ecopages.git",
18
+ "directory": "packages/file-system"
19
+ },
20
+ "exports": {
21
+ ".": {
22
+ "import": "./src/index.js",
23
+ "types": "./src/types.d.ts",
24
+ "default": "./src/index.js"
25
+ },
26
+ "./bun": {
27
+ "import": "./src/adapters/bun.js",
28
+ "types": "./src/adapters/bun.d.ts",
29
+ "default": "./src/adapters/bun.js"
30
+ },
31
+ "./node": {
32
+ "import": "./src/adapters/node.js",
33
+ "types": "./src/adapters/node.d.ts",
34
+ "default": "./src/adapters/node.js"
35
+ },
36
+ "./bun.ts": {
37
+ "import": "./src/adapters/bun.js",
38
+ "types": "./src/adapters/bun.d.ts",
39
+ "default": "./src/adapters/bun.js"
40
+ },
41
+ "./node.ts": {
42
+ "import": "./src/adapters/node.js",
43
+ "types": "./src/adapters/node.d.ts",
44
+ "default": "./src/adapters/node.js"
45
+ }
46
+ },
47
+ "dependencies": {
48
+ "fast-glob": "^3.3.2"
49
+ },
50
+ "peerDependencies": {
51
+ "bun": ">=1.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "bun": {
55
+ "optional": true
56
+ }
57
+ },
58
+ "types": "./src/types.d.ts"
60
59
  }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @module @ecopages/file-system/adapters/bun
3
+ * @description Bun-optimized file system adapter using Bun.Glob, Bun.hash, and Bun.file.
4
+ */
5
+ import type { FileSystem, GlobOptions } from '../types.js';
6
+ import { BaseFileSystem } from '../utils/common.js';
7
+ /**
8
+ * Bun-optimized implementation of the FileSystem interface.
9
+ */
10
+ export declare class BunFileSystem extends BaseFileSystem implements FileSystem {
11
+ existsAsync(path: string): Promise<boolean>;
12
+ readFile(path: string): Promise<string>;
13
+ writeAsync(filepath: string, contents: string | Buffer): Promise<void>;
14
+ copyFileAsync(source: string, destination: string): Promise<void>;
15
+ glob(patterns: string[], options?: GlobOptions): Promise<string[]>;
16
+ hash(path: string): string;
17
+ }
18
+ /**
19
+ * Singleton instance for Bun runtime.
20
+ */
21
+ export declare const bunFs: BunFileSystem;
@@ -0,0 +1,63 @@
1
+ import { dirname } from "node:path";
2
+ import { BaseFileSystem } from "../utils/common.js";
3
+ class BunFileSystem extends BaseFileSystem {
4
+ async existsAsync(path) {
5
+ return Bun.file(path).exists();
6
+ }
7
+ async readFile(path) {
8
+ try {
9
+ this.verifyFileExists(path);
10
+ return await Bun.file(path).text();
11
+ } catch (error) {
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ throw new Error(`Error reading file: ${path}, ${message}`);
14
+ }
15
+ }
16
+ async writeAsync(filepath, contents) {
17
+ try {
18
+ await this.ensureDirAsync(dirname(filepath));
19
+ await Bun.write(filepath, contents);
20
+ } catch (error) {
21
+ const message = error instanceof Error ? error.message : String(error);
22
+ throw new Error(`Error writing file: ${filepath}. Cause: ${message}`);
23
+ }
24
+ }
25
+ async copyFileAsync(source, destination) {
26
+ try {
27
+ await Bun.write(destination, Bun.file(source));
28
+ } catch (error) {
29
+ const message = error instanceof Error ? error.message : String(error);
30
+ throw new Error(`Error copying file: ${source} to ${destination}. Cause: ${message}`);
31
+ }
32
+ }
33
+ async glob(patterns, options = {}) {
34
+ const scanOptions = {
35
+ cwd: options.cwd ?? process.cwd()
36
+ };
37
+ const promises = patterns.map((pattern) => {
38
+ const glob = new Bun.Glob(pattern);
39
+ return Array.fromAsync(glob.scan(scanOptions));
40
+ });
41
+ const results = await Promise.all(promises);
42
+ let files = results.flat();
43
+ if (options.ignore?.length) {
44
+ const ignoreGlobs = options.ignore.map((pattern) => new Bun.Glob(pattern));
45
+ files = files.filter((file) => !ignoreGlobs.some((glob) => glob.match(file)));
46
+ }
47
+ return files;
48
+ }
49
+ hash(path) {
50
+ try {
51
+ const buffer = this.readFileAsBuffer(path);
52
+ return Bun.hash(buffer).toString();
53
+ } catch (error) {
54
+ const message = error instanceof Error ? error.message : String(error);
55
+ throw new Error(`Error hashing file: ${path}. Cause: ${message}`);
56
+ }
57
+ }
58
+ }
59
+ const bunFs = new BunFileSystem();
60
+ export {
61
+ BunFileSystem,
62
+ bunFs
63
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @module @ecopages/file-system/adapters/node
3
+ * @description Node.js file system adapter using fast-glob, crypto, and node:fs.
4
+ */
5
+ import type { FileSystem, GlobOptions } from '../types.js';
6
+ import { BaseFileSystem } from '../utils/common.js';
7
+ /**
8
+ * Node.js implementation of the FileSystem interface.
9
+ */
10
+ export declare class NodeFileSystem extends BaseFileSystem implements FileSystem {
11
+ existsAsync(filePath: string): Promise<boolean>;
12
+ readFile(path: string): Promise<string>;
13
+ writeAsync(filepath: string, contents: string | Buffer): Promise<void>;
14
+ copyFileAsync(source: string, destination: string): Promise<void>;
15
+ glob(patterns: string[], options?: GlobOptions): Promise<string[]>;
16
+ hash(path: string): string;
17
+ }
18
+ /**
19
+ * Singleton instance for Node.js runtime.
20
+ */
21
+ export declare const nodeFs: NodeFileSystem;
@@ -0,0 +1,62 @@
1
+ import crypto from "node:crypto";
2
+ import {
3
+ access as accessAsync,
4
+ cp as cpAsync,
5
+ readFile as readFileAsync,
6
+ writeFile as writeFileAsync
7
+ } from "node:fs/promises";
8
+ import { dirname } from "node:path";
9
+ import fg from "fast-glob";
10
+ import { BaseFileSystem } from "../utils/common.js";
11
+ class NodeFileSystem extends BaseFileSystem {
12
+ async existsAsync(filePath) {
13
+ try {
14
+ await accessAsync(filePath);
15
+ return true;
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+ async readFile(path) {
21
+ try {
22
+ this.verifyFileExists(path);
23
+ return await readFileAsync(path, "utf-8");
24
+ } catch (error) {
25
+ const message = error instanceof Error ? error.message : String(error);
26
+ throw new Error(`Error reading file: ${path}, ${message}`);
27
+ }
28
+ }
29
+ async writeAsync(filepath, contents) {
30
+ try {
31
+ await this.ensureDirAsync(dirname(filepath));
32
+ await writeFileAsync(filepath, contents);
33
+ } catch (error) {
34
+ const message = error instanceof Error ? error.message : String(error);
35
+ throw new Error(`Error writing file: ${filepath}. Cause: ${message}`);
36
+ }
37
+ }
38
+ async copyFileAsync(source, destination) {
39
+ await cpAsync(source, destination);
40
+ }
41
+ async glob(patterns, options = {}) {
42
+ return fg(patterns, {
43
+ cwd: options.cwd ?? process.cwd(),
44
+ ignore: options.ignore,
45
+ ...options
46
+ });
47
+ }
48
+ hash(path) {
49
+ try {
50
+ const buffer = this.readFileAsBuffer(path);
51
+ return crypto.createHash("sha256").update(buffer).digest("hex");
52
+ } catch (error) {
53
+ const message = error instanceof Error ? error.message : String(error);
54
+ throw new Error(`Error hashing file: ${path}. Cause: ${message}`);
55
+ }
56
+ }
57
+ }
58
+ const nodeFs = new NodeFileSystem();
59
+ export {
60
+ NodeFileSystem,
61
+ nodeFs
62
+ };
package/src/index.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @module @ecopages/file-system
3
+ * @description Runtime-agnostic file system utilities for Ecopages.
4
+ *
5
+ * Automatically selects the optimal adapter based on runtime:
6
+ * - **Bun**: Uses `Bun.Glob`, `Bun.hash`, `Bun.file` for maximum performance
7
+ * - **Node.js**: Uses `fast-glob` and `crypto` for compatibility
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { fileSystem } from '@ecopages/file-system';
12
+ *
13
+ * const files = await fileSystem.glob(['**\/*.ts']);
14
+ * const content = await fileSystem.readFile('file.txt');
15
+ */
16
+ export * from './types.js';
17
+ export { BaseFileSystem } from './utils/common.js';
18
+ export { BunFileSystem, bunFs } from './adapters/bun.js';
19
+ export { NodeFileSystem, nodeFs } from './adapters/node.js';
20
+ import type { FileSystem } from './types.js';
21
+ /**
22
+ * Runtime-agnostic file system instance.
23
+ * Automatically uses Bun or Node adapter based on environment.
24
+ */
25
+ export declare const fileSystem: FileSystem;
package/src/index.js ADDED
@@ -0,0 +1,21 @@
1
+ export * from "./types.js";
2
+ import { BaseFileSystem } from "./utils/common.js";
3
+ import { BunFileSystem, bunFs } from "./adapters/bun.js";
4
+ import { NodeFileSystem, nodeFs } from "./adapters/node.js";
5
+ async function createFileSystem() {
6
+ if (typeof Bun !== "undefined") {
7
+ const { bunFs: bunFs2 } = await import("./adapters/bun.js");
8
+ return bunFs2;
9
+ }
10
+ const { nodeFs: nodeFs2 } = await import("./adapters/node.js");
11
+ return nodeFs2;
12
+ }
13
+ const fileSystem = await createFileSystem();
14
+ export {
15
+ BaseFileSystem,
16
+ BunFileSystem,
17
+ NodeFileSystem,
18
+ bunFs,
19
+ fileSystem,
20
+ nodeFs
21
+ };
package/src/types.d.ts ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * @module @ecopages/file-system/types
3
+ * @description Type definitions for runtime-agnostic file system operations.
4
+ */
5
+ /**
6
+ * Options for glob pattern matching.
7
+ */
8
+ export interface GlobOptions {
9
+ /** Working directory for glob patterns */
10
+ cwd?: string;
11
+ /** Patterns to ignore */
12
+ ignore?: string[];
13
+ }
14
+ /**
15
+ * Runtime-agnostic file system interface.
16
+ * Implementations exist for Bun (optimized) and Node.js (fallback).
17
+ */
18
+ export interface FileSystem {
19
+ /**
20
+ * Scan the file system for files matching glob patterns.
21
+ * @param patterns - Glob patterns to match
22
+ * @param options - Glob options
23
+ */
24
+ glob(patterns: string[], options?: GlobOptions): Promise<string[]>;
25
+ /**
26
+ * Read a file as a string asynchronously.
27
+ * @param path - Path to the file
28
+ */
29
+ readFile(path: string): Promise<string>;
30
+ /**
31
+ * Read a file as a string synchronously.
32
+ * @param path - Path to the file
33
+ */
34
+ readFileSync(path: string): string;
35
+ /**
36
+ * Read a file as a Buffer.
37
+ * @param path - Path to the file
38
+ */
39
+ readFileAsBuffer(path: string): Buffer;
40
+ /**
41
+ * Write contents to a file synchronously.
42
+ * Creates parent directories if they don't exist.
43
+ * @param filepath - Path to write to
44
+ * @param contents - Content to write
45
+ */
46
+ write(filepath: string, contents: string | Buffer): void;
47
+ /**
48
+ * Write contents to a file asynchronously.
49
+ * @param filepath - Path to write to
50
+ * @param contents - Content to write
51
+ */
52
+ writeAsync(filepath: string, contents: string | Buffer): Promise<void>;
53
+ /**
54
+ * Ensure a directory exists, optionally cleaning it first.
55
+ * @param dirPath - Directory path
56
+ * @param forceCleanup - If true, remove existing directory first
57
+ */
58
+ ensureDir(dirPath: string, forceCleanup?: boolean): void;
59
+ /**
60
+ * Ensure a directory exists asynchronously, optionally cleaning it first.
61
+ * @param dirPath - Directory path
62
+ * @param forceCleanup - If true, remove existing directory first
63
+ */
64
+ ensureDirAsync(dirPath: string, forceCleanup?: boolean): Promise<void>;
65
+ /**
66
+ * Copy a directory recursively.
67
+ * @param source - Source directory
68
+ * @param destination - Destination directory
69
+ */
70
+ copyDir(source: string, destination: string): void;
71
+ /**
72
+ * Copy a directory recursively asynchronously.
73
+ * @param source - Source directory
74
+ * @param destination - Destination directory
75
+ */
76
+ copyDirAsync(source: string, destination: string): Promise<void>;
77
+ /**
78
+ * Remove all contents of a directory.
79
+ * @param path - Directory path
80
+ */
81
+ emptyDir(path: string): void;
82
+ /**
83
+ * Remove all contents of a directory asynchronously.
84
+ * @param path - Directory path
85
+ */
86
+ emptyDirAsync(path: string): Promise<void>;
87
+ /**
88
+ * Check if a path is a directory.
89
+ * @param path - Path to check
90
+ */
91
+ isDirectory(path: string): boolean;
92
+ /**
93
+ * Check if a path is a directory asynchronously.
94
+ * @param path - Path to check
95
+ */
96
+ isDirectoryAsync(path: string): Promise<boolean>;
97
+ /**
98
+ * Check if a file or directory exists.
99
+ * @param path - Path to check
100
+ */
101
+ exists(path: string): boolean;
102
+ /**
103
+ * Check if a file or directory exists asynchronously.
104
+ * @param path - Path to check
105
+ */
106
+ existsAsync(path: string): Promise<boolean>;
107
+ /**
108
+ * Copy a file.
109
+ * @param source - Source file path
110
+ * @param destination - Destination file path
111
+ */
112
+ copyFile(source: string, destination: string): void;
113
+ /**
114
+ * Copy a file asynchronously.
115
+ * @param source - Source file path
116
+ * @param destination - Destination file path
117
+ */
118
+ copyFileAsync(source: string, destination: string): Promise<void>;
119
+ /**
120
+ * Remove a file or directory synchronously.
121
+ * @param path - Path to remove
122
+ */
123
+ remove(path: string): void;
124
+ /**
125
+ * Remove a file or directory asynchronously.
126
+ * @param path - Path to remove
127
+ */
128
+ removeAsync(path: string): Promise<void>;
129
+ /**
130
+ * Get a hash of a file's contents.
131
+ * @param path - Path to the file
132
+ */
133
+ hash(path: string): string;
134
+ /**
135
+ * Gzip a single file.
136
+ * @param path - Path to the file
137
+ */
138
+ gzipFile(path: string): void;
139
+ /**
140
+ * Gzip all files with specified extensions in a directory.
141
+ * @param path - Directory path
142
+ * @param extensions - File extensions to gzip (without dot)
143
+ */
144
+ gzipDir(path: string, extensions: string[]): void;
145
+ /**
146
+ * Verify that a file exists, throw FileNotFoundError if not.
147
+ * @param path - Path to verify
148
+ * @throws FileNotFoundError
149
+ */
150
+ verifyFileExists(path: string): void;
151
+ }
152
+ /**
153
+ * Error thrown when a file is not found.
154
+ */
155
+ export declare class FileNotFoundError extends Error {
156
+ code: string;
157
+ constructor(path: string);
158
+ }
package/src/types.js ADDED
@@ -0,0 +1,10 @@
1
+ class FileNotFoundError extends Error {
2
+ code = "ENOENT";
3
+ constructor(path) {
4
+ super(`File: ${path} not found`);
5
+ this.name = "FileNotFoundError";
6
+ }
7
+ }
8
+ export {
9
+ FileNotFoundError
10
+ };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * @module @ecopages/file-system/utils/common
3
+ * @description Shared utilities used by both Bun and Node adapters.
4
+ */
5
+ import { type GlobOptions } from '../types.js';
6
+ /**
7
+ * Base implementation for file system operations.
8
+ * Subclasses override specific methods with runtime-optimized versions.
9
+ */
10
+ export declare abstract class BaseFileSystem {
11
+ /**
12
+ * Check if a file or directory exists.
13
+ */
14
+ exists(filePath: string): boolean;
15
+ /**
16
+ * Verify that a file exists, throw FileNotFoundError if not.
17
+ */
18
+ verifyFileExists(filePath: string): void;
19
+ /**
20
+ * Read a file as a string synchronously.
21
+ */
22
+ readFileSync(filePath: string): string;
23
+ /**
24
+ * Read a file as a Buffer.
25
+ */
26
+ readFileAsBuffer(filePath: string): Buffer;
27
+ /**
28
+ * Write contents to a file synchronously.
29
+ */
30
+ write(filepath: string, contents: string | Buffer): void;
31
+ /**
32
+ * Ensure a directory exists.
33
+ */
34
+ ensureDir(dirPath: string, forceCleanup?: boolean): void;
35
+ /**
36
+ * Ensure a directory exists asynchronously.
37
+ */
38
+ ensureDirAsync(dirPath: string, forceCleanup?: boolean): Promise<void>;
39
+ /**
40
+ * Copy a directory recursively.
41
+ */
42
+ copyDir(source: string, destination: string): void;
43
+ /**
44
+ * Copy a directory recursively asynchronously.
45
+ */
46
+ copyDirAsync(source: string, destination: string): Promise<void>;
47
+ /**
48
+ * Copy a file.
49
+ */
50
+ copyFile(source: string, destination: string): void;
51
+ /**
52
+ * Remove all contents of a directory.
53
+ */
54
+ emptyDir(dirPath: string): void;
55
+ /**
56
+ * Remove a directory's contents asynchronously.
57
+ */
58
+ emptyDirAsync(dirPath: string): Promise<void>;
59
+ /**
60
+ * Remove a file or directory synchronously.
61
+ */
62
+ remove(filePath: string): void;
63
+ /**
64
+ * Remove a file or directory asynchronously.
65
+ */
66
+ removeAsync(filePath: string): Promise<void>;
67
+ /**
68
+ * Check if a path is a directory.
69
+ */
70
+ isDirectory(filePath: string): boolean;
71
+ /**
72
+ * Check if a path is a directory asynchronously.
73
+ */
74
+ isDirectoryAsync(filePath: string): Promise<boolean>;
75
+ /**
76
+ * Gzip a single file.
77
+ */
78
+ gzipFile(filePath: string): void;
79
+ /**
80
+ * Gzip all files with specified extensions in a directory.
81
+ */
82
+ gzipDir(dirPath: string, extensionsToGzip: string[]): void;
83
+ /**
84
+ * Glob patterns - must be implemented by runtime-specific adapters.
85
+ */
86
+ abstract glob(patterns: string[], options?: GlobOptions): Promise<string[]>;
87
+ /**
88
+ * Read file async - must be implemented by runtime-specific adapters.
89
+ */
90
+ abstract readFile(path: string): Promise<string>;
91
+ /**
92
+ * Write file async - must be implemented by runtime-specific adapters.
93
+ */
94
+ abstract writeAsync(filepath: string, contents: string | Buffer): Promise<void>;
95
+ /**
96
+ * Check if a file exists asynchronously - must be implemented by runtime-specific adapters.
97
+ */
98
+ abstract existsAsync(filePath: string): Promise<boolean>;
99
+ /**
100
+ * Copy a file asynchronously - must be implemented by runtime-specific adapters.
101
+ */
102
+ abstract copyFileAsync(source: string, destination: string): Promise<void>;
103
+ /**
104
+ * Hash file - must be implemented by runtime-specific adapters.
105
+ */
106
+ abstract hash(path: string): string;
107
+ }
@@ -0,0 +1,169 @@
1
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
+ import { cp as cpAsync, mkdir as mkdirAsync, readdir, rm as rmAsync, stat as statAsync } from "node:fs/promises";
3
+ import { dirname, extname, join as pathJoin } from "node:path";
4
+ import zlib from "node:zlib";
5
+ import { FileNotFoundError } from "../types.js";
6
+ class BaseFileSystem {
7
+ /**
8
+ * Check if a file or directory exists.
9
+ */
10
+ exists(filePath) {
11
+ return existsSync(filePath);
12
+ }
13
+ /**
14
+ * Verify that a file exists, throw FileNotFoundError if not.
15
+ */
16
+ verifyFileExists(filePath) {
17
+ if (!this.exists(filePath)) {
18
+ throw new FileNotFoundError(filePath);
19
+ }
20
+ }
21
+ /**
22
+ * Read a file as a string synchronously.
23
+ */
24
+ readFileSync(filePath) {
25
+ try {
26
+ return readFileSync(filePath, "utf-8");
27
+ } catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ throw new Error(`Error reading file: ${filePath}, ${message}`, { cause: error });
30
+ }
31
+ }
32
+ /**
33
+ * Read a file as a Buffer.
34
+ */
35
+ readFileAsBuffer(filePath) {
36
+ try {
37
+ return readFileSync(filePath);
38
+ } catch (error) {
39
+ const message = error instanceof Error ? error.message : String(error);
40
+ throw new Error(`Error reading file: ${filePath}, ${message}`, { cause: error });
41
+ }
42
+ }
43
+ /**
44
+ * Write contents to a file synchronously.
45
+ */
46
+ write(filepath, contents) {
47
+ try {
48
+ this.ensureDir(dirname(filepath));
49
+ writeFileSync(filepath, contents);
50
+ } catch (error) {
51
+ const message = error instanceof Error ? error.message : String(error);
52
+ throw new Error(`Error writing file: ${filepath}. Cause: ${message}`);
53
+ }
54
+ }
55
+ /**
56
+ * Ensure a directory exists.
57
+ */
58
+ ensureDir(dirPath, forceCleanup) {
59
+ if (forceCleanup && existsSync(dirPath)) {
60
+ rmSync(dirPath, { recursive: true, force: true });
61
+ }
62
+ mkdirSync(dirPath, { recursive: true });
63
+ }
64
+ /**
65
+ * Ensure a directory exists asynchronously.
66
+ */
67
+ async ensureDirAsync(dirPath, forceCleanup) {
68
+ if (forceCleanup && await this.existsAsync(dirPath)) {
69
+ await rmAsync(dirPath, { recursive: true, force: true });
70
+ }
71
+ await mkdirAsync(dirPath, { recursive: true });
72
+ }
73
+ /**
74
+ * Copy a directory recursively.
75
+ */
76
+ copyDir(source, destination) {
77
+ cpSync(source, destination, { recursive: true });
78
+ }
79
+ /**
80
+ * Copy a directory recursively asynchronously.
81
+ */
82
+ async copyDirAsync(source, destination) {
83
+ await cpAsync(source, destination, { recursive: true });
84
+ }
85
+ /**
86
+ * Copy a file.
87
+ */
88
+ copyFile(source, destination) {
89
+ cpSync(source, destination);
90
+ }
91
+ /**
92
+ * Remove all contents of a directory.
93
+ */
94
+ emptyDir(dirPath) {
95
+ if (!this.isDirectory(dirPath)) {
96
+ return;
97
+ }
98
+ const entries = readdirSync(dirPath);
99
+ for (const entry of entries) {
100
+ const fullPath = pathJoin(dirPath, entry);
101
+ rmSync(fullPath, { recursive: true, force: true });
102
+ }
103
+ }
104
+ /**
105
+ * Remove a directory's contents asynchronously.
106
+ */
107
+ async emptyDirAsync(dirPath) {
108
+ if (!await this.isDirectoryAsync(dirPath)) {
109
+ return;
110
+ }
111
+ const entries = await readdir(dirPath);
112
+ await Promise.all(entries.map((entry) => rmAsync(pathJoin(dirPath, entry), { recursive: true, force: true })));
113
+ }
114
+ /**
115
+ * Remove a file or directory synchronously.
116
+ */
117
+ remove(filePath) {
118
+ rmSync(filePath, { recursive: true, force: true });
119
+ }
120
+ /**
121
+ * Remove a file or directory asynchronously.
122
+ */
123
+ async removeAsync(filePath) {
124
+ await rmAsync(filePath, { recursive: true, force: true });
125
+ }
126
+ /**
127
+ * Check if a path is a directory.
128
+ */
129
+ isDirectory(filePath) {
130
+ return existsSync(filePath) && statSync(filePath).isDirectory();
131
+ }
132
+ /**
133
+ * Check if a path is a directory asynchronously.
134
+ */
135
+ async isDirectoryAsync(filePath) {
136
+ try {
137
+ const stats = await statAsync(filePath);
138
+ return stats.isDirectory();
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+ /**
144
+ * Gzip a single file.
145
+ */
146
+ gzipFile(filePath) {
147
+ const data = this.readFileAsBuffer(filePath);
148
+ const compressedData = zlib.gzipSync(Buffer.from(data));
149
+ const gzipFile = `${filePath}.gz`;
150
+ writeFileSync(gzipFile, compressedData);
151
+ }
152
+ /**
153
+ * Gzip all files with specified extensions in a directory.
154
+ */
155
+ gzipDir(dirPath, extensionsToGzip) {
156
+ const entries = readdirSync(dirPath, { recursive: true, withFileTypes: true });
157
+ for (const entry of entries) {
158
+ if (entry.isFile()) {
159
+ const ext = extname(entry.name).slice(1);
160
+ if (extensionsToGzip.includes(ext)) {
161
+ this.gzipFile(pathJoin(entry.parentPath, entry.name));
162
+ }
163
+ }
164
+ }
165
+ }
166
+ }
167
+ export {
168
+ BaseFileSystem
169
+ };