vvvfs 0.1.4 → 0.1.6
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/README.md +214 -34
- package/index.html +1 -31
- package/index.js +34 -0
- package/index.ts +282 -13
- package/package.json +33 -33
- package/types/index.d.ts +504 -0
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ const VVVFS = require("vvvfs"); // CommonJS
|
|
|
31
31
|
|
|
32
32
|
```javascript
|
|
33
33
|
const vvvfs = new VVVFS("vvvfs", {
|
|
34
|
-
|
|
34
|
+
throwError: true, // 设置为true后,操作文件失败时,不会返回false,而是会抛出错误
|
|
35
35
|
}); // 创建一个名为vvvfs的虚拟文件系统
|
|
36
36
|
```
|
|
37
37
|
|
|
@@ -40,34 +40,40 @@ const vvvfs = new VVVFS("vvvfs", {
|
|
|
40
40
|
```javascript
|
|
41
41
|
// 所有操作都是异步的
|
|
42
42
|
(async function () {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
43
|
+
await vvvfs.reset(); // 重置文件系统
|
|
44
|
+
await vvvfs.init("UserName"); // 初始化文件系统
|
|
45
|
+
vvvfs.watch("/home/user/Desktop/test.txt", (type) => {
|
|
46
|
+
console.log(type); // 打印文件操作的类型
|
|
47
|
+
return true; // 返回true或false,表示是否取消该操作
|
|
48
|
+
});
|
|
49
|
+
await vvvfs.lock("/home/user/Desktop/test.txt"); // 锁定文件,防止其他代码访问该文件
|
|
50
|
+
await vvvfs.unlock("/home/user/Desktop/test.txt"); // 解锁文件
|
|
51
|
+
await vvvfs.createDir("/home/user/Desktop"); // 创建目录,返回true和false
|
|
52
|
+
await vvvfs.writeText("/home/user/Desktop/test.txt", "Hello World!"); // 写入文本文件,写入文件还包括write(path: string, content: Blob)和writeJson(path: string, content: Record<string, any>)方法,返回true和false
|
|
53
|
+
await vvvfs.appendText("/home/user/Desktop/test.txt", "Hello World!"); // 追加文本文件,返回true和false
|
|
54
|
+
console.log(await vvvfs.readText("/home/user/Desktop/test.txt")); // 读取文本文件,读取文件还包括read(path: string): Blob | null和readJson(path: string): Record<string, any> | null方法
|
|
55
|
+
console.log(await vvvfs.readChunkText("/home/user/Desktop/test.txt", 0, 5)); // 读取文件块,返回string | null
|
|
56
|
+
await vvvfs.delete("/home/user/Desktop/test.txt"); // 删除文件,返回true和false
|
|
57
|
+
if (await vvvfs.exists("/home/user/Desktop")) {
|
|
58
|
+
// 判断文件或目录是否存在
|
|
59
|
+
}
|
|
60
|
+
console.log(await vvvfs.list("/home/user/Desktop")); // 列出目录下的文件,返回string[] | null
|
|
61
|
+
if (await vvvfs.isDir("/home/user/Desktop")) {
|
|
62
|
+
// 判断是否为目录
|
|
63
|
+
}
|
|
64
|
+
if (await vvvfs.isFile("/home/user/Desktop/test.txt")) {
|
|
65
|
+
// 判断是否为文件
|
|
66
|
+
}
|
|
67
|
+
await vvvfs.rename("/home/user/Desktop/test.txt", "test2.txt"); // 重命名文件,返回true和false
|
|
68
|
+
await vvvfs.move(
|
|
69
|
+
"/home/user/Desktop/test2.txt",
|
|
70
|
+
"/home/user/Desktop/test.txt",
|
|
71
|
+
); // 移动文件,返回true和false
|
|
72
|
+
await vvvfs.copy(
|
|
73
|
+
"/home/user/Desktop/test.txt",
|
|
74
|
+
"/home/user/Desktop/test2.txt",
|
|
75
|
+
); // 复制文件,返回true和false
|
|
76
|
+
await vvvfs.search("/home/user/Desktop", "test.txt"); // 搜索文件,返回string[] | null
|
|
71
77
|
})();
|
|
72
78
|
```
|
|
73
79
|
|
|
@@ -75,17 +81,25 @@ const vvvfs = new VVVFS("vvvfs", {
|
|
|
75
81
|
|
|
76
82
|
```javascript
|
|
77
83
|
(async function () {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
const file = new VVVFS.File("/home/user/Desktop/test.txt");
|
|
85
|
+
// file.options.throwError = true;
|
|
86
|
+
await file.writeText("Hello World!");
|
|
87
|
+
console.log(await file.readText());
|
|
88
|
+
await file.delete();
|
|
83
89
|
})();
|
|
84
90
|
// 用法与 vvvfs 相当
|
|
85
91
|
```
|
|
86
92
|
|
|
87
93
|
## 更新日志
|
|
88
94
|
|
|
95
|
+
### 0.1.6
|
|
96
|
+
|
|
97
|
+
- 新增 `VVVFS.path` ,用法与 NodeJS 的 `path` 模块类似。
|
|
98
|
+
|
|
99
|
+
### 0.1.5
|
|
100
|
+
|
|
101
|
+
- 重新 `lock` 和 `unlock` 方法
|
|
102
|
+
|
|
89
103
|
### 0.1.4
|
|
90
104
|
|
|
91
105
|
- 使用 AI 优化代码
|
|
@@ -145,3 +159,169 @@ const vvvfs = new VVVFS("vvvfs", {
|
|
|
145
159
|
### 0.0.1
|
|
146
160
|
|
|
147
161
|
- 发布正式版
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
# English
|
|
166
|
+
|
|
167
|
+
# VVVFS
|
|
168
|
+
|
|
169
|
+
A lightweight and simple Linux virtual file system in the browser, based on IndexedDB, built with Dexie.js
|
|
170
|
+
|
|
171
|
+
## Quick Start
|
|
172
|
+
|
|
173
|
+
1. Install the dependency
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
npm install vvvfs
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
2. Import
|
|
180
|
+
|
|
181
|
+
```javascript
|
|
182
|
+
import VVVFS from "vvvfs"; // ES6
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
const VVVFS = require("vvvfs"); // CommonJS
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```html
|
|
190
|
+
<script src="dist/vvvfs.min.js"></script>
|
|
191
|
+
<script src="https://cdn.jsdelivr.net/npm/vvvfs@latest/dist/vvvfs.min.js"></script>
|
|
192
|
+
<script src="https://unpkg.com/vvvfs@latest/dist/vvvfs.min.js"></script>
|
|
193
|
+
<!-- Browser -->
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
3. Initialize
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
const vvvfs = new VVVFS("vvvfs", {
|
|
200
|
+
throwError: true, // When set to true, failed file operations throw an error instead of returning false
|
|
201
|
+
}); // Create a virtual file system named vvvfs
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
4. Usage
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
// All operations are asynchronous
|
|
208
|
+
(async function () {
|
|
209
|
+
await vvvfs.reset(); // Reset the file system
|
|
210
|
+
await vvvfs.init("UserName"); // Initialize the file system
|
|
211
|
+
vvvfs.watch("/home/user/Desktop/test.txt", (type) => {
|
|
212
|
+
console.log(type); // Log the type of the file operation
|
|
213
|
+
return true; // Return true or false to indicate whether to cancel the operation
|
|
214
|
+
});
|
|
215
|
+
await vvvfs.lock("/home/user/Desktop/test.txt"); // Lock the file to prevent other code from accessing it
|
|
216
|
+
await vvvfs.unlock("/home/user/Desktop/test.txt"); // Unlock the file
|
|
217
|
+
await vvvfs.createDir("/home/user/Desktop"); // Create a directory, returns true and false
|
|
218
|
+
await vvvfs.writeText("/home/user/Desktop/test.txt", "Hello World!"); // Write a text file. Writing also includes write(path: string, content: Blob) and writeJson(path: string, content: Record<string, any>), returns true and false
|
|
219
|
+
await vvvfs.appendText("/home/user/Desktop/test.txt", "Hello World!"); // Append text to a file, returns true and false
|
|
220
|
+
console.log(await vvvfs.readText("/home/user/Desktop/test.txt")); // Read a text file. Reading also includes read(path: string): Blob | null and readJson(path: string): Record<string, any> | null
|
|
221
|
+
console.log(await vvvfs.readChunkText("/home/user/Desktop/test.txt", 0, 5)); // Read a file chunk, returns string | null
|
|
222
|
+
await vvvfs.delete("/home/user/Desktop/test.txt"); // Delete a file, returns true and false
|
|
223
|
+
if (await vvvfs.exists("/home/user/Desktop")) {
|
|
224
|
+
// Check whether a file or directory exists
|
|
225
|
+
}
|
|
226
|
+
console.log(await vvvfs.list("/home/user/Desktop")); // List the files in a directory, returns string[] | null
|
|
227
|
+
if (await vvvfs.isDir("/home/user/Desktop")) {
|
|
228
|
+
// Check whether it is a directory
|
|
229
|
+
}
|
|
230
|
+
if (await vvvfs.isFile("/home/user/Desktop/test.txt")) {
|
|
231
|
+
// Check whether it is a file
|
|
232
|
+
}
|
|
233
|
+
await vvvfs.rename("/home/user/Desktop/test.txt", "test2.txt"); // Rename a file, returns true and false
|
|
234
|
+
await vvvfs.move(
|
|
235
|
+
"/home/user/Desktop/test2.txt",
|
|
236
|
+
"/home/user/Desktop/test.txt",
|
|
237
|
+
); // Move a file, returns true and false
|
|
238
|
+
await vvvfs.copy(
|
|
239
|
+
"/home/user/Desktop/test.txt",
|
|
240
|
+
"/home/user/Desktop/test2.txt",
|
|
241
|
+
); // Copy a file, returns true and false
|
|
242
|
+
await vvvfs.search("/home/user/Desktop", "test.txt"); // Search for files, returns string[] | null
|
|
243
|
+
})();
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Using the `VVVFS.File` class:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
(async function () {
|
|
250
|
+
const file = new VVVFS.File("/home/user/Desktop/test.txt");
|
|
251
|
+
// file.options.throwError = true;
|
|
252
|
+
await file.writeText("Hello World!");
|
|
253
|
+
console.log(await file.readText());
|
|
254
|
+
await file.delete();
|
|
255
|
+
})();
|
|
256
|
+
// The usage is equivalent to vvvfs
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Changelog
|
|
260
|
+
|
|
261
|
+
### 0.1.6
|
|
262
|
+
|
|
263
|
+
- Added `VVVFS.path`, which works in a similar way to the `path` module in NodeJS.
|
|
264
|
+
|
|
265
|
+
### 0.1.5
|
|
266
|
+
|
|
267
|
+
- Redesigned the `lock` and `unlock` methods
|
|
268
|
+
|
|
269
|
+
### 0.1.4
|
|
270
|
+
|
|
271
|
+
- Optimized the code with AI
|
|
272
|
+
|
|
273
|
+
### 0.1.3
|
|
274
|
+
|
|
275
|
+
- Added the `lock` and `unlock` methods, which can lock files to prevent other code from accessing them
|
|
276
|
+
|
|
277
|
+
### 0.1.2
|
|
278
|
+
|
|
279
|
+
- Added the `readChunk` and `readChunkText` methods for reading file chunks
|
|
280
|
+
|
|
281
|
+
### 0.1.1
|
|
282
|
+
|
|
283
|
+
- Added the `append` and `appendText` methods for appending content
|
|
284
|
+
|
|
285
|
+
### 0.1.0
|
|
286
|
+
|
|
287
|
+
- Fixed `init` rewriting existing files and directories when initialized repeatedly
|
|
288
|
+
|
|
289
|
+
### 0.0.9
|
|
290
|
+
|
|
291
|
+
- Updated the `watch` method: while watching, returning `true` cancels the operation
|
|
292
|
+
|
|
293
|
+
### 0.0.8
|
|
294
|
+
|
|
295
|
+
- Added the `watch` method (this is an experimental feature and may change in the future)
|
|
296
|
+
|
|
297
|
+
### 0.0.7
|
|
298
|
+
|
|
299
|
+
- Optimized a bit with AI
|
|
300
|
+
|
|
301
|
+
### 0.0.6
|
|
302
|
+
|
|
303
|
+
- Added the `dbname` and `options` properties to the `VVVFS.File` class
|
|
304
|
+
- Added the `defaultDBName` property to `VVVFS`
|
|
305
|
+
|
|
306
|
+
### 0.0.5
|
|
307
|
+
|
|
308
|
+
- Added the `VVVFS.File` class
|
|
309
|
+
|
|
310
|
+
### 0.0.4
|
|
311
|
+
|
|
312
|
+
- Fixed IndexedDB spamming a bunch of warnings
|
|
313
|
+
- Fixed a bug in `search`
|
|
314
|
+
|
|
315
|
+
### 0.0.3
|
|
316
|
+
|
|
317
|
+
- Added maximum length limits: 4096 characters for full paths and 255 characters for file names
|
|
318
|
+
- Added the `init` method for initializing Linux files and directories
|
|
319
|
+
- Fixed a bug where `list` returned an empty string (the root directory itself) in the array when listing the root directory
|
|
320
|
+
|
|
321
|
+
### 0.0.2
|
|
322
|
+
|
|
323
|
+
- Added the `throwError` option, which defaults to `false`
|
|
324
|
+
|
|
325
|
+
### 0.0.1
|
|
326
|
+
|
|
327
|
+
- Initial release
|
package/index.html
CHANGED
|
@@ -9,37 +9,7 @@
|
|
|
9
9
|
</head>
|
|
10
10
|
|
|
11
11
|
<body>
|
|
12
|
-
<script>
|
|
13
|
-
(async () => {
|
|
14
|
-
try {
|
|
15
|
-
globalThis.vvvfs = new VVVFS();
|
|
16
|
-
vvvfs.options.throwError = true;
|
|
17
|
-
await vvvfs.reset();
|
|
18
|
-
await vvvfs.init("IFTC");
|
|
19
|
-
console.log(vvvfs);
|
|
20
|
-
vvvfs.watch("/test.txt", (type) => {
|
|
21
|
-
console.log(type);
|
|
22
|
-
// return true;
|
|
23
|
-
});
|
|
24
|
-
console.log("创建文件", await vvvfs.createFile("/test.txt"));
|
|
25
|
-
console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
|
|
26
|
-
console.log("追加文件", await vvvfs.appendText("/test.txt", " - Appended"));
|
|
27
|
-
// await vvvfs.lock("/test.txt");
|
|
28
|
-
console.log("文件是否存在", await vvvfs.exists("/test.txt"));
|
|
29
|
-
console.log("文件是否存在", await vvvfs.exists("/test2.txt"));
|
|
30
|
-
console.log("读取文件", await vvvfs.readText("/test.txt"));
|
|
31
|
-
console.log("读取文件块", await vvvfs.readTextChunk("/test.txt", 0, 5));
|
|
32
|
-
console.log("复制文件", await vvvfs.copy("/test.txt", "/test2.txt"));
|
|
33
|
-
console.log("搜索文件", await vvvfs.search("/", "t"));
|
|
34
|
-
console.log("移动文件", await vvvfs.move("/test2.txt", "/test3.txt"));
|
|
35
|
-
console.log("删除文件", await vvvfs.delete("/test.txt"));
|
|
36
|
-
const file = new VVVFS.File("test.txt");
|
|
37
|
-
console.log("写入文件", await file.writeText("Hello World"));
|
|
38
|
-
} catch (error) {
|
|
39
|
-
console.error(error);
|
|
40
|
-
}
|
|
41
|
-
})();
|
|
42
|
-
</script>
|
|
12
|
+
<script src="index.js"></script>
|
|
43
13
|
</body>
|
|
44
14
|
|
|
45
15
|
</html>
|
package/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference path="./types/index.d.ts" />
|
|
2
|
+
/**
|
|
3
|
+
* @class VVVFS
|
|
4
|
+
* @description VVVFS 虚拟文件系统
|
|
5
|
+
*/
|
|
6
|
+
(async () => {
|
|
7
|
+
try {
|
|
8
|
+
globalThis.vvvfs = new VVVFS();
|
|
9
|
+
vvvfs.options.throwError = true;
|
|
10
|
+
await vvvfs.reset();
|
|
11
|
+
await vvvfs.init("IFTC");
|
|
12
|
+
console.log(vvvfs);
|
|
13
|
+
vvvfs.watch("/test.txt", (type) => {
|
|
14
|
+
console.log(type);
|
|
15
|
+
// return true;
|
|
16
|
+
});
|
|
17
|
+
console.log("创建文件", await vvvfs.createFile("/test.txt"));
|
|
18
|
+
console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
|
|
19
|
+
console.log("追加文件", await vvvfs.appendText("/test.txt", " - Appended"));
|
|
20
|
+
// await vvvfs.lock("/test.txt");
|
|
21
|
+
console.log("文件是否存在", await vvvfs.exists("/test.txt"));
|
|
22
|
+
console.log("文件是否存在", await vvvfs.exists("/test2.txt"));
|
|
23
|
+
console.log("读取文件", await vvvfs.readText("/test.txt"));
|
|
24
|
+
console.log("读取文件块", await vvvfs.readTextChunk("/test.txt", 0, 5));
|
|
25
|
+
console.log("复制文件", await vvvfs.copy("/test.txt", "/test2.txt"));
|
|
26
|
+
console.log("搜索文件", await vvvfs.search("/", "t"));
|
|
27
|
+
console.log("移动文件", await vvvfs.move("/test2.txt", "/test3.txt"));
|
|
28
|
+
console.log("删除文件", await vvvfs.delete("/test.txt"));
|
|
29
|
+
const file = new VVVFS.File("test.txt");
|
|
30
|
+
console.log("写入文件", await file.writeText("Hello World"));
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error(error);
|
|
33
|
+
}
|
|
34
|
+
})();
|
package/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import mime from "mime";
|
|
|
15
15
|
* @param path 文件路径
|
|
16
16
|
* @param type 文件类型(file或dir)
|
|
17
17
|
* @param file 文件对象
|
|
18
|
+
* @param locked 是否锁定
|
|
18
19
|
*/
|
|
19
20
|
export interface FileRecord {
|
|
20
21
|
id?: number;
|
|
@@ -22,6 +23,7 @@ export interface FileRecord {
|
|
|
22
23
|
path: string;
|
|
23
24
|
type: string;
|
|
24
25
|
file: File | null;
|
|
26
|
+
locked?: boolean;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/**
|
|
@@ -264,6 +266,221 @@ class VVVFSFile {
|
|
|
264
266
|
async unlock() {
|
|
265
267
|
return await this._vvvfs.unlock(this._path);
|
|
266
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* 判断文件是否已锁定
|
|
271
|
+
*/
|
|
272
|
+
async isLocked() {
|
|
273
|
+
return await this._vvvfs.isLocked(this._path);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* path类(类似于NodeJS中的path模块)
|
|
279
|
+
*/
|
|
280
|
+
class Path {
|
|
281
|
+
/**
|
|
282
|
+
* 路径分隔符
|
|
283
|
+
*/
|
|
284
|
+
static readonly sep = "/";
|
|
285
|
+
/**
|
|
286
|
+
* 环境变量PATH的分隔符
|
|
287
|
+
*/
|
|
288
|
+
static readonly delimiter = ":";
|
|
289
|
+
/**
|
|
290
|
+
* 内部存储的路径
|
|
291
|
+
*/
|
|
292
|
+
private _path: string;
|
|
293
|
+
/**
|
|
294
|
+
* 构造函数
|
|
295
|
+
* @param paths 路径片段
|
|
296
|
+
*/
|
|
297
|
+
constructor(...paths: string[]) {
|
|
298
|
+
this._path = Path.resolve(...paths);
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* 获取路径
|
|
302
|
+
*/
|
|
303
|
+
get path() {
|
|
304
|
+
return this._path;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* 设置路径
|
|
308
|
+
* @param path 路径
|
|
309
|
+
*/
|
|
310
|
+
set path(path: string) {
|
|
311
|
+
this._path = Path.resolve(path);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* 获取文件名
|
|
315
|
+
*/
|
|
316
|
+
get name() {
|
|
317
|
+
return Path.basename(this._path);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 获取文件所在目录
|
|
321
|
+
*/
|
|
322
|
+
get parent() {
|
|
323
|
+
return Path.dirname(this._path);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* 获取文件扩展名
|
|
327
|
+
*/
|
|
328
|
+
get ext() {
|
|
329
|
+
return Path.extname(this._path);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 获取根目录("/"或"")
|
|
333
|
+
*/
|
|
334
|
+
get root() {
|
|
335
|
+
return Path.parse(this._path).root;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* 判断路径是否为绝对路径
|
|
339
|
+
*/
|
|
340
|
+
isAbsolute() {
|
|
341
|
+
return Path.isAbsolute(this._path);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* 合并路径
|
|
345
|
+
* @param paths 路径片段
|
|
346
|
+
*/
|
|
347
|
+
join(...paths: string[]) {
|
|
348
|
+
return Path.join(this._path, ...paths);
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* 转换为字符串
|
|
352
|
+
*/
|
|
353
|
+
toString() {
|
|
354
|
+
return this._path;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 合并路径
|
|
358
|
+
* @param paths 路径片段
|
|
359
|
+
*/
|
|
360
|
+
static join(...paths: string[]) {
|
|
361
|
+
return joinPath(...paths);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* 解析路径为绝对路径
|
|
365
|
+
* @param paths 路径片段
|
|
366
|
+
*/
|
|
367
|
+
static resolve(...paths: string[]) {
|
|
368
|
+
return paths.length === 0 ? "/" : joinPath(...paths);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* 规范化路径(清除"."、解析"..")
|
|
372
|
+
* @param path 路径
|
|
373
|
+
*/
|
|
374
|
+
static normalize(path: string) {
|
|
375
|
+
if (path.length === 0) return ".";
|
|
376
|
+
const isAbsolute = path.startsWith("/");
|
|
377
|
+
const trailingSlash = path.length > 1 && path.endsWith("/");
|
|
378
|
+
const stack: string[] = [];
|
|
379
|
+
for (const part of path.split("/")) {
|
|
380
|
+
if (part === "" || part === ".") {
|
|
381
|
+
continue;
|
|
382
|
+
} else if (part === "..") {
|
|
383
|
+
if (stack.length > 0 && stack[stack.length - 1] !== "..") {
|
|
384
|
+
stack.pop();
|
|
385
|
+
} else if (!isAbsolute) {
|
|
386
|
+
stack.push("..");
|
|
387
|
+
}
|
|
388
|
+
} else {
|
|
389
|
+
stack.push(part);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
let result = stack.join("/");
|
|
393
|
+
if (isAbsolute) {
|
|
394
|
+
result = "/" + result;
|
|
395
|
+
} else if (result.length === 0) {
|
|
396
|
+
result = ".";
|
|
397
|
+
}
|
|
398
|
+
if (trailingSlash && result !== "/" && result !== "." && !result.endsWith("/")) {
|
|
399
|
+
result += "/";
|
|
400
|
+
}
|
|
401
|
+
return result;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* 判断路径是否为绝对路径
|
|
405
|
+
* @param path 路径
|
|
406
|
+
*/
|
|
407
|
+
static isAbsolute(path: string) {
|
|
408
|
+
return path.length > 0 && path.startsWith("/");
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* 获取文件名(可去除后缀)
|
|
412
|
+
* @param path 路径
|
|
413
|
+
* @param suffix 后缀(如扩展名)
|
|
414
|
+
*/
|
|
415
|
+
static basename(path: string, suffix?: string) {
|
|
416
|
+
if (path.length === 0) return "";
|
|
417
|
+
const normalized = Path.normalize(path);
|
|
418
|
+
if (normalized === "/") return "/";
|
|
419
|
+
let base = normalized.endsWith("/") ? normalized.slice(0, -1) : normalized;
|
|
420
|
+
base = base.slice(base.lastIndexOf("/") + 1);
|
|
421
|
+
if (suffix && base !== suffix && base.endsWith(suffix)) {
|
|
422
|
+
base = base.slice(0, base.length - suffix.length);
|
|
423
|
+
}
|
|
424
|
+
return base;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* 获取文件所在目录
|
|
428
|
+
* @param path 路径
|
|
429
|
+
*/
|
|
430
|
+
static dirname(path: string) {
|
|
431
|
+
const normalized = Path.normalize(path);
|
|
432
|
+
if (normalized === "/" || normalized === ".") return normalized;
|
|
433
|
+
const parts = normalized.split("/");
|
|
434
|
+
parts.pop();
|
|
435
|
+
const result = parts.join("/");
|
|
436
|
+
if (result.length === 0) {
|
|
437
|
+
return Path.isAbsolute(normalized) ? "/" : ".";
|
|
438
|
+
}
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* 获取文件扩展名
|
|
443
|
+
* @param path 路径
|
|
444
|
+
*/
|
|
445
|
+
static extname(path: string) {
|
|
446
|
+
const base = Path.basename(path);
|
|
447
|
+
const lastDot = base.lastIndexOf(".");
|
|
448
|
+
if (lastDot <= 0) return "";
|
|
449
|
+
return base.slice(lastDot);
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* 解析路径
|
|
453
|
+
* @param path 路径
|
|
454
|
+
*/
|
|
455
|
+
static parse(path: string) {
|
|
456
|
+
const base = Path.basename(path);
|
|
457
|
+
const ext = Path.extname(base);
|
|
458
|
+
return {
|
|
459
|
+
root: Path.isAbsolute(path) ? "/" : "",
|
|
460
|
+
dir: Path.dirname(path),
|
|
461
|
+
base,
|
|
462
|
+
ext,
|
|
463
|
+
name: ext.length > 0 ? base.slice(0, base.length - ext.length) : base,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* 计算从from到to的相对路径
|
|
468
|
+
* @param from 起始路径
|
|
469
|
+
* @param to 目标路径
|
|
470
|
+
*/
|
|
471
|
+
static relative(from: string, to: string) {
|
|
472
|
+
const fromParts = Path.resolve(from).split("/").filter((part) => part.length > 0);
|
|
473
|
+
const toParts = Path.resolve(to).split("/").filter((part) => part.length > 0);
|
|
474
|
+
let i = 0;
|
|
475
|
+
while (i < fromParts.length && i < toParts.length && fromParts[i] === toParts[i]) {
|
|
476
|
+
i++;
|
|
477
|
+
}
|
|
478
|
+
const up = new Array<string>(Math.max(0, fromParts.length - i)).fill("..").join("/");
|
|
479
|
+
const down = toParts.slice(i).join("/");
|
|
480
|
+
if (up.length > 0 && down.length > 0) return up + "/" + down;
|
|
481
|
+
if (up.length > 0) return up;
|
|
482
|
+
return down;
|
|
483
|
+
}
|
|
267
484
|
}
|
|
268
485
|
|
|
269
486
|
const version = packageJson.version;
|
|
@@ -281,13 +498,13 @@ class VVVFS {
|
|
|
281
498
|
*/
|
|
282
499
|
static File = VVVFSFile;
|
|
283
500
|
/**
|
|
284
|
-
*
|
|
501
|
+
* path类(类似于NodeJS中的path模块)
|
|
285
502
|
*/
|
|
286
|
-
|
|
503
|
+
static path = Path;
|
|
287
504
|
/**
|
|
288
|
-
*
|
|
505
|
+
* 虚拟文件系统监听器
|
|
289
506
|
*/
|
|
290
|
-
#
|
|
507
|
+
#watchers: Record<string, Array<(type: string) => Promise<boolean>>> = {};
|
|
291
508
|
|
|
292
509
|
/**
|
|
293
510
|
* 检查文件访问权限(监听器和锁定状态)
|
|
@@ -309,7 +526,7 @@ class VVVFS {
|
|
|
309
526
|
}
|
|
310
527
|
}
|
|
311
528
|
}
|
|
312
|
-
if (this.#
|
|
529
|
+
if (await this.#isLocked(path)) {
|
|
313
530
|
if (this.options.throwError) {
|
|
314
531
|
throw new VVVFSError(
|
|
315
532
|
operation.charAt(0).toUpperCase() + operation.slice(1),
|
|
@@ -321,6 +538,15 @@ class VVVFS {
|
|
|
321
538
|
return true;
|
|
322
539
|
}
|
|
323
540
|
|
|
541
|
+
/**
|
|
542
|
+
* 内部判断是否锁定(跳过错误包装)
|
|
543
|
+
*/
|
|
544
|
+
async #isLocked(path: string): Promise<boolean> {
|
|
545
|
+
const { name, parent } = parsePath(path);
|
|
546
|
+
const fileRecord = await this.#db.files.where({ name, path: parent }).first();
|
|
547
|
+
return !!fileRecord?.locked;
|
|
548
|
+
}
|
|
549
|
+
|
|
324
550
|
/**
|
|
325
551
|
* 错误处理包装器
|
|
326
552
|
* @param operation 操作名称
|
|
@@ -364,6 +590,15 @@ class VVVFS {
|
|
|
364
590
|
this.#db.version(1).stores({
|
|
365
591
|
files: "++id, name, path, type, file, [name+path+type]",
|
|
366
592
|
});
|
|
593
|
+
this.#db.version(2).stores({
|
|
594
|
+
files: "++id, name, path, type, file, locked, [name+path+type]",
|
|
595
|
+
}).upgrade(async (tx) => {
|
|
596
|
+
await tx.table("files").toCollection().modify((file: FileRecord) => {
|
|
597
|
+
if (file.locked === undefined) {
|
|
598
|
+
file.locked = false;
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
});
|
|
367
602
|
} catch (error) {
|
|
368
603
|
console.error("创建数据库失败", error);
|
|
369
604
|
throw new VVVFSError("CreateDatabase", "创建数据库失败");
|
|
@@ -384,12 +619,14 @@ class VVVFS {
|
|
|
384
619
|
path: "/",
|
|
385
620
|
type: "dir" as const,
|
|
386
621
|
file: new File([], name),
|
|
622
|
+
locked: false,
|
|
387
623
|
}));
|
|
388
624
|
linuxInitFiles.push({
|
|
389
625
|
name: user || "root",
|
|
390
626
|
path: "/home",
|
|
391
627
|
type: "dir" as const,
|
|
392
628
|
file: new File([], "home"),
|
|
629
|
+
locked: false,
|
|
393
630
|
});
|
|
394
631
|
try {
|
|
395
632
|
for (const file of linuxInitFiles) {
|
|
@@ -411,6 +648,9 @@ class VVVFS {
|
|
|
411
648
|
this.#db.version(1).stores({
|
|
412
649
|
files: "++id, name, path, type, file, [name+path+type]",
|
|
413
650
|
});
|
|
651
|
+
this.#db.version(2).stores({
|
|
652
|
+
files: "++id, name, path, type, file, locked, [name+path+type]",
|
|
653
|
+
});
|
|
414
654
|
} catch (error) {
|
|
415
655
|
console.error("重置数据库失败", error);
|
|
416
656
|
throw new VVVFSError("ResetDatabase", "重置数据库失败" + error);
|
|
@@ -439,6 +679,7 @@ class VVVFS {
|
|
|
439
679
|
file: new File([], name, {
|
|
440
680
|
type: mime.getType(targetPath) || "application/octet-stream",
|
|
441
681
|
}),
|
|
682
|
+
locked: false,
|
|
442
683
|
});
|
|
443
684
|
return true;
|
|
444
685
|
}, false);
|
|
@@ -463,6 +704,7 @@ class VVVFS {
|
|
|
463
704
|
path: "/",
|
|
464
705
|
type: "dir",
|
|
465
706
|
file: new File([], ""),
|
|
707
|
+
locked: false,
|
|
466
708
|
});
|
|
467
709
|
if (name === "") return true;
|
|
468
710
|
} else {
|
|
@@ -474,6 +716,7 @@ class VVVFS {
|
|
|
474
716
|
path: parent,
|
|
475
717
|
type: "dir",
|
|
476
718
|
file: new File([], name),
|
|
719
|
+
locked: false,
|
|
477
720
|
});
|
|
478
721
|
return true;
|
|
479
722
|
}, false);
|
|
@@ -862,6 +1105,7 @@ class VVVFS {
|
|
|
862
1105
|
path: newParent,
|
|
863
1106
|
type: fileRecord.type,
|
|
864
1107
|
file: fileRecord.file,
|
|
1108
|
+
locked: false,
|
|
865
1109
|
});
|
|
866
1110
|
return true;
|
|
867
1111
|
}
|
|
@@ -922,16 +1166,22 @@ class VVVFS {
|
|
|
922
1166
|
*/
|
|
923
1167
|
async lock(path: string) {
|
|
924
1168
|
return this.#withErrorHandling("lock", async () => {
|
|
925
|
-
|
|
926
|
-
if (!(await this.exists(
|
|
1169
|
+
const targetPath = joinPath(path);
|
|
1170
|
+
if (!(await this.exists(targetPath))) {
|
|
927
1171
|
console.warn("文件不存在");
|
|
928
1172
|
return false;
|
|
929
1173
|
}
|
|
930
|
-
if (this.#
|
|
1174
|
+
if (await this.#isLocked(targetPath)) {
|
|
931
1175
|
console.warn("文件已被锁定");
|
|
932
1176
|
return false;
|
|
933
1177
|
}
|
|
934
|
-
|
|
1178
|
+
const { name, parent } = parsePath(targetPath);
|
|
1179
|
+
const fileRecord = await this.#db.files.where({ name, path: parent }).first();
|
|
1180
|
+
if (!fileRecord?.id) {
|
|
1181
|
+
console.warn("文件记录未找到");
|
|
1182
|
+
return false;
|
|
1183
|
+
}
|
|
1184
|
+
await this.#db.files.update(fileRecord.id, { locked: true });
|
|
935
1185
|
return true;
|
|
936
1186
|
}, false);
|
|
937
1187
|
}
|
|
@@ -941,19 +1191,38 @@ class VVVFS {
|
|
|
941
1191
|
*/
|
|
942
1192
|
async unlock(path: string) {
|
|
943
1193
|
return this.#withErrorHandling("unlock", async () => {
|
|
944
|
-
|
|
945
|
-
if (!(await this.exists(
|
|
1194
|
+
const targetPath = joinPath(path);
|
|
1195
|
+
if (!(await this.exists(targetPath))) {
|
|
946
1196
|
console.warn("文件不存在");
|
|
947
1197
|
return false;
|
|
948
1198
|
}
|
|
949
|
-
if (!this.#
|
|
1199
|
+
if (!(await this.#isLocked(targetPath))) {
|
|
950
1200
|
console.warn("文件未被锁定");
|
|
951
1201
|
return false;
|
|
952
1202
|
}
|
|
953
|
-
|
|
1203
|
+
const { name, parent } = parsePath(targetPath);
|
|
1204
|
+
const fileRecord = await this.#db.files.where({ name, path: parent }).first();
|
|
1205
|
+
if (!fileRecord?.id) {
|
|
1206
|
+
console.warn("文件记录未找到");
|
|
1207
|
+
return false;
|
|
1208
|
+
}
|
|
1209
|
+
await this.#db.files.update(fileRecord.id, { locked: false });
|
|
954
1210
|
return true;
|
|
955
1211
|
}, false);
|
|
956
1212
|
}
|
|
1213
|
+
/**
|
|
1214
|
+
* 判断文件是否已锁定
|
|
1215
|
+
* @param path 文件路径
|
|
1216
|
+
*/
|
|
1217
|
+
async isLocked(path: string) {
|
|
1218
|
+
return this.#withErrorHandling("isLocked", async () => {
|
|
1219
|
+
const targetPath = joinPath(path);
|
|
1220
|
+
if (!(await this.exists(targetPath))) {
|
|
1221
|
+
return false;
|
|
1222
|
+
}
|
|
1223
|
+
return await this.#isLocked(targetPath);
|
|
1224
|
+
}, false);
|
|
1225
|
+
}
|
|
957
1226
|
}
|
|
958
1227
|
/**
|
|
959
1228
|
* 解析路径
|
package/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "vvvfs",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "一个使用IndexDB实现的虚拟文件系统",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"Virtual",
|
|
7
|
-
"File",
|
|
8
|
-
"System",
|
|
9
|
-
"IFTC",
|
|
10
|
-
"VV",
|
|
11
|
-
"File",
|
|
12
|
-
"System",
|
|
13
|
-
"vfs",
|
|
14
|
-
"fs"
|
|
15
|
-
],
|
|
16
|
-
"license": "MIT",
|
|
17
|
-
"author": "IFTC",
|
|
18
|
-
"type": "module",
|
|
19
|
-
"main": "index.js",
|
|
20
|
-
"scripts": {
|
|
21
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
22
|
-
"build": "node esbuild.config.js"
|
|
23
|
-
},
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"dexie": "^4.3.0",
|
|
26
|
-
"esbuild": "^0.27.3",
|
|
27
|
-
"mime": "^4.1.0"
|
|
28
|
-
},
|
|
29
|
-
"repository": {
|
|
30
|
-
"type": "git",
|
|
31
|
-
"url": "https://github.com/IFTC-XLKJ/vvvfs.git"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "vvvfs",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "一个使用IndexDB实现的虚拟文件系统",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"Virtual",
|
|
7
|
+
"File",
|
|
8
|
+
"System",
|
|
9
|
+
"IFTC",
|
|
10
|
+
"VV",
|
|
11
|
+
"File",
|
|
12
|
+
"System",
|
|
13
|
+
"vfs",
|
|
14
|
+
"fs"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "IFTC",
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "index.js",
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
22
|
+
"build": "node esbuild.config.js"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"dexie": "^4.3.0",
|
|
26
|
+
"esbuild": "^0.27.3",
|
|
27
|
+
"mime": "^4.1.0"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/IFTC-XLKJ/vvvfs.git"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import { Dexie, Table } from "dexie";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 虚拟文件系统
|
|
5
|
+
*/
|
|
6
|
+
declare global {
|
|
7
|
+
// 扩展 Window 接口
|
|
8
|
+
interface window {
|
|
9
|
+
VVVFS: typeof VVVFS; // 如果 VVFS 是个类,这里可能需要调整,或者直接定义 vvfs 实例
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// 扩展 GlobalThis (针对你在 index.js 里的 globalThis.vvfs 写法)
|
|
13
|
+
// 注意:GlobalThis 和 Window 往往指向同一个对象,但在 TS 类型中有时需要分别处理
|
|
14
|
+
// var VVVFS: typeof VVVFS;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 文件记录
|
|
20
|
+
* @param id 文件id
|
|
21
|
+
* @param name 文件名
|
|
22
|
+
* @param path 文件路径
|
|
23
|
+
* @param type 文件类型(file或dir)
|
|
24
|
+
* @param file 文件对象
|
|
25
|
+
* @param locked 是否锁定
|
|
26
|
+
*/
|
|
27
|
+
declare interface FileRecord {
|
|
28
|
+
id?: number;
|
|
29
|
+
name: string;
|
|
30
|
+
path: string;
|
|
31
|
+
type: string;
|
|
32
|
+
file: File | null;
|
|
33
|
+
locked?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 虚拟文件系统数据库
|
|
38
|
+
* @param files 文件存储数据表
|
|
39
|
+
*/
|
|
40
|
+
declare interface VVVFSDatabase extends Dexie {
|
|
41
|
+
files: Table<FileRecord, number>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 虚拟文件系统配置项
|
|
46
|
+
* @param throwError 是否抛出错误
|
|
47
|
+
*/
|
|
48
|
+
declare interface VVVFSOptions {
|
|
49
|
+
throwError: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 虚拟文件系统错误类
|
|
54
|
+
*/
|
|
55
|
+
declare class VVVFSError extends Error {
|
|
56
|
+
/**
|
|
57
|
+
* @param type 错误类型
|
|
58
|
+
* @param message 错误信息
|
|
59
|
+
*/
|
|
60
|
+
constructor(type: string, message: string);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 虚拟文件系统文件类
|
|
65
|
+
* @param path 文件路径
|
|
66
|
+
*/
|
|
67
|
+
declare class VVVFSFile {
|
|
68
|
+
/**
|
|
69
|
+
* 获取文件路径
|
|
70
|
+
*/
|
|
71
|
+
get path(): string;
|
|
72
|
+
/**
|
|
73
|
+
* 设置文件路径
|
|
74
|
+
* @param path 文件路径
|
|
75
|
+
*/
|
|
76
|
+
set path(path: string);
|
|
77
|
+
/**
|
|
78
|
+
* 设置数据库名称
|
|
79
|
+
* @param dbname 数据库名称
|
|
80
|
+
*/
|
|
81
|
+
set dbname(dbname: string);
|
|
82
|
+
/**
|
|
83
|
+
* 获取数据库名称
|
|
84
|
+
*/
|
|
85
|
+
get dbname(): string;
|
|
86
|
+
set options(options: VVVFSOptions);
|
|
87
|
+
get options(): VVVFSOptions;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 构造函数
|
|
91
|
+
* @param paths 文件路径
|
|
92
|
+
*/
|
|
93
|
+
constructor(...paths: string[]);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 读取文件
|
|
97
|
+
*/
|
|
98
|
+
read(): Promise<File | null>;
|
|
99
|
+
/**
|
|
100
|
+
* 读取文件文本内容
|
|
101
|
+
*/
|
|
102
|
+
readText(): Promise<string | null>;
|
|
103
|
+
/**
|
|
104
|
+
* 读取文件JSON内容
|
|
105
|
+
*/
|
|
106
|
+
readJSON(): Promise<Record<string, unknown> | null>;
|
|
107
|
+
/**
|
|
108
|
+
* 读取文件内容
|
|
109
|
+
* @param start 起始位置
|
|
110
|
+
* @param end 结束位置
|
|
111
|
+
*/
|
|
112
|
+
readChunk(start: number, end: number): Promise<Blob | null>;
|
|
113
|
+
/**
|
|
114
|
+
* 读取文件内容
|
|
115
|
+
* @param start 起始位置
|
|
116
|
+
* @param end 结束位置
|
|
117
|
+
*/
|
|
118
|
+
readTextChunk(start: number, end: number): Promise<string | null>;
|
|
119
|
+
/**
|
|
120
|
+
* 写入文件
|
|
121
|
+
* @param file 文件对象
|
|
122
|
+
*/
|
|
123
|
+
write(file: Blob): Promise<boolean>;
|
|
124
|
+
/**
|
|
125
|
+
* 写入文件文本内容
|
|
126
|
+
* @param text 文件文本内容
|
|
127
|
+
*/
|
|
128
|
+
writeText(text: string): Promise<boolean>;
|
|
129
|
+
/**
|
|
130
|
+
* 写入文件JSON内容
|
|
131
|
+
* @param json 文件JSON内容
|
|
132
|
+
*/
|
|
133
|
+
writeJSON(json: Record<string, unknown>, format?: boolean): Promise<boolean>;
|
|
134
|
+
/**
|
|
135
|
+
* 追加文件内容
|
|
136
|
+
* @param file 文件对象
|
|
137
|
+
*/
|
|
138
|
+
append(file: Blob): Promise<boolean>;
|
|
139
|
+
/**
|
|
140
|
+
* 追加文件文本内容
|
|
141
|
+
* @param text 文件文本内容
|
|
142
|
+
*/
|
|
143
|
+
appendText(text: string): Promise<boolean>;
|
|
144
|
+
/**
|
|
145
|
+
* 创建文件
|
|
146
|
+
*/
|
|
147
|
+
createFile(): Promise<boolean>;
|
|
148
|
+
/**
|
|
149
|
+
* 创建文件夹
|
|
150
|
+
*/
|
|
151
|
+
createDir(): Promise<boolean>;
|
|
152
|
+
/**
|
|
153
|
+
* 删除文件
|
|
154
|
+
*/
|
|
155
|
+
delete(): Promise<boolean>;
|
|
156
|
+
/**
|
|
157
|
+
* 判断文件是否存在
|
|
158
|
+
*/
|
|
159
|
+
exists(): Promise<boolean>;
|
|
160
|
+
/**
|
|
161
|
+
* 获取文件大小
|
|
162
|
+
*/
|
|
163
|
+
isFile(): Promise<boolean>;
|
|
164
|
+
/**
|
|
165
|
+
* 判断文件是否是文件夹
|
|
166
|
+
*/
|
|
167
|
+
isDir(): Promise<boolean>;
|
|
168
|
+
/**
|
|
169
|
+
* 列出文件
|
|
170
|
+
*/
|
|
171
|
+
list(): Promise<string[]>;
|
|
172
|
+
/**
|
|
173
|
+
* 重命名文件
|
|
174
|
+
*/
|
|
175
|
+
rename(newName: string): Promise<boolean>;
|
|
176
|
+
/**
|
|
177
|
+
* 移动文件
|
|
178
|
+
*/
|
|
179
|
+
move(newPath: string): Promise<boolean>;
|
|
180
|
+
/**
|
|
181
|
+
* 复制文件
|
|
182
|
+
*/
|
|
183
|
+
copy(newPath: string): Promise<boolean>;
|
|
184
|
+
/**
|
|
185
|
+
* 搜索文件
|
|
186
|
+
*/
|
|
187
|
+
search(query: string): Promise<string[] | null>;
|
|
188
|
+
/**
|
|
189
|
+
* 监听文件
|
|
190
|
+
*/
|
|
191
|
+
watch(handler: (type: string) => Promise<boolean>): void;
|
|
192
|
+
/**
|
|
193
|
+
* 锁定文件
|
|
194
|
+
*/
|
|
195
|
+
lock(): Promise<boolean>;
|
|
196
|
+
/**
|
|
197
|
+
* 解锁文件
|
|
198
|
+
*/
|
|
199
|
+
unlock(): Promise<boolean>;
|
|
200
|
+
/**
|
|
201
|
+
* 判断文件是否已锁定
|
|
202
|
+
*/
|
|
203
|
+
isLocked(): Promise<boolean>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* path类(类似于NodeJS中的path模块)
|
|
208
|
+
*/
|
|
209
|
+
declare class Path {
|
|
210
|
+
/**
|
|
211
|
+
* 路径分隔符
|
|
212
|
+
*/
|
|
213
|
+
static readonly sep: string;
|
|
214
|
+
/**
|
|
215
|
+
* 环境变量PATH的分隔符
|
|
216
|
+
*/
|
|
217
|
+
static readonly delimiter: string;
|
|
218
|
+
/**
|
|
219
|
+
* 构造函数
|
|
220
|
+
* @param paths 路径片段
|
|
221
|
+
*/
|
|
222
|
+
constructor(...paths: string[]);
|
|
223
|
+
/**
|
|
224
|
+
* 获取路径
|
|
225
|
+
*/
|
|
226
|
+
get path(): string;
|
|
227
|
+
/**
|
|
228
|
+
* 设置路径
|
|
229
|
+
* @param path 路径
|
|
230
|
+
*/
|
|
231
|
+
set path(path: string);
|
|
232
|
+
/**
|
|
233
|
+
* 获取文件名
|
|
234
|
+
*/
|
|
235
|
+
get name(): string;
|
|
236
|
+
/**
|
|
237
|
+
* 获取文件所在目录
|
|
238
|
+
*/
|
|
239
|
+
get parent(): string;
|
|
240
|
+
/**
|
|
241
|
+
* 获取文件扩展名
|
|
242
|
+
*/
|
|
243
|
+
get ext(): string;
|
|
244
|
+
/**
|
|
245
|
+
* 获取根目录("/"或"")
|
|
246
|
+
*/
|
|
247
|
+
get root(): string;
|
|
248
|
+
/**
|
|
249
|
+
* 判断路径是否为绝对路径
|
|
250
|
+
*/
|
|
251
|
+
isAbsolute(): boolean;
|
|
252
|
+
/**
|
|
253
|
+
* 合并路径
|
|
254
|
+
* @param paths 路径片段
|
|
255
|
+
*/
|
|
256
|
+
join(...paths: string[]): string;
|
|
257
|
+
/**
|
|
258
|
+
* 转换为字符串
|
|
259
|
+
*/
|
|
260
|
+
toString(): string;
|
|
261
|
+
/**
|
|
262
|
+
* 合并路径
|
|
263
|
+
* @param paths 路径片段
|
|
264
|
+
*/
|
|
265
|
+
static join(...paths: string[]): string;
|
|
266
|
+
/**
|
|
267
|
+
* 解析路径为绝对路径
|
|
268
|
+
* @param paths 路径片段
|
|
269
|
+
*/
|
|
270
|
+
static resolve(...paths: string[]): string;
|
|
271
|
+
/**
|
|
272
|
+
* 规范化路径(清除"."、解析"..")
|
|
273
|
+
* @param path 路径
|
|
274
|
+
*/
|
|
275
|
+
static normalize(path: string): string;
|
|
276
|
+
/**
|
|
277
|
+
* 判断路径是否为绝对路径
|
|
278
|
+
* @param path 路径
|
|
279
|
+
*/
|
|
280
|
+
static isAbsolute(path: string): boolean;
|
|
281
|
+
/**
|
|
282
|
+
* 获取文件名(可去除后缀)
|
|
283
|
+
* @param path 路径
|
|
284
|
+
* @param suffix 后缀(如扩展名)
|
|
285
|
+
*/
|
|
286
|
+
static basename(path: string, suffix?: string): string;
|
|
287
|
+
/**
|
|
288
|
+
* 获取文件所在目录
|
|
289
|
+
* @param path 路径
|
|
290
|
+
*/
|
|
291
|
+
static dirname(path: string): string;
|
|
292
|
+
/**
|
|
293
|
+
* 获取文件扩展名
|
|
294
|
+
* @param path 路径
|
|
295
|
+
*/
|
|
296
|
+
static extname(path: string): string;
|
|
297
|
+
/**
|
|
298
|
+
* 解析路径
|
|
299
|
+
* @param path 路径
|
|
300
|
+
*/
|
|
301
|
+
static parse(path: string): { root: string; dir: string; base: string; ext: string; name: string };
|
|
302
|
+
/**
|
|
303
|
+
* 计算从from到to的相对路径
|
|
304
|
+
* @param from 起始路径
|
|
305
|
+
* @param to 目标路径
|
|
306
|
+
*/
|
|
307
|
+
static relative(from: string, to: string): string;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 虚拟文件系统
|
|
312
|
+
* @author IFTC
|
|
313
|
+
* @description 虚拟文件系统
|
|
314
|
+
*/
|
|
315
|
+
declare class VVVFS {
|
|
316
|
+
static defaultDBName: string;
|
|
317
|
+
/**
|
|
318
|
+
* 虚拟文件系统版本
|
|
319
|
+
*/
|
|
320
|
+
static readonly version: string;
|
|
321
|
+
/**
|
|
322
|
+
* 虚拟文件系统作者
|
|
323
|
+
*/
|
|
324
|
+
static readonly author: string;
|
|
325
|
+
/**
|
|
326
|
+
* 虚拟文件系统文件类
|
|
327
|
+
*/
|
|
328
|
+
static File: typeof VVVFSFile;
|
|
329
|
+
/**
|
|
330
|
+
* path类(类似于NodeJS中的path模块)
|
|
331
|
+
*/
|
|
332
|
+
static path: typeof Path;
|
|
333
|
+
|
|
334
|
+
options: VVVFSOptions;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* 创建虚拟文件系统
|
|
338
|
+
* @param name 虚拟文件系统名称
|
|
339
|
+
* @param options 配置项
|
|
340
|
+
*/
|
|
341
|
+
constructor(name?: string, options?: VVVFSOptions);
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* 初始化虚拟文件系统
|
|
345
|
+
* @description 将Linux的系统初始文件初始化到数据库中
|
|
346
|
+
*/
|
|
347
|
+
init(user?: string): Promise<void>;
|
|
348
|
+
/**
|
|
349
|
+
* 重置虚拟文件系统
|
|
350
|
+
*/
|
|
351
|
+
reset(): Promise<void>;
|
|
352
|
+
/**
|
|
353
|
+
* 创建文件
|
|
354
|
+
* @param path 文件路径
|
|
355
|
+
*/
|
|
356
|
+
createFile(path: string): Promise<boolean>;
|
|
357
|
+
/**
|
|
358
|
+
* 创建目录
|
|
359
|
+
* @param path 目录路径
|
|
360
|
+
*/
|
|
361
|
+
createDir(path: string): Promise<boolean>;
|
|
362
|
+
/**
|
|
363
|
+
* 判断文件是否存在
|
|
364
|
+
* @param path 文件路径
|
|
365
|
+
*/
|
|
366
|
+
exists(path: string): Promise<boolean>;
|
|
367
|
+
/**
|
|
368
|
+
* 读取文件内容
|
|
369
|
+
* @param path 文件路径
|
|
370
|
+
*/
|
|
371
|
+
read(path: string): Promise<File | null>;
|
|
372
|
+
/**
|
|
373
|
+
* 读取文件文本内容
|
|
374
|
+
* @param path 文件路径
|
|
375
|
+
*/
|
|
376
|
+
readText(path: string): Promise<string | null>;
|
|
377
|
+
/**
|
|
378
|
+
* 读取JSON内容
|
|
379
|
+
* @param path 文件路径
|
|
380
|
+
*/
|
|
381
|
+
readJson(path: string): Promise<Record<string, unknown> | null>;
|
|
382
|
+
/**
|
|
383
|
+
* 写入文件
|
|
384
|
+
* @param path 文件路径
|
|
385
|
+
* @param content 文件内容
|
|
386
|
+
*/
|
|
387
|
+
write(path: string, content: Blob): Promise<boolean>;
|
|
388
|
+
/**
|
|
389
|
+
* 写入文本内容
|
|
390
|
+
* @param path 文件路径
|
|
391
|
+
* @param content 文本内容
|
|
392
|
+
*/
|
|
393
|
+
writeText(path: string, content: string): Promise<boolean>;
|
|
394
|
+
/**
|
|
395
|
+
* 写入JSON内容
|
|
396
|
+
* @param path 文件路径
|
|
397
|
+
* @param content JSON内容
|
|
398
|
+
* @param format 是否格式化
|
|
399
|
+
*/
|
|
400
|
+
writeJson(path: string, content: Record<string, unknown>, format?: boolean): Promise<boolean>;
|
|
401
|
+
/**
|
|
402
|
+
* 追加内容
|
|
403
|
+
* @param path 文件路径
|
|
404
|
+
* @param content 追加内容
|
|
405
|
+
*/
|
|
406
|
+
append(path: string, content: Blob): Promise<boolean>;
|
|
407
|
+
/**
|
|
408
|
+
* 追加文本内容
|
|
409
|
+
* @param path 文件路径
|
|
410
|
+
* @param content 追加内容
|
|
411
|
+
*/
|
|
412
|
+
appendText(path: string, content: string): Promise<boolean>;
|
|
413
|
+
/**
|
|
414
|
+
* 读取文件内容
|
|
415
|
+
* @param path 文件路径
|
|
416
|
+
* @param start 开始位置
|
|
417
|
+
* @param end 结束位置
|
|
418
|
+
*/
|
|
419
|
+
readChunk(path: string, start: number, end: number): Promise<Blob | null>;
|
|
420
|
+
/**
|
|
421
|
+
* 读取文件内容
|
|
422
|
+
* @param path 文件路径
|
|
423
|
+
* @param start 读取开始位置
|
|
424
|
+
* @param end 读取结束位置
|
|
425
|
+
*/
|
|
426
|
+
readTextChunk(path: string, start: number, end: number): Promise<string | null>;
|
|
427
|
+
/**
|
|
428
|
+
* 判断是否是文件
|
|
429
|
+
* @param path 文件路径
|
|
430
|
+
*/
|
|
431
|
+
isFile(path: string): Promise<boolean>;
|
|
432
|
+
/**
|
|
433
|
+
* 判断是否是目录
|
|
434
|
+
* @param path 文件路径
|
|
435
|
+
*/
|
|
436
|
+
isDir(path: string): Promise<boolean>;
|
|
437
|
+
/**
|
|
438
|
+
* 列出目录下的文件
|
|
439
|
+
* @param path 目录路径
|
|
440
|
+
*/
|
|
441
|
+
list(path: string): Promise<string[]>;
|
|
442
|
+
/**
|
|
443
|
+
* 重命名文件
|
|
444
|
+
* @param path 文件路径
|
|
445
|
+
* @param newName 新文件名
|
|
446
|
+
*/
|
|
447
|
+
rename(path: string, newName: string): Promise<boolean>;
|
|
448
|
+
/**
|
|
449
|
+
* 删除文件
|
|
450
|
+
* @param path 文件路径
|
|
451
|
+
*/
|
|
452
|
+
delete(path: string): Promise<boolean>;
|
|
453
|
+
/**
|
|
454
|
+
* 移动文件
|
|
455
|
+
* @param path 文件路径
|
|
456
|
+
* @param newPath 新路径
|
|
457
|
+
*/
|
|
458
|
+
move(path: string, newPath: string): Promise<boolean>;
|
|
459
|
+
/**
|
|
460
|
+
* 复制文件
|
|
461
|
+
* @param path 文件路径
|
|
462
|
+
* @param newPath 新路径
|
|
463
|
+
*/
|
|
464
|
+
copy(path: string, newPath: string): Promise<boolean>;
|
|
465
|
+
/**
|
|
466
|
+
* 搜索文件
|
|
467
|
+
* @param basePath 基础路径
|
|
468
|
+
* @param query 查询字符串
|
|
469
|
+
*/
|
|
470
|
+
search(basePath: string, query: string): Promise<string[] | null>;
|
|
471
|
+
/**
|
|
472
|
+
* 监听文件
|
|
473
|
+
* @param path 文件路径
|
|
474
|
+
* @param handler 监听器
|
|
475
|
+
*/
|
|
476
|
+
watch(path: string, handler: (type: string) => Promise<boolean>): void;
|
|
477
|
+
/**
|
|
478
|
+
* 锁定文件
|
|
479
|
+
* @param path 文件路径
|
|
480
|
+
*/
|
|
481
|
+
lock(path: string): Promise<boolean>;
|
|
482
|
+
/**
|
|
483
|
+
* 解锁文件
|
|
484
|
+
* @param path 文件路径
|
|
485
|
+
*/
|
|
486
|
+
unlock(path: string): Promise<boolean>;
|
|
487
|
+
/**
|
|
488
|
+
* 判断文件是否已锁定
|
|
489
|
+
* @param path 文件路径
|
|
490
|
+
*/
|
|
491
|
+
isLocked(path: string): Promise<boolean>;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* 解析路径
|
|
496
|
+
* @param path 路径
|
|
497
|
+
*/
|
|
498
|
+
declare function parsePath(path: string): { name: string; parent: string };
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* 合并路径
|
|
502
|
+
* @param paths 路径
|
|
503
|
+
*/
|
|
504
|
+
declare function joinPath(...paths: string[]): string;
|