@statewalker/webrun-files 0.5.0 → 0.9.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/LICENSE +1 -1
- package/README.md +131 -114
- package/dist/cjs/index.cjs +85 -529
- package/dist/esm/index.js +78 -519
- package/dist/file-utils.d.ts +47 -0
- package/dist/file-utils.d.ts.map +1 -0
- package/dist/index.d.ts +14 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/{utils/path-utils.d.ts → path-utils.d.ts} +10 -1
- package/dist/path-utils.d.ts.map +1 -0
- package/dist/types.d.ts +130 -51
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -10
- package/src/file-utils.ts +106 -0
- package/src/index.ts +38 -27
- package/src/{utils/path-utils.ts → path-utils.ts} +16 -2
- package/src/types.ts +136 -53
- package/dist/files-api.d.ts +0 -44
- package/dist/files-api.d.ts.map +0 -1
- package/dist/impl/index.d.ts +0 -6
- package/dist/impl/index.d.ts.map +0 -1
- package/dist/impl/mem-files-api.d.ts +0 -13
- package/dist/impl/mem-files-api.d.ts.map +0 -1
- package/dist/impl/node-files-api.d.ts +0 -24
- package/dist/impl/node-files-api.d.ts.map +0 -1
- package/dist/utils/collect-stream.d.ts +0 -16
- package/dist/utils/collect-stream.d.ts.map +0 -1
- package/dist/utils/file-info-utils.d.ts +0 -10
- package/dist/utils/file-info-utils.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -8
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/normalize-path.d.ts +0 -21
- package/dist/utils/normalize-path.d.ts.map +0 -1
- package/dist/utils/path-utils.d.ts.map +0 -1
- package/src/files-api.ts +0 -168
- package/src/impl/index.ts +0 -6
- package/src/impl/mem-files-api.ts +0 -323
- package/src/impl/node-files-api.ts +0 -271
- package/src/utils/collect-stream.ts +0 -35
- package/src/utils/file-info-utils.ts +0 -15
- package/src/utils/index.ts +0 -8
- package/src/utils/normalize-path.ts +0 -34
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EACL,MAAM,EACN,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,WAAW,EACX,SAAS,GACV,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,QAAQ,EACR,OAAO,EACP,OAAO,EACP,QAAQ,EACR,aAAa,GACd,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC"}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Path manipulation utilities
|
|
2
|
+
* Path manipulation utilities for virtual filesystem paths.
|
|
3
|
+
* All paths use forward slashes and start with "/".
|
|
3
4
|
*/
|
|
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;
|
|
4
13
|
/**
|
|
5
14
|
* Joins path segments with proper normalization.
|
|
6
15
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAMtD;AAED;;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/dist/types.d.ts
CHANGED
|
@@ -1,67 +1,146 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Core types for FilesApi abstraction
|
|
3
3
|
*/
|
|
4
4
|
export type FileKind = "file" | "directory";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Options for reading file content with range support.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // Read 100 bytes starting at position 50
|
|
10
|
+
* api.read("/file.bin", { start: 50, length: 100 })
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Read from position 1000 to end of file
|
|
14
|
+
* api.read("/file.bin", { start: 1000 })
|
|
15
|
+
*/
|
|
16
|
+
export interface ReadOptions {
|
|
17
|
+
/** Starting byte position (0-indexed). Defaults to 0. */
|
|
18
|
+
start?: number;
|
|
19
|
+
/** Number of bytes to read. If omitted, reads to end of file. */
|
|
20
|
+
length?: number;
|
|
21
|
+
/** AbortSignal for cancellation support. */
|
|
22
|
+
signal?: AbortSignal;
|
|
12
23
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Options for listing directory contents.
|
|
26
|
+
*/
|
|
16
27
|
export interface ListOptions {
|
|
28
|
+
/** If true, lists all descendants recursively. Defaults to false. */
|
|
17
29
|
recursive?: boolean;
|
|
18
30
|
}
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Metadata of a regular file.
|
|
33
|
+
*
|
|
34
|
+
* `size` and `lastModified` are REQUIRED. Every storage that can hold a file
|
|
35
|
+
* can say how large it is and when it last changed; an implementation that
|
|
36
|
+
* cannot is claiming less than it must, and the omission must surface as a
|
|
37
|
+
* failure rather than as an `undefined` a consumer has to defend against.
|
|
38
|
+
*/
|
|
39
|
+
export interface FileEntryStats {
|
|
40
|
+
kind: "file";
|
|
41
|
+
/** Length in bytes. A zero-byte file is `0`, never `undefined`. */
|
|
42
|
+
size: number;
|
|
43
|
+
/** Modification time as milliseconds since the epoch. */
|
|
44
|
+
lastModified: number;
|
|
21
45
|
}
|
|
22
|
-
|
|
23
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Metadata of a directory.
|
|
48
|
+
*
|
|
49
|
+
* Deliberately carries nothing else. A directory has no size, and a
|
|
50
|
+
* modification time is available on some storages (memory, a local
|
|
51
|
+
* filesystem) and unavailable on others (an object store, where a directory
|
|
52
|
+
* is a common prefix that exists only as an artefact of key naming). Neither
|
|
53
|
+
* value may be relied upon, so neither is part of the contract: an
|
|
54
|
+
* implementation that happens to know one drops it here instead of leaking a
|
|
55
|
+
* value consumers would have to treat as optional anyway.
|
|
56
|
+
*/
|
|
57
|
+
export interface DirectoryEntryStats {
|
|
58
|
+
kind: "directory";
|
|
24
59
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
60
|
+
/**
|
|
61
|
+
* File or directory metadata returned by `stats()`.
|
|
62
|
+
*
|
|
63
|
+
* A DISCRIMINATED UNION on `kind`, not a single shape with optional fields.
|
|
64
|
+
* The optionality this replaces never expressed what a given implementation
|
|
65
|
+
* could report — it expressed a property of the entry kind, collapsed into
|
|
66
|
+
* one type because `kind` was an ordinary field. Narrow on `kind` and the
|
|
67
|
+
* fields that exist for that kind are then known to be present:
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const stats = await api.stats("/some/path");
|
|
71
|
+
* if (stats?.kind === "file") {
|
|
72
|
+
* console.log(stats.size, stats.lastModified); // both `number`
|
|
73
|
+
* }
|
|
74
|
+
*/
|
|
75
|
+
export type FileStats = FileEntryStats | DirectoryEntryStats;
|
|
76
|
+
/** Name and path carried by every entry a `list()` yields. */
|
|
77
|
+
export interface FileEntryLocation {
|
|
78
|
+
name: string;
|
|
79
|
+
path: string;
|
|
29
80
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
signal?: AbortSignal;
|
|
81
|
+
/** A file yielded by `list()`. */
|
|
82
|
+
export interface FileEntryInfo extends FileEntryStats, FileEntryLocation {
|
|
33
83
|
}
|
|
34
|
-
|
|
35
|
-
export interface
|
|
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
|
-
/**
|
|
42
|
-
* Read bytes from the file at a specific position.
|
|
43
|
-
*
|
|
44
|
-
* @param buffer - The buffer to read bytes into
|
|
45
|
-
* @param offset - The offset in the buffer to start writing at
|
|
46
|
-
* @param length - The number of bytes to read
|
|
47
|
-
* @param position - The position in the file to start reading from
|
|
48
|
-
* @returns The number of bytes actually read (may be less than length at EOF)
|
|
49
|
-
*/
|
|
50
|
-
read(buffer: Uint8Array, offset: number, length: number, position: number): Promise<number>;
|
|
51
|
-
chmod?(mode: number): Promise<void>;
|
|
52
|
-
chown?(uid: number, gid: number): Promise<void>;
|
|
84
|
+
/** A directory yielded by `list()`. */
|
|
85
|
+
export interface DirectoryEntryInfo extends DirectoryEntryStats, FileEntryLocation {
|
|
53
86
|
}
|
|
54
87
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
88
|
+
* File or directory information returned by `list()`.
|
|
89
|
+
*
|
|
90
|
+
* The same union as {@link FileStats}, plus `name` and `path`, so a listing
|
|
91
|
+
* consumer narrows per entry exactly as a `stats()` consumer does.
|
|
57
92
|
*/
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
93
|
+
export type FileInfo = FileEntryInfo | DirectoryEntryInfo;
|
|
94
|
+
/**
|
|
95
|
+
* Cross-platform filesystem abstraction interface.
|
|
96
|
+
*
|
|
97
|
+
* All paths are virtual paths using forward slashes and starting with "/".
|
|
98
|
+
* Implementations handle mapping to underlying storage.
|
|
99
|
+
*/
|
|
100
|
+
export interface FilesApi {
|
|
101
|
+
/**
|
|
102
|
+
* Read file content as an async iterable of chunks.
|
|
103
|
+
* Returns empty iterable for non-existent files.
|
|
104
|
+
*/
|
|
105
|
+
read(path: string, options?: ReadOptions): AsyncIterable<Uint8Array>;
|
|
106
|
+
/**
|
|
107
|
+
* Write content to a file, creating parent directories as needed.
|
|
108
|
+
* Overwrites existing file content.
|
|
109
|
+
*/
|
|
110
|
+
write(path: string, content: Iterable<Uint8Array> | AsyncIterable<Uint8Array>): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Create a directory and all parent directories.
|
|
113
|
+
* No-op if directory already exists.
|
|
114
|
+
*/
|
|
115
|
+
mkdir(path: string): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* List directory contents.
|
|
118
|
+
* Returns empty iterable for non-existent or non-directory paths.
|
|
119
|
+
*/
|
|
120
|
+
list(path: string, options?: ListOptions): AsyncIterable<FileInfo>;
|
|
121
|
+
/**
|
|
122
|
+
* Get file or directory metadata.
|
|
123
|
+
* Returns undefined for non-existent paths.
|
|
124
|
+
*/
|
|
125
|
+
stats(path: string): Promise<FileStats | undefined>;
|
|
126
|
+
/**
|
|
127
|
+
* Check if a path exists.
|
|
128
|
+
*/
|
|
129
|
+
exists(path: string): Promise<boolean>;
|
|
130
|
+
/**
|
|
131
|
+
* Remove a file or directory (recursively).
|
|
132
|
+
* Returns true if something was removed, false if path didn't exist.
|
|
133
|
+
*/
|
|
134
|
+
remove(path: string): Promise<boolean>;
|
|
135
|
+
/**
|
|
136
|
+
* Move/rename a file or directory.
|
|
137
|
+
* Returns true on success, false if source doesn't exist.
|
|
138
|
+
*/
|
|
139
|
+
move(source: string, target: string): Promise<boolean>;
|
|
140
|
+
/**
|
|
141
|
+
* Copy a file or directory (recursively).
|
|
142
|
+
* Returns true on success, false if source doesn't exist.
|
|
143
|
+
*/
|
|
144
|
+
copy(source: string, target: string): Promise<boolean>;
|
|
66
145
|
}
|
|
67
146
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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;;;;;;;;;;GAUG;AACH,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAE7D,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kCAAkC;AAClC,MAAM,WAAW,aAAc,SAAQ,cAAc,EAAE,iBAAiB;CAAG;AAE3E,uCAAuC;AACvC,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB,EAAE,iBAAiB;CAAG;AAErF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAE1D;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IAErE;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9F;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAEnE;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IAEpD;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvC;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvC;;;OAGG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAEvD;;;OAGG;IACH,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxD"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/webrun-files",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "
|
|
6
|
+
"description": "FilesApi interface and utilities for cross-platform filesystem abstraction",
|
|
7
7
|
"homepage": "https://github.com/statewalker/webrun-files",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Mikhail Kotelnikov",
|
|
@@ -29,12 +29,10 @@
|
|
|
29
29
|
"src"
|
|
30
30
|
],
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@types/node": "^
|
|
33
|
-
"rimraf": "^6.1.
|
|
34
|
-
"rolldown": "^1.
|
|
35
|
-
"typescript": "^
|
|
36
|
-
"vitest": "^4.0.12",
|
|
37
|
-
"@statewalker/webrun-files-tests": "1.0.0"
|
|
32
|
+
"@types/node": "^26.2.0",
|
|
33
|
+
"rimraf": "^6.1.3",
|
|
34
|
+
"rolldown": "^1.2.4",
|
|
35
|
+
"typescript": "^7.0.2"
|
|
38
36
|
},
|
|
39
37
|
"sideEffects": false,
|
|
40
38
|
"publishConfig": {
|
|
@@ -42,7 +40,6 @@
|
|
|
42
40
|
},
|
|
43
41
|
"scripts": {
|
|
44
42
|
"build": "rimraf dist && rolldown -c && tsc --emitDeclarationOnly --declaration",
|
|
45
|
-
"
|
|
46
|
-
"lint": "biome lint src tests"
|
|
43
|
+
"lint": "biome lint src"
|
|
47
44
|
}
|
|
48
45
|
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { FilesApi } from "./types.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Collect async iterable chunks into a single Uint8Array.
|
|
5
|
+
*/
|
|
6
|
+
async function collectChunks(chunks: AsyncIterable<Uint8Array>): Promise<Uint8Array> {
|
|
7
|
+
const parts: Uint8Array[] = [];
|
|
8
|
+
let totalLength = 0;
|
|
9
|
+
for await (const chunk of chunks) {
|
|
10
|
+
parts.push(chunk);
|
|
11
|
+
totalLength += chunk.length;
|
|
12
|
+
}
|
|
13
|
+
if (parts.length === 0) return new Uint8Array(0);
|
|
14
|
+
if (parts.length === 1) return parts[0];
|
|
15
|
+
const result = new Uint8Array(totalLength);
|
|
16
|
+
let offset = 0;
|
|
17
|
+
for (const chunk of parts) {
|
|
18
|
+
result.set(chunk, offset);
|
|
19
|
+
offset += chunk.length;
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Read entire file content into a Uint8Array.
|
|
26
|
+
* Returns empty array if file doesn't exist.
|
|
27
|
+
*/
|
|
28
|
+
export async function readFile(files: FilesApi, path: string): Promise<Uint8Array> {
|
|
29
|
+
return collectChunks(files.read(path));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read entire file content as a UTF-8 string.
|
|
34
|
+
* Returns empty string if file doesn't exist.
|
|
35
|
+
*/
|
|
36
|
+
export async function readText(files: FilesApi, path: string): Promise<string> {
|
|
37
|
+
const data = await readFile(files, path);
|
|
38
|
+
return new TextDecoder().decode(data);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read file content, returning undefined if file doesn't exist.
|
|
43
|
+
*/
|
|
44
|
+
export async function tryReadFile(files: FilesApi, path: string): Promise<Uint8Array | undefined> {
|
|
45
|
+
if (!(await files.exists(path))) return undefined;
|
|
46
|
+
return readFile(files, path);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Read file content as string, returning undefined if file doesn't exist.
|
|
51
|
+
*/
|
|
52
|
+
export async function tryReadText(files: FilesApi, path: string): Promise<string | undefined> {
|
|
53
|
+
const data = await tryReadFile(files, path);
|
|
54
|
+
return data ? new TextDecoder().decode(data) : undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Read a specific byte range from a file.
|
|
59
|
+
*
|
|
60
|
+
* @param files - FilesApi instance
|
|
61
|
+
* @param path - File path
|
|
62
|
+
* @param position - Starting byte position
|
|
63
|
+
* @param length - Number of bytes to read
|
|
64
|
+
* @returns Uint8Array with requested bytes (may be shorter if EOF reached)
|
|
65
|
+
*/
|
|
66
|
+
export async function readRange(
|
|
67
|
+
files: FilesApi,
|
|
68
|
+
path: string,
|
|
69
|
+
position: number,
|
|
70
|
+
length: number,
|
|
71
|
+
): Promise<Uint8Array> {
|
|
72
|
+
return collectChunks(files.read(path, { start: position, length }));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Read bytes from a file into a buffer at a specific offset.
|
|
77
|
+
* Similar to Node.js fs.read() signature.
|
|
78
|
+
*
|
|
79
|
+
* @param files - FilesApi instance
|
|
80
|
+
* @param path - File path
|
|
81
|
+
* @param buffer - Target buffer to write into
|
|
82
|
+
* @param bufferOffset - Offset in buffer where to start writing
|
|
83
|
+
* @param length - Number of bytes to read
|
|
84
|
+
* @param position - Position in file where to start reading
|
|
85
|
+
* @returns Number of bytes actually read
|
|
86
|
+
*/
|
|
87
|
+
export async function readAt(
|
|
88
|
+
files: FilesApi,
|
|
89
|
+
path: string,
|
|
90
|
+
buffer: Uint8Array,
|
|
91
|
+
bufferOffset: number,
|
|
92
|
+
length: number,
|
|
93
|
+
position: number,
|
|
94
|
+
): Promise<number> {
|
|
95
|
+
const data = await readRange(files, path, position, length);
|
|
96
|
+
const bytesToCopy = Math.min(data.length, buffer.length - bufferOffset);
|
|
97
|
+
buffer.set(data.subarray(0, bytesToCopy), bufferOffset);
|
|
98
|
+
return bytesToCopy;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Write a string to a file as UTF-8.
|
|
103
|
+
*/
|
|
104
|
+
export async function writeText(files: FilesApi, path: string, content: string): Promise<void> {
|
|
105
|
+
await files.write(path, [new TextEncoder().encode(content)]);
|
|
106
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,36 +1,47 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* webrun-files - Cross-platform filesystem API
|
|
3
|
+
*
|
|
4
|
+
* This package provides:
|
|
5
|
+
* - FilesApi interface for filesystem abstraction
|
|
6
|
+
* - Utility functions for common file operations
|
|
7
|
+
* - Path manipulation utilities
|
|
8
|
+
*
|
|
9
|
+
* For implementations, use:
|
|
10
|
+
* - @statewalker/webrun-files-mem (in-memory)
|
|
11
|
+
* - @statewalker/webrun-files-node (Node.js)
|
|
12
|
+
* - @statewalker/webrun-files-browser (browser)
|
|
13
|
+
* - @statewalker/webrun-files-s3 (S3)
|
|
3
14
|
*/
|
|
4
15
|
|
|
5
|
-
//
|
|
6
|
-
export {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
16
|
+
// File utilities
|
|
17
|
+
export {
|
|
18
|
+
readAt,
|
|
19
|
+
readFile,
|
|
20
|
+
readRange,
|
|
21
|
+
readText,
|
|
22
|
+
tryReadFile,
|
|
23
|
+
tryReadText,
|
|
24
|
+
writeText,
|
|
25
|
+
} from "./file-utils.js";
|
|
26
|
+
// Path utilities
|
|
27
|
+
export {
|
|
28
|
+
basename,
|
|
29
|
+
dirname,
|
|
30
|
+
extname,
|
|
31
|
+
joinPath,
|
|
32
|
+
normalizePath,
|
|
33
|
+
} from "./path-utils.js";
|
|
34
|
+
// Core types
|
|
11
35
|
export type {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
36
|
+
DirectoryEntryInfo,
|
|
37
|
+
DirectoryEntryStats,
|
|
38
|
+
FileEntryInfo,
|
|
39
|
+
FileEntryLocation,
|
|
40
|
+
FileEntryStats,
|
|
16
41
|
FileInfo,
|
|
17
42
|
FileKind,
|
|
18
|
-
|
|
19
|
-
|
|
43
|
+
FileStats,
|
|
44
|
+
FilesApi,
|
|
20
45
|
ListOptions,
|
|
21
|
-
|
|
22
|
-
WriteStreamOptions,
|
|
46
|
+
ReadOptions,
|
|
23
47
|
} from "./types.js";
|
|
24
|
-
export {
|
|
25
|
-
collectGenerator,
|
|
26
|
-
toAsyncIterable,
|
|
27
|
-
toBinaryAsyncIterable,
|
|
28
|
-
} from "./utils/collect-stream.js";
|
|
29
|
-
// Utilities
|
|
30
|
-
export {
|
|
31
|
-
normalizePath,
|
|
32
|
-
resolveFileRef,
|
|
33
|
-
toPath,
|
|
34
|
-
} from "./utils/normalize-path.js";
|
|
35
|
-
export { isDirectory, isFile } from "./utils/file-info-utils.js";
|
|
36
|
-
export { basename, dirname, extname, joinPath } from "./utils/path-utils.js";
|
|
@@ -1,8 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Path manipulation utilities
|
|
2
|
+
* Path manipulation utilities for virtual filesystem paths.
|
|
3
|
+
* All paths use forward slashes and start with "/".
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Normalizes a file path to a consistent format.
|
|
8
|
+
* - Adds leading slash if missing
|
|
9
|
+
* - Removes trailing slash
|
|
10
|
+
* - Removes `.` segments
|
|
11
|
+
* - Collapses multiple slashes
|
|
12
|
+
*/
|
|
13
|
+
export function normalizePath(filePath: string): string {
|
|
14
|
+
const segments = filePath.split("/").filter((s) => !!s && s !== ".");
|
|
15
|
+
if (segments.length === 0) {
|
|
16
|
+
return "/";
|
|
17
|
+
}
|
|
18
|
+
return `/${segments.join("/")}`;
|
|
19
|
+
}
|
|
6
20
|
|
|
7
21
|
/**
|
|
8
22
|
* Joins path segments with proper normalization.
|