vvvfs 0.0.8 → 0.1.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/README.md +10 -1
- package/index.html +2 -0
- package/index.ts +120 -65
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -44,6 +44,7 @@ const vvvfs = new VVVFS("vvvfs", {
|
|
|
44
44
|
await vvvfs.init("UserName"); // 初始化文件系统
|
|
45
45
|
await vvvfs.watch("/home/user/Desktop/test.txt", (type) => {
|
|
46
46
|
console.log(type); // 打印文件操作的类型
|
|
47
|
+
return true; // 返回true或false,表示是否取消该操作
|
|
47
48
|
});
|
|
48
49
|
await vvvfs.createDir("/home/user/Desktop"); // 创建目录,返回true和false
|
|
49
50
|
await vvvfs.writeText("/home/user/Desktop/test.txt", "Hello World!"); // 写入文本文件,写入文件还包括write(path: string, content: Blob)和writeJson(path: string, content: Record<string, any>)方法,返回true和false
|
|
@@ -81,9 +82,17 @@ const vvvfs = new VVVFS("vvvfs", {
|
|
|
81
82
|
|
|
82
83
|
## 更新日志
|
|
83
84
|
|
|
85
|
+
### 0.1.0
|
|
86
|
+
|
|
87
|
+
- 修复 `init` 重复初始化时重复写入已存在文件和目录
|
|
88
|
+
|
|
89
|
+
### 0.0.9
|
|
90
|
+
|
|
91
|
+
- 更新 `watch` 方法,监听时,返回 `true` 表示取消该操作
|
|
92
|
+
|
|
84
93
|
### 0.0.8
|
|
85
94
|
|
|
86
|
-
- 新增 `watch` 方法
|
|
95
|
+
- 新增 `watch` 方法 (该方法属于实验性功能,后期可能会改变)
|
|
87
96
|
|
|
88
97
|
### 0.0.7
|
|
89
98
|
|
package/index.html
CHANGED
|
@@ -13,11 +13,13 @@
|
|
|
13
13
|
(async () => {
|
|
14
14
|
try {
|
|
15
15
|
globalThis.vvvfs = new VVVFS();
|
|
16
|
+
vvvfs.options.throwError = true;
|
|
16
17
|
await vvvfs.reset();
|
|
17
18
|
await vvvfs.init("IFTC");
|
|
18
19
|
console.log(vvvfs);
|
|
19
20
|
await vvvfs.watch("/test.txt", (type) => {
|
|
20
21
|
console.log(type);
|
|
22
|
+
// return true;
|
|
21
23
|
});
|
|
22
24
|
console.log("创建文件", await vvvfs.createFile("/test.txt"));
|
|
23
25
|
console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
|
package/index.ts
CHANGED
|
@@ -216,7 +216,7 @@ class VVVFSFile {
|
|
|
216
216
|
async search(query: string) {
|
|
217
217
|
return await this._vvvfs.search(this._path, query);
|
|
218
218
|
}
|
|
219
|
-
async watch(handler: (type: string) =>
|
|
219
|
+
async watch(handler: (type: string) => Promise<boolean>) {
|
|
220
220
|
return await this._vvvfs.watch(this._path, handler);
|
|
221
221
|
}
|
|
222
222
|
}
|
|
@@ -234,6 +234,10 @@ class VVVFS {
|
|
|
234
234
|
* 虚拟文件系统文件类
|
|
235
235
|
*/
|
|
236
236
|
static File = VVVFSFile;
|
|
237
|
+
/**
|
|
238
|
+
* 虚拟文件系统监听器
|
|
239
|
+
*/
|
|
240
|
+
watchers: Record<string, Array<(type: string) => Promise<boolean>>> = {};
|
|
237
241
|
/**
|
|
238
242
|
* 创建虚拟文件系统
|
|
239
243
|
* @param name 虚拟文件系统名称
|
|
@@ -385,6 +389,7 @@ class VVVFS {
|
|
|
385
389
|
];
|
|
386
390
|
try {
|
|
387
391
|
for (const file of linuxInitFiles) {
|
|
392
|
+
if (await this.exists(file.path)) continue;
|
|
388
393
|
await this.db.files.put(file);
|
|
389
394
|
}
|
|
390
395
|
} catch (error) {
|
|
@@ -413,11 +418,21 @@ class VVVFS {
|
|
|
413
418
|
*/
|
|
414
419
|
async createFile(path: string) {
|
|
415
420
|
const targetPath = joinPath(path);
|
|
416
|
-
if (await this.exists(targetPath)) {
|
|
417
|
-
console.warn("文件已存在");
|
|
418
|
-
return true;
|
|
419
|
-
}
|
|
420
421
|
try {
|
|
422
|
+
if (this.watchers[targetPath]) {
|
|
423
|
+
for (const handler of this.watchers[targetPath]) {
|
|
424
|
+
if (await handler("create")) {
|
|
425
|
+
if (this.options.throwError) {
|
|
426
|
+
throw new VVVFSError("CreateFile", "创建文件失败:监听器取消了操作");
|
|
427
|
+
}
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (await this.exists(targetPath)) {
|
|
433
|
+
console.warn("文件已存在");
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
421
436
|
const { name, parent } = parsePath(targetPath);
|
|
422
437
|
if (!(await this.exists(parent))) {
|
|
423
438
|
await this.createDir(parent);
|
|
@@ -445,11 +460,21 @@ class VVVFS {
|
|
|
445
460
|
*/
|
|
446
461
|
async createDir(path: string) {
|
|
447
462
|
const targetPath = joinPath(path);
|
|
448
|
-
if (await this.exists(targetPath)) {
|
|
449
|
-
console.warn("目录已存在");
|
|
450
|
-
return true;
|
|
451
|
-
}
|
|
452
463
|
try {
|
|
464
|
+
if (this.watchers[targetPath]) {
|
|
465
|
+
for (const handler of this.watchers[targetPath]) {
|
|
466
|
+
if (await handler("create")) {
|
|
467
|
+
if (this.options.throwError) {
|
|
468
|
+
throw new VVVFSError("CreateDir", "创建目录失败:监听器取消了操作");
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (await this.exists(targetPath)) {
|
|
475
|
+
console.warn("目录已存在");
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
453
478
|
const { name, parent } = parsePath(targetPath);
|
|
454
479
|
if (!(await this.exists(parent))) {
|
|
455
480
|
if (parent == "/") {
|
|
@@ -510,6 +535,16 @@ class VVVFS {
|
|
|
510
535
|
async write(path: string, content: Blob) {
|
|
511
536
|
try {
|
|
512
537
|
const targetPath = joinPath(path);
|
|
538
|
+
if (this.watchers[targetPath]) {
|
|
539
|
+
for (const handler of this.watchers[targetPath]) {
|
|
540
|
+
if (await handler("write")) {
|
|
541
|
+
if (this.options.throwError) {
|
|
542
|
+
throw new VVVFSError("Write", "写入文件失败:监听器取消了操作");
|
|
543
|
+
}
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
513
548
|
if (!(await this.exists(targetPath))) {
|
|
514
549
|
const success = await this.createFile(targetPath);
|
|
515
550
|
if (!success) {
|
|
@@ -597,6 +632,16 @@ class VVVFS {
|
|
|
597
632
|
async read(path: string) {
|
|
598
633
|
try {
|
|
599
634
|
const targetPath = joinPath(path);
|
|
635
|
+
if (this.watchers[targetPath]) {
|
|
636
|
+
for (const handler of this.watchers[targetPath]) {
|
|
637
|
+
if (await handler("read")) {
|
|
638
|
+
if (this.options.throwError) {
|
|
639
|
+
throw new VVVFSError("Read", "读取文件失败:监听器取消了操作");
|
|
640
|
+
}
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
600
645
|
if (!(await this.exists(targetPath))) {
|
|
601
646
|
console.warn("文件不存在");
|
|
602
647
|
if (this.options.throwError) {
|
|
@@ -707,6 +752,23 @@ class VVVFS {
|
|
|
707
752
|
async list(path: string) {
|
|
708
753
|
try {
|
|
709
754
|
const targetPath = joinPath(path);
|
|
755
|
+
if (this.watchers[targetPath]) {
|
|
756
|
+
for (const handler of this.watchers[targetPath]) {
|
|
757
|
+
if (await handler("list")) {
|
|
758
|
+
if (this.options.throwError) {
|
|
759
|
+
throw new VVVFSError("List", "列出目录下的文件失败:监听器取消了操作");
|
|
760
|
+
}
|
|
761
|
+
return [];
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
if (!(await this.exists(targetPath))) {
|
|
766
|
+
console.warn("路径不存在");
|
|
767
|
+
if (this.options.throwError) {
|
|
768
|
+
throw new VVVFSError("List", "路径不存在");
|
|
769
|
+
}
|
|
770
|
+
return [];
|
|
771
|
+
}
|
|
710
772
|
if (!(await this.isDir(targetPath))) {
|
|
711
773
|
console.warn("路径不是目录");
|
|
712
774
|
if (this.options.throwError) {
|
|
@@ -734,6 +796,16 @@ class VVVFS {
|
|
|
734
796
|
try {
|
|
735
797
|
const sourcePath = joinPath(path);
|
|
736
798
|
const { name, parent } = parsePath(sourcePath);
|
|
799
|
+
if (this.watchers[sourcePath]) {
|
|
800
|
+
for (const handler of this.watchers[sourcePath]) {
|
|
801
|
+
if (await handler("rename")) {
|
|
802
|
+
if (this.options.throwError) {
|
|
803
|
+
throw new VVVFSError("Rename", "重命名文件失败:监听器取消了操作");
|
|
804
|
+
}
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
737
809
|
if (!(await this.exists(sourcePath))) {
|
|
738
810
|
console.warn("文件不存在");
|
|
739
811
|
if (this.options.throwError) {
|
|
@@ -793,6 +865,16 @@ class VVVFS {
|
|
|
793
865
|
async delete(path: string) {
|
|
794
866
|
try {
|
|
795
867
|
const targetPath = joinPath(path);
|
|
868
|
+
if (this.watchers[targetPath]) {
|
|
869
|
+
for (const handler of this.watchers[targetPath]) {
|
|
870
|
+
if (await handler("delete")) {
|
|
871
|
+
if (this.options.throwError) {
|
|
872
|
+
throw new VVVFSError("Delete", "删除文件失败:监听器取消了操作");
|
|
873
|
+
}
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
796
878
|
if (!(await this.exists(targetPath))) {
|
|
797
879
|
console.warn("文件不存在");
|
|
798
880
|
if (this.options.throwError) {
|
|
@@ -839,6 +921,19 @@ class VVVFS {
|
|
|
839
921
|
try {
|
|
840
922
|
const sourcePath = joinPath(path);
|
|
841
923
|
const destinationPath = joinPath(newPath);
|
|
924
|
+
if (this.watchers[sourcePath]) {
|
|
925
|
+
for (const handler of this.watchers[sourcePath]) {
|
|
926
|
+
if (await handler("move")) {
|
|
927
|
+
if (this.options.throwError) {
|
|
928
|
+
throw new VVVFSError("Move", "移动文件失败:监听器取消了操作");
|
|
929
|
+
}
|
|
930
|
+
return false;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
if (sourcePath === destinationPath) {
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
842
937
|
if (await this.exists(destinationPath)) {
|
|
843
938
|
console.warn("目标文件已存在");
|
|
844
939
|
if (this.options.throwError) {
|
|
@@ -903,6 +998,16 @@ class VVVFS {
|
|
|
903
998
|
try {
|
|
904
999
|
const sourcePath = joinPath(path);
|
|
905
1000
|
const destinationPath = joinPath(newPath);
|
|
1001
|
+
if (this.watchers[sourcePath]) {
|
|
1002
|
+
for (const handler of this.watchers[sourcePath]) {
|
|
1003
|
+
if (await handler("copy")) {
|
|
1004
|
+
if (this.options.throwError) {
|
|
1005
|
+
throw new VVVFSError("Copy", "复制文件失败:监听器取消了操作");
|
|
1006
|
+
}
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
906
1011
|
if (await this.exists(destinationPath)) {
|
|
907
1012
|
console.warn("目标文件已存在");
|
|
908
1013
|
if (this.options.throwError) {
|
|
@@ -1006,62 +1111,12 @@ class VVVFS {
|
|
|
1006
1111
|
return null;
|
|
1007
1112
|
}
|
|
1008
1113
|
}
|
|
1009
|
-
async watch(path: string, handler: (type: string) =>
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
return { exists: false, signature: "" };
|
|
1016
|
-
}
|
|
1017
|
-
if (await this.isDir(targetPath)) {
|
|
1018
|
-
const records = await this.db.files
|
|
1019
|
-
.filter(
|
|
1020
|
-
(file) =>
|
|
1021
|
-
file.path === targetPath || file.path.startsWith(targetPath + "/"),
|
|
1022
|
-
)
|
|
1023
|
-
.toArray();
|
|
1024
|
-
const signature = records
|
|
1025
|
-
.map((file) => {
|
|
1026
|
-
const fullPath = joinPath(file.path, file.name);
|
|
1027
|
-
const fileInfo = file.file
|
|
1028
|
-
? `${file.file.type}:${file.file.size}:${file.file.lastModified}`
|
|
1029
|
-
: "null";
|
|
1030
|
-
return `${fullPath}:${file.type}:${fileInfo}`;
|
|
1031
|
-
})
|
|
1032
|
-
.sort()
|
|
1033
|
-
.join("|");
|
|
1034
|
-
return { exists: true, signature };
|
|
1035
|
-
}
|
|
1036
|
-
const { name, parent } = parsePath(targetPath);
|
|
1037
|
-
const record = await this.db.files.where({ name, path: parent, type: "file" }).first();
|
|
1038
|
-
const signature = record?.file
|
|
1039
|
-
? `${record.file.type}:${record.file.size}:${record.file.lastModified}`
|
|
1040
|
-
: "";
|
|
1041
|
-
return { exists: true, signature };
|
|
1042
|
-
};
|
|
1043
|
-
|
|
1044
|
-
let previousState = await getState();
|
|
1045
|
-
setInterval(async () => {
|
|
1046
|
-
try {
|
|
1047
|
-
const currentState = await getState();
|
|
1048
|
-
if (
|
|
1049
|
-
previousState.exists !== currentState.exists ||
|
|
1050
|
-
previousState.signature !== currentState.signature
|
|
1051
|
-
) {
|
|
1052
|
-
if (!previousState.exists && currentState.exists) {
|
|
1053
|
-
handler("create");
|
|
1054
|
-
} else if (previousState.exists && !currentState.exists) {
|
|
1055
|
-
handler("unlink");
|
|
1056
|
-
} else {
|
|
1057
|
-
handler("change");
|
|
1058
|
-
}
|
|
1059
|
-
previousState = currentState;
|
|
1060
|
-
}
|
|
1061
|
-
} catch (error) {
|
|
1062
|
-
console.error("监听失败", error);
|
|
1063
|
-
}
|
|
1064
|
-
});
|
|
1114
|
+
async watch(path: string, handler: (type: string) => Promise<boolean>) {
|
|
1115
|
+
path = joinPath(path);
|
|
1116
|
+
if (!this.watchers[path]) {
|
|
1117
|
+
this.watchers[path] = [];
|
|
1118
|
+
}
|
|
1119
|
+
this.watchers[path].push(handler);
|
|
1065
1120
|
}
|
|
1066
1121
|
}
|
|
1067
1122
|
function parsePath(path: string) {
|