vvvfs 0.0.6 → 0.0.8

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.
Files changed (4) hide show
  1. package/README.md +11 -0
  2. package/index.html +4 -0
  3. package/index.ts +211 -65
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -42,6 +42,9 @@ const vvvfs = new VVVFS("vvvfs", {
42
42
  (async function () {
43
43
  await vvvfs.reset(); // 重置文件系统
44
44
  await vvvfs.init("UserName"); // 初始化文件系统
45
+ await vvvfs.watch("/home/user/Desktop/test.txt", (type) => {
46
+ console.log(type); // 打印文件操作的类型
47
+ });
45
48
  await vvvfs.createDir("/home/user/Desktop"); // 创建目录,返回true和false
46
49
  await vvvfs.writeText("/home/user/Desktop/test.txt", "Hello World!"); // 写入文本文件,写入文件还包括write(path: string, content: Blob)和writeJson(path: string, content: Record<string, any>)方法,返回true和false
47
50
  console.log(await vvvfs.readText("/home/user/Desktop/test.txt")); // 读取文本文件,读取文件还包括read(path: string): Blob | null和readJson(path: string): Record<string, any> | null方法
@@ -78,6 +81,14 @@ const vvvfs = new VVVFS("vvvfs", {
78
81
 
79
82
  ## 更新日志
80
83
 
84
+ ### 0.0.8
85
+
86
+ - 新增 `watch` 方法
87
+
88
+ ### 0.0.7
89
+
90
+ - AI 了优化一下
91
+
81
92
  ### 0.0.6
82
93
 
83
94
  - `VVVFS.File` 类中新增 `dbname` 和 `options` 属性
package/index.html CHANGED
@@ -16,6 +16,9 @@
16
16
  await vvvfs.reset();
17
17
  await vvvfs.init("IFTC");
18
18
  console.log(vvvfs);
19
+ await vvvfs.watch("/test.txt", (type) => {
20
+ console.log(type);
21
+ });
19
22
  console.log("创建文件", await vvvfs.createFile("/test.txt"));
20
23
  console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
21
24
  console.log("文件是否存在", await vvvfs.exists("/test.txt"));
@@ -26,6 +29,7 @@
26
29
  console.log("移动文件", await vvvfs.move("/test2.txt", "/test3.txt"));
27
30
  console.log("删除文件", await vvvfs.delete("/test.txt"));
28
31
  const file = new VVVFS.File("test.txt");
32
+ console.log("写入文件", await file.writeText("Hello World"));
29
33
  } catch (error) {
30
34
  console.error(error);
31
35
  }
package/index.ts CHANGED
@@ -216,6 +216,9 @@ 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) => void) {
220
+ return await this._vvvfs.watch(this._path, handler);
221
+ }
219
222
  }
220
223
 
221
224
  const version = packageJson.version;
@@ -409,22 +412,22 @@ class VVVFS {
409
412
  * @param path 文件路径
410
413
  */
411
414
  async createFile(path: string) {
412
- if (await this.exists(path)) {
415
+ const targetPath = joinPath(path);
416
+ if (await this.exists(targetPath)) {
413
417
  console.warn("文件已存在");
414
418
  return true;
415
419
  }
416
420
  try {
417
- const { name, parent } = parsePath(path);
421
+ const { name, parent } = parsePath(targetPath);
418
422
  if (!(await this.exists(parent))) {
419
423
  await this.createDir(parent);
420
424
  }
421
- // console.log(name, parent);
422
425
  await this.db.files.add({
423
426
  name: name,
424
427
  path: parent,
425
428
  type: "file",
426
429
  file: new File([], name, {
427
- type: mime.getType(path) || "application/octet-stream",
430
+ type: mime.getType(targetPath) || "application/octet-stream",
428
431
  }),
429
432
  });
430
433
  return true;
@@ -441,12 +444,13 @@ class VVVFS {
441
444
  * @param path 目录路径
442
445
  */
443
446
  async createDir(path: string) {
444
- if (await this.exists(path)) {
447
+ const targetPath = joinPath(path);
448
+ if (await this.exists(targetPath)) {
445
449
  console.warn("目录已存在");
446
450
  return true;
447
451
  }
448
452
  try {
449
- const { name, parent } = parsePath(path);
453
+ const { name, parent } = parsePath(targetPath);
450
454
  if (!(await this.exists(parent))) {
451
455
  if (parent == "/") {
452
456
  await this.db.files.add({
@@ -460,7 +464,6 @@ class VVVFS {
460
464
  await this.createDir(parent);
461
465
  }
462
466
  }
463
- console.log(name, parent);
464
467
  await this.db.files.add({
465
468
  name: name,
466
469
  path: parent,
@@ -482,7 +485,8 @@ class VVVFS {
482
485
  */
483
486
  async exists(path: string) {
484
487
  try {
485
- const { name, parent } = parsePath(path);
488
+ const targetPath = joinPath(path);
489
+ const { name, parent } = parsePath(targetPath);
486
490
  return (
487
491
  (await this.db.files
488
492
  .where({
@@ -505,8 +509,9 @@ class VVVFS {
505
509
  */
506
510
  async write(path: string, content: Blob) {
507
511
  try {
508
- if (!(await this.exists(path))) {
509
- const success = await this.createFile(path);
512
+ const targetPath = joinPath(path);
513
+ if (!(await this.exists(targetPath))) {
514
+ const success = await this.createFile(targetPath);
510
515
  if (!success) {
511
516
  console.warn("创建文件失败");
512
517
  if (this.options.throwError) {
@@ -515,19 +520,17 @@ class VVVFS {
515
520
  return false;
516
521
  }
517
522
  }
518
- const { name, parent } = parsePath(path);
519
- console.log(name, parent);
520
- if (await this.isDir(parent + "/" + name)) {
523
+ if (await this.isDir(targetPath)) {
521
524
  console.warn("路径已存在");
522
525
  if (this.options.throwError) {
523
526
  throw new VVVFSError("Write", "路径已存在");
524
527
  }
525
528
  return false;
526
529
  }
530
+ const { name, parent } = parsePath(targetPath);
527
531
  const file = new File([content], name, {
528
- type: mime.getType(path) || "application/octet-stream",
532
+ type: mime.getType(targetPath) || "application/octet-stream",
529
533
  });
530
- console.log(file);
531
534
  const fileRecord = await this.db.files.where({ name, path: parent }).first();
532
535
  if (fileRecord) {
533
536
  await this.db.files.put({
@@ -593,14 +596,15 @@ class VVVFS {
593
596
  */
594
597
  async read(path: string) {
595
598
  try {
596
- if (!(await this.exists(path))) {
599
+ const targetPath = joinPath(path);
600
+ if (!(await this.exists(targetPath))) {
597
601
  console.warn("文件不存在");
598
602
  if (this.options.throwError) {
599
603
  throw new VVVFSError("Read", "文件不存在");
600
604
  }
601
605
  return null;
602
606
  }
603
- const { name, parent } = parsePath(path);
607
+ const { name, parent } = parsePath(targetPath);
604
608
  return (await this.db.files.where({ name, path: parent }).first())?.file;
605
609
  } catch (error) {
606
610
  console.error("读取文件失败", error);
@@ -668,7 +672,8 @@ class VVVFS {
668
672
  */
669
673
  async isFile(path: string) {
670
674
  try {
671
- const { name, parent } = parsePath(path);
675
+ const targetPath = joinPath(path);
676
+ const { name, parent } = parsePath(targetPath);
672
677
  return (await this.db.files.where({ name, path: parent, type: "file" }).count()) > 0;
673
678
  } catch (error) {
674
679
  console.error("判断文件类型失败", error);
@@ -684,7 +689,8 @@ class VVVFS {
684
689
  */
685
690
  async isDir(path: string) {
686
691
  try {
687
- const { name, parent } = parsePath(path);
692
+ const targetPath = joinPath(path);
693
+ const { name, parent } = parsePath(targetPath);
688
694
  return (await this.db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
689
695
  } catch (error) {
690
696
  console.error("判断文件类型失败", error);
@@ -700,8 +706,15 @@ class VVVFS {
700
706
  */
701
707
  async list(path: string) {
702
708
  try {
703
- const { name, parent } = parsePath(path);
704
- return (await this.db.files.where({ path: parent }).toArray())
709
+ const targetPath = joinPath(path);
710
+ if (!(await this.isDir(targetPath))) {
711
+ console.warn("路径不是目录");
712
+ if (this.options.throwError) {
713
+ throw new VVVFSError("List", "路径不是目录");
714
+ }
715
+ return [];
716
+ }
717
+ return (await this.db.files.where({ path: targetPath }).toArray())
705
718
  .map((file) => file.name)
706
719
  .filter((item) => item != "");
707
720
  } catch (error) {
@@ -719,20 +732,50 @@ class VVVFS {
719
732
  */
720
733
  async rename(path: string, newName: string) {
721
734
  try {
722
- if (!(await this.exists(path))) {
735
+ const sourcePath = joinPath(path);
736
+ const { name, parent } = parsePath(sourcePath);
737
+ if (!(await this.exists(sourcePath))) {
723
738
  console.warn("文件不存在");
724
739
  if (this.options.throwError) {
725
740
  throw new VVVFSError("Rename", "文件不存在");
726
741
  }
727
742
  return false;
728
743
  }
729
- const { name, parent } = parsePath(path);
730
- const file = await this.db.files.get({ path, name });
731
- await this.db.files.put({
744
+ const newPath = joinPath(parent, newName);
745
+ if (newPath === sourcePath) {
746
+ return true;
747
+ }
748
+ if (await this.exists(newPath)) {
749
+ console.warn("目标文件已存在");
750
+ if (this.options.throwError) {
751
+ throw new VVVFSError("Rename", "目标文件已存在");
752
+ }
753
+ return false;
754
+ }
755
+ const fileRecord = await this.db.files.where({ path: parent, name }).first();
756
+ if (!fileRecord) {
757
+ console.warn("文件记录未找到");
758
+ if (this.options.throwError) {
759
+ throw new VVVFSError("Rename", "文件记录未找到");
760
+ }
761
+ return false;
762
+ }
763
+ if (fileRecord.type === "dir") {
764
+ const descendants = await this.db.files
765
+ .filter(
766
+ (file) =>
767
+ file.path === sourcePath || file.path.startsWith(sourcePath + "/"),
768
+ )
769
+ .toArray();
770
+ for (const descendant of descendants) {
771
+ const relativePath = descendant.path.slice(sourcePath.length);
772
+ const updatedPath = joinPath(newPath + relativePath);
773
+ await this.db.files.update(descendant.id!, { path: updatedPath });
774
+ }
775
+ }
776
+ await this.db.files.update(fileRecord.id!, {
732
777
  name: newName,
733
778
  path: parent,
734
- type: "file",
735
- file: file!.file,
736
779
  });
737
780
  return true;
738
781
  } catch (e) {
@@ -749,29 +792,34 @@ class VVVFS {
749
792
  */
750
793
  async delete(path: string) {
751
794
  try {
752
- if (!(await this.exists(path))) {
795
+ const targetPath = joinPath(path);
796
+ if (!(await this.exists(targetPath))) {
753
797
  console.warn("文件不存在");
754
798
  if (this.options.throwError) {
755
799
  throw new VVVFSError("Delete", "文件不存在");
756
800
  }
757
801
  return false;
758
802
  }
759
- const { name, parent } = parsePath(path);
760
- if (await this.isDir(path)) {
761
- const files = await this.list(path);
803
+ const { name, parent } = parsePath(targetPath);
804
+ if (await this.isDir(targetPath)) {
805
+ const files = await this.list(targetPath);
762
806
  for (const file of files) {
763
- if (await this.isDir(joinPath(path, file))) {
764
- const fileRecord = await this.db.files
765
- .where({ name, path: parent })
766
- .first();
767
- await this.db.files.delete(fileRecord!.id!);
768
- }
769
- await this.delete(joinPath(parent, file));
807
+ await this.delete(joinPath(targetPath, file));
808
+ }
809
+ const dirRecord = await this.db.files
810
+ .where({ name, path: parent, type: "dir" })
811
+ .first();
812
+ if (dirRecord) {
813
+ await this.db.files.delete(dirRecord.id!);
770
814
  }
771
815
  return true;
772
816
  } else {
773
- const fileRecord = await this.db.files.where({ name, path: parent }).first();
774
- await this.db.files.delete(fileRecord!.id!);
817
+ const fileRecord = await this.db.files
818
+ .where({ name, path: parent, type: "file" })
819
+ .first();
820
+ if (fileRecord) {
821
+ await this.db.files.delete(fileRecord.id!);
822
+ }
775
823
  return true;
776
824
  }
777
825
  } catch (e) {
@@ -789,34 +837,55 @@ class VVVFS {
789
837
  */
790
838
  async move(path: string, newPath: string) {
791
839
  try {
792
- if (await this.exists(newPath)) {
840
+ const sourcePath = joinPath(path);
841
+ const destinationPath = joinPath(newPath);
842
+ if (await this.exists(destinationPath)) {
793
843
  console.warn("目标文件已存在");
794
844
  if (this.options.throwError) {
795
845
  throw new VVVFSError("Move", "目标文件已存在");
796
846
  }
797
847
  return false;
798
848
  }
799
- if (!(await this.exists(path))) {
849
+ if (!(await this.exists(sourcePath))) {
800
850
  console.warn("源文件不存在");
801
851
  if (this.options.throwError) {
802
852
  throw new VVVFSError("Move", "源文件不存在");
803
853
  }
804
854
  return false;
805
855
  }
806
- const { name, parent } = parsePath(path);
807
- const { name: newName, parent: newParent } = parsePath(newPath);
808
- await this.createDir(newParent);
809
- if (await this.isDir(path)) {
810
- const dirs = await this.list(path);
811
- for (const dir of dirs) {
812
- await this.move(path + "/" + dir, newPath + "/" + dir);
856
+ if (destinationPath.startsWith(sourcePath + "/")) {
857
+ console.warn("目标路径是源路径的子路径");
858
+ if (this.options.throwError) {
859
+ throw new VVVFSError("Move", "目标路径是源路径的子路径");
860
+ }
861
+ return false;
862
+ }
863
+ const { name, parent } = parsePath(sourcePath);
864
+ const { name: newName, parent: newParent } = parsePath(destinationPath);
865
+ if (await this.isDir(sourcePath)) {
866
+ await this.createDir(destinationPath);
867
+ const children = await this.list(sourcePath);
868
+ for (const child of children) {
869
+ await this.move(joinPath(sourcePath, child), joinPath(destinationPath, child));
870
+ }
871
+ const dirRecord = await this.db.files
872
+ .where({ name, path: parent, type: "dir" })
873
+ .first();
874
+ if (dirRecord) {
875
+ await this.db.files.delete(dirRecord.id!);
813
876
  }
877
+ return true;
814
878
  } else {
815
- const fileRecord = await this.db.files.where({ name, path: parent }).first();
816
- await this.db.files.update(fileRecord!.id!, { name: newName, path: newParent });
879
+ await this.createDir(newParent);
880
+ const fileRecord = await this.db.files
881
+ .where({ name, path: parent, type: "file" })
882
+ .first();
883
+ if (fileRecord) {
884
+ await this.db.files.update(fileRecord.id!, { name: newName, path: newParent });
885
+ return true;
886
+ }
887
+ return false;
817
888
  }
818
- await this.delete(path);
819
- return true;
820
889
  } catch (e) {
821
890
  console.error(e);
822
891
  if (this.options.throwError) {
@@ -832,33 +901,53 @@ class VVVFS {
832
901
  */
833
902
  async copy(path: string, newPath: string) {
834
903
  try {
835
- if (await this.exists(newPath)) {
904
+ const sourcePath = joinPath(path);
905
+ const destinationPath = joinPath(newPath);
906
+ if (await this.exists(destinationPath)) {
836
907
  console.warn("目标文件已存在");
837
908
  if (this.options.throwError) {
838
909
  throw new VVVFSError("Copy", "目标文件已存在");
839
910
  }
840
911
  return false;
841
912
  }
842
- if (!(await this.exists(path))) {
913
+ if (!(await this.exists(sourcePath))) {
843
914
  console.warn("源文件不存在");
844
915
  if (this.options.throwError) {
845
916
  throw new VVVFSError("Copy", "源文件不存在");
846
917
  }
847
918
  return false;
848
919
  }
849
- const { name, parent } = parsePath(path);
850
- const { name: newName, parent: newParent } = parsePath(newPath);
851
- await this.createDir(newParent);
852
- if (await this.isDir(path)) {
853
- const dirs = await this.list(path);
854
- for (const dir of dirs) {
855
- await this.move(path + "/" + dir, newPath + "/" + dir);
920
+ if (destinationPath.startsWith(sourcePath + "/")) {
921
+ console.warn("目标路径是源路径的子路径");
922
+ if (this.options.throwError) {
923
+ throw new VVVFSError("Copy", "目标路径是源路径的子路径");
924
+ }
925
+ return false;
926
+ }
927
+ const { name, parent } = parsePath(sourcePath);
928
+ const { name: newName, parent: newParent } = parsePath(destinationPath);
929
+ if (await this.isDir(sourcePath)) {
930
+ await this.createDir(destinationPath);
931
+ const children = await this.list(sourcePath);
932
+ for (const child of children) {
933
+ await this.copy(joinPath(sourcePath, child), joinPath(destinationPath, child));
856
934
  }
935
+ return true;
857
936
  } else {
858
- const fileRecord = await this.db.files.where({ name, path: parent }).first();
859
- await this.db.files.update(fileRecord!.id!, { name: newName, path: newParent });
937
+ const fileRecord = await this.db.files
938
+ .where({ name, path: parent, type: "file" })
939
+ .first();
940
+ if (fileRecord) {
941
+ await this.db.files.add({
942
+ name: newName,
943
+ path: newParent,
944
+ type: fileRecord.type,
945
+ file: fileRecord.file,
946
+ });
947
+ return true;
948
+ }
949
+ return false;
860
950
  }
861
- return true;
862
951
  } catch (e) {
863
952
  console.error(e);
864
953
  if (this.options.throwError) {
@@ -917,6 +1006,63 @@ class VVVFS {
917
1006
  return null;
918
1007
  }
919
1008
  }
1009
+ async watch(path: string, handler: (type: string) => void) {
1010
+ const targetPath = joinPath(path);
1011
+
1012
+ const getState = async () => {
1013
+ const exists = await this.exists(targetPath);
1014
+ if (!exists) {
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
+ });
1065
+ }
920
1066
  }
921
1067
  function parsePath(path: string) {
922
1068
  path = joinPath(path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vvvfs",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "一个使用IndexDB实现的虚拟文件系统",
5
5
  "keywords": [
6
6
  "Virtual",