@statewalker/webrun-files 0.3.1 → 0.4.0
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/dist/cjs/index.cjs +564 -0
- package/dist/esm/index.js +551 -0
- package/dist/files-api.d.ts +44 -0
- package/dist/files-api.d.ts.map +1 -0
- package/dist/impl/index.d.ts +6 -0
- package/dist/impl/index.d.ts.map +1 -0
- package/dist/impl/mem-files-api.d.ts +13 -0
- package/dist/impl/mem-files-api.d.ts.map +1 -0
- package/dist/impl/node-files-api.d.ts +24 -0
- package/dist/impl/node-files-api.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/collect-stream.d.ts +16 -0
- package/dist/utils/collect-stream.d.ts.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/normalize-path.d.ts +21 -0
- package/dist/utils/normalize-path.d.ts.map +1 -0
- package/dist/utils/path-utils.d.ts +20 -0
- package/dist/utils/path-utils.d.ts.map +1 -0
- package/package.json +29 -31
- package/src/files-api.ts +167 -0
- package/src/impl/index.ts +6 -0
- package/src/impl/mem-files-api.ts +306 -0
- package/src/impl/node-files-api.ts +261 -0
- package/src/index.ts +35 -0
- package/src/types.ts +68 -0
- package/src/utils/collect-stream.ts +35 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/normalize-path.ts +34 -0
- package/src/utils/path-utils.ts +46 -0
- package/CHANGELOG.md +0 -2
- package/README.md +0 -17
- package/dist/index-umd.js +0 -1054
- package/dist/index-umd.min.js +0 -2
- package/dist/index.js +0 -1033
- package/dist/index.min.js +0 -2
- package/index.js +0 -1
- package/src/BrowserFilesApi.js +0 -335
- package/src/FilesApi.js +0 -133
- package/src/MemFilesApi.js +0 -129
- package/src/NodeFilesApi.js +0 -222
- package/src/addFilesApiLogger.js +0 -25
- package/src/getMimeType.js +0 -19
- package/src/index.js +0 -10
- package/src/mimeTypes.js +0 -96
- package/src/normalizePath.js +0 -8
- package/src/notImplemented.js +0 -3
- package/src/openBrowserFilesApi.js +0 -67
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for IFilesApi
|
|
3
|
+
*/
|
|
4
|
+
export type FileKind = "file" | "directory";
|
|
5
|
+
export interface FileInfo {
|
|
6
|
+
kind: FileKind;
|
|
7
|
+
name: string;
|
|
8
|
+
path: string;
|
|
9
|
+
type?: string;
|
|
10
|
+
size?: number;
|
|
11
|
+
lastModified: number;
|
|
12
|
+
}
|
|
13
|
+
export type FileRef = string | {
|
|
14
|
+
path: string;
|
|
15
|
+
};
|
|
16
|
+
export interface ListOptions {
|
|
17
|
+
recursive?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface CopyOptions {
|
|
20
|
+
recursive?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface AppendOptions {
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
}
|
|
25
|
+
export interface ReadStreamOptions {
|
|
26
|
+
start?: number;
|
|
27
|
+
end?: number;
|
|
28
|
+
signal?: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
export interface WriteStreamOptions {
|
|
31
|
+
start?: number;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
export type BinaryStream = AsyncIterable<Uint8Array> | Iterable<Uint8Array>;
|
|
35
|
+
export interface FileHandle {
|
|
36
|
+
readonly size: number;
|
|
37
|
+
close(): Promise<void>;
|
|
38
|
+
appendFile(data: BinaryStream, options?: AppendOptions): Promise<number>;
|
|
39
|
+
createReadStream(options?: ReadStreamOptions): AsyncGenerator<Uint8Array>;
|
|
40
|
+
createWriteStream(data: BinaryStream, options?: WriteStreamOptions): Promise<number>;
|
|
41
|
+
chmod?(mode: number): Promise<void>;
|
|
42
|
+
chown?(uid: number, gid: number): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Core filesystem interface - minimal contract for implementations.
|
|
46
|
+
* Platform-specific implementations should implement this interface.
|
|
47
|
+
*/
|
|
48
|
+
export interface IFilesApi {
|
|
49
|
+
list(file: FileRef, options?: ListOptions): AsyncGenerator<FileInfo>;
|
|
50
|
+
stats(file: FileRef): Promise<FileInfo | undefined>;
|
|
51
|
+
remove(file: FileRef): Promise<boolean>;
|
|
52
|
+
open(file: FileRef): Promise<FileHandle>;
|
|
53
|
+
move?(source: FileRef, target: FileRef): Promise<boolean>;
|
|
54
|
+
copy?(source: FileRef, target: FileRef, options?: CopyOptions): Promise<boolean>;
|
|
55
|
+
mkdir?(file: FileRef): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE5C,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhD,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;AAE5E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzE,gBAAgB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;IAC1E,iBAAiB,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAErF,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrE,KAAK,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IACpD,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjF,KAAK,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stream collection utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Collects all values from an async generator into an array.
|
|
6
|
+
*/
|
|
7
|
+
export declare function collectGenerator<T>(generator: AsyncIterable<T>): Promise<T[]>;
|
|
8
|
+
/**
|
|
9
|
+
* Converts a synchronous iterable to an async iterable.
|
|
10
|
+
*/
|
|
11
|
+
export declare function toAsyncIterable<T>(iterable: Iterable<T>): AsyncIterable<T>;
|
|
12
|
+
/**
|
|
13
|
+
* Normalizes BinaryStream to AsyncIterable<Uint8Array>.
|
|
14
|
+
*/
|
|
15
|
+
export declare function toBinaryAsyncIterable(data: AsyncIterable<Uint8Array> | Iterable<Uint8Array>): AsyncIterable<Uint8Array>;
|
|
16
|
+
//# sourceMappingURL=collect-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-stream.d.ts","sourceRoot":"","sources":["../../src/utils/collect-stream.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAMnF;AAED;;GAEG;AACH,wBAAuB,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAIjF;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,GACrD,aAAa,CAAC,UAAU,CAAC,CAK3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path normalization utilities
|
|
3
|
+
*/
|
|
4
|
+
import type { FileRef } from "../types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes a file path to a consistent format.
|
|
7
|
+
* - Adds leading slash if missing
|
|
8
|
+
* - Removes trailing slash
|
|
9
|
+
* - Removes `.` segments
|
|
10
|
+
* - Collapses multiple slashes
|
|
11
|
+
*/
|
|
12
|
+
export declare function normalizePath(filePath: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Extracts path string from FileRef.
|
|
15
|
+
*/
|
|
16
|
+
export declare function toPath(file: FileRef): string;
|
|
17
|
+
/**
|
|
18
|
+
* Normalizes FileRef to a path string.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveFileRef(file: FileRef): string;
|
|
21
|
+
//# sourceMappingURL=normalize-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-path.d.ts","sourceRoot":"","sources":["../../src/utils/normalize-path.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEpD"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path manipulation utilities
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Joins path segments with proper normalization.
|
|
6
|
+
*/
|
|
7
|
+
export declare function joinPath(...segments: string[]): string;
|
|
8
|
+
/**
|
|
9
|
+
* Returns the directory portion of a path.
|
|
10
|
+
*/
|
|
11
|
+
export declare function dirname(path: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the filename portion of a path.
|
|
14
|
+
*/
|
|
15
|
+
export declare function basename(path: string, ext?: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Returns the file extension including the dot.
|
|
18
|
+
*/
|
|
19
|
+
export declare function extname(path: string): string;
|
|
20
|
+
//# sourceMappingURL=path-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../../src/utils/path-utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;GAEG;AACH,wBAAgB,QAAQ,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK5C"}
|
package/package.json
CHANGED
|
@@ -1,49 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/webrun-files",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
4
6
|
"description": "Basic files API",
|
|
5
|
-
"keywords": [],
|
|
6
7
|
"homepage": "https://github.com/statewalker/webrun-files",
|
|
7
8
|
"author": {
|
|
8
9
|
"name": "Mikhail Kotelnikov",
|
|
9
10
|
"email": "mikhail.kotelnikov@gmail.com"
|
|
10
11
|
},
|
|
11
12
|
"license": "MIT",
|
|
12
|
-
"type": "module",
|
|
13
|
-
"files": [
|
|
14
|
-
"dist/**/package.json",
|
|
15
|
-
"dist/**/*.js",
|
|
16
|
-
"src/**/*.js",
|
|
17
|
-
"index.js"
|
|
18
|
-
],
|
|
19
|
-
"module": "src/index.js",
|
|
20
|
-
"main": "src/index.js",
|
|
21
|
-
"jsdelivr": "dist/index.js",
|
|
22
|
-
"unpkg": "dist/index.js",
|
|
23
|
-
"exports": {
|
|
24
|
-
"umd": "./dist/index-umd.min.js",
|
|
25
|
-
"default": "./src/index.js"
|
|
26
|
-
},
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@statewalker/rollup": "^0.1.6",
|
|
29
|
-
"@statewalker/webrun-files-tests": "^0.1.0",
|
|
30
|
-
"eslint": "^8",
|
|
31
|
-
"expect.js": "^0.3",
|
|
32
|
-
"mocha": "^10",
|
|
33
|
-
"rollup": "^3"
|
|
34
|
-
},
|
|
35
13
|
"repository": {
|
|
36
14
|
"type": "git",
|
|
37
15
|
"url": "git@github.com:statewalker/webrun-files.git"
|
|
38
16
|
},
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
17
|
+
"main": "./dist/cjs/index.cjs",
|
|
18
|
+
"module": "./dist/esm/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/esm/index.js",
|
|
24
|
+
"require": "./dist/cjs/index.cjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"src"
|
|
30
|
+
],
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^24.10.1",
|
|
33
|
+
"rimraf": "^6.1.2",
|
|
34
|
+
"rolldown": "^1.0.0-beta.53",
|
|
35
|
+
"typescript": "^5.6.0",
|
|
36
|
+
"vitest": "^4.0.12"
|
|
44
37
|
},
|
|
45
38
|
"sideEffects": false,
|
|
46
39
|
"publishConfig": {
|
|
47
40
|
"access": "public"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "rimraf dist && rolldown -c && tsc --emitDeclarationOnly --declaration",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"lint": "biome lint src tests"
|
|
48
46
|
}
|
|
49
|
-
}
|
|
47
|
+
}
|
package/src/files-api.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FilesApi wrapper class providing convenience methods on top of IFilesApi
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
BinaryStream,
|
|
7
|
+
CopyOptions,
|
|
8
|
+
FileHandle,
|
|
9
|
+
FileInfo,
|
|
10
|
+
FileRef,
|
|
11
|
+
IFilesApi,
|
|
12
|
+
ListOptions,
|
|
13
|
+
ReadStreamOptions,
|
|
14
|
+
} from "./types.js";
|
|
15
|
+
import { toBinaryAsyncIterable } from "./utils/collect-stream.js";
|
|
16
|
+
import { joinPath, resolveFileRef } from "./utils/index.js";
|
|
17
|
+
|
|
18
|
+
const IGNORE_FILE = ".gitkeep";
|
|
19
|
+
|
|
20
|
+
export class FilesApi implements IFilesApi {
|
|
21
|
+
constructor(private fs: IFilesApi) {}
|
|
22
|
+
|
|
23
|
+
// ========================================
|
|
24
|
+
// IFilesApi delegation
|
|
25
|
+
// ========================================
|
|
26
|
+
|
|
27
|
+
list(file: FileRef, options?: ListOptions): AsyncGenerator<FileInfo> {
|
|
28
|
+
return this.fs.list(file, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
stats(file: FileRef): Promise<FileInfo | undefined> {
|
|
32
|
+
return this.fs.stats(file);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
remove(file: FileRef): Promise<boolean> {
|
|
36
|
+
return this.fs.remove(file);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
open(file: FileRef): Promise<FileHandle> {
|
|
40
|
+
return this.fs.open(file);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ========================================
|
|
44
|
+
// Convenience methods built on core API
|
|
45
|
+
// ========================================
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Checks if a file or directory exists.
|
|
49
|
+
*/
|
|
50
|
+
async exists(file: FileRef): Promise<boolean> {
|
|
51
|
+
const info = await this.stats(file);
|
|
52
|
+
return info !== undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reads file content as a stream.
|
|
57
|
+
*/
|
|
58
|
+
async *read(file: FileRef, options: ReadStreamOptions = {}): AsyncGenerator<Uint8Array> {
|
|
59
|
+
const handle = await this.open(file);
|
|
60
|
+
try {
|
|
61
|
+
yield* handle.createReadStream(options);
|
|
62
|
+
} finally {
|
|
63
|
+
await handle.close();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Writes content to a file.
|
|
69
|
+
*/
|
|
70
|
+
async write(file: FileRef, content: BinaryStream): Promise<void> {
|
|
71
|
+
const handle = await this.open(file);
|
|
72
|
+
try {
|
|
73
|
+
await handle.createWriteStream(toBinaryAsyncIterable(content));
|
|
74
|
+
} finally {
|
|
75
|
+
await handle.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Reads entire file into a single buffer.
|
|
81
|
+
*/
|
|
82
|
+
async readFile(file: FileRef): Promise<Uint8Array> {
|
|
83
|
+
const chunks: Uint8Array[] = [];
|
|
84
|
+
let totalLength = 0;
|
|
85
|
+
|
|
86
|
+
for await (const chunk of this.read(file)) {
|
|
87
|
+
chunks.push(chunk);
|
|
88
|
+
totalLength += chunk.length;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (chunks.length === 0) return new Uint8Array(0);
|
|
92
|
+
if (chunks.length === 1) return chunks[0];
|
|
93
|
+
|
|
94
|
+
const result = new Uint8Array(totalLength);
|
|
95
|
+
let offset = 0;
|
|
96
|
+
for (const chunk of chunks) {
|
|
97
|
+
result.set(chunk, offset);
|
|
98
|
+
offset += chunk.length;
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Copies file or directory.
|
|
105
|
+
* Uses native implementation if available, otherwise falls back to read/write.
|
|
106
|
+
*/
|
|
107
|
+
async copy(source: FileRef, target: FileRef, options: CopyOptions = {}): Promise<boolean> {
|
|
108
|
+
// Try native implementation first
|
|
109
|
+
if (this.fs.copy) {
|
|
110
|
+
return this.fs.copy(source, target, options);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Fallback: read from source, write to target
|
|
114
|
+
const sourceInfo = await this.stats(source);
|
|
115
|
+
if (!sourceInfo) return false;
|
|
116
|
+
|
|
117
|
+
const sourcePath = resolveFileRef(source);
|
|
118
|
+
const targetPath = resolveFileRef(target);
|
|
119
|
+
const recursive = options.recursive ?? true;
|
|
120
|
+
|
|
121
|
+
if (sourceInfo.kind === "directory") {
|
|
122
|
+
for await (const entry of this.list(source, { recursive })) {
|
|
123
|
+
if (entry.kind === "directory") continue;
|
|
124
|
+
const suffix = entry.path.substring(sourcePath.length);
|
|
125
|
+
const newTargetPath = targetPath + suffix;
|
|
126
|
+
await this.write(newTargetPath, this.read(entry.path));
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
await this.write(targetPath, this.read(sourcePath));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Moves file or directory.
|
|
137
|
+
* Uses native implementation if available, otherwise falls back to copy+delete.
|
|
138
|
+
*/
|
|
139
|
+
async move(source: FileRef, target: FileRef): Promise<boolean> {
|
|
140
|
+
// Try native implementation first
|
|
141
|
+
if (this.fs.move) {
|
|
142
|
+
return this.fs.move(source, target);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Fallback: copy then delete
|
|
146
|
+
const copied = await this.copy(source, target);
|
|
147
|
+
if (!copied) return false;
|
|
148
|
+
await this.remove(source);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Creates a directory.
|
|
154
|
+
* Uses native implementation if available, otherwise creates a placeholder file.
|
|
155
|
+
*/
|
|
156
|
+
async mkdir(file: FileRef): Promise<void> {
|
|
157
|
+
// Try native implementation first
|
|
158
|
+
if (this.fs.mkdir) {
|
|
159
|
+
return this.fs.mkdir(file);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Fallback: create a placeholder file to establish the directory
|
|
163
|
+
const dirPath = resolveFileRef(file);
|
|
164
|
+
const placeholderPath = joinPath(dirPath, IGNORE_FILE);
|
|
165
|
+
await this.write(placeholderPath, [new Uint8Array(0)]);
|
|
166
|
+
}
|
|
167
|
+
}
|