vvvfs 0.1.1 → 0.1.3

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 -1
  2. package/index.html +4 -2
  3. package/index.ts +279 -58
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -42,14 +42,17 @@ 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) => {
45
+ vvvfs.watch("/home/user/Desktop/test.txt", (type) => {
46
46
  console.log(type); // 打印文件操作的类型
47
47
  return true; // 返回true或false,表示是否取消该操作
48
48
  });
49
+ await vvvfs.lock("/home/user/Desktop/test.txt"); // 锁定文件,防止其他代码访问该文件
50
+ await vvvfs.unlock("/home/user/Desktop/test.txt"); // 解锁文件
49
51
  await vvvfs.createDir("/home/user/Desktop"); // 创建目录,返回true和false
50
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
51
53
  await vvvfs.appendText("/home/user/Desktop/test.txt", "Hello World!"); // 追加文本文件,返回true和false
52
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
53
56
  await vvvfs.delete("/home/user/Desktop/test.txt"); // 删除文件,返回true和false
54
57
  if (await vvvfs.exists("/home/user/Desktop")) {
55
58
  // 判断文件或目录是否存在
@@ -83,6 +86,13 @@ const vvvfs = new VVVFS("vvvfs", {
83
86
 
84
87
  ## 更新日志
85
88
 
89
+ ### 0.1.3
90
+ - 新增 `lock` 和 `unlock` 方法,可以锁定文件,防止其他代码访问该文件
91
+
92
+ ### 0.1.2
93
+
94
+ - 新增 `readChunk` 和 `readChunkText` 方法,用于读取文件块
95
+
86
96
  ### 0.1.1
87
97
 
88
98
  - 新增 `append` 和 `appendText` 方法,用于追加内容
package/index.html CHANGED
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="zh-CN">
3
3
 
4
4
  <head>
5
5
  <meta charset="UTF-8">
@@ -17,16 +17,18 @@
17
17
  await vvvfs.reset();
18
18
  await vvvfs.init("IFTC");
19
19
  console.log(vvvfs);
20
- await vvvfs.watch("/test.txt", (type) => {
20
+ vvvfs.watch("/test.txt", (type) => {
21
21
  console.log(type);
22
22
  // return true;
23
23
  });
24
24
  console.log("创建文件", await vvvfs.createFile("/test.txt"));
25
25
  console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
26
26
  console.log("追加文件", await vvvfs.appendText("/test.txt", " - Appended"));
27
+ // await vvvfs.lock("/test.txt");
27
28
  console.log("文件是否存在", await vvvfs.exists("/test.txt"));
28
29
  console.log("文件是否存在", await vvvfs.exists("/test2.txt"));
29
30
  console.log("读取文件", await vvvfs.readText("/test.txt"));
31
+ console.log("读取文件块", await vvvfs.readTextChunk("/test.txt", 0, 5));
30
32
  console.log("复制文件", await vvvfs.copy("/test.txt", "/test2.txt"));
31
33
  console.log("搜索文件", await vvvfs.search("/", "t"));
32
34
  console.log("移动文件", await vvvfs.move("/test2.txt", "/test3.txt"));
package/index.ts CHANGED
@@ -129,6 +129,22 @@ class VVVFSFile {
129
129
  async readJSON() {
130
130
  return await this._vvvfs.readJson(this._path);
131
131
  }
132
+ /**
133
+ * 读取文件内容
134
+ * @param start 起始位置
135
+ * @param end 结束位置
136
+ */
137
+ async readChunk(start: number, end: number) {
138
+ return await this._vvvfs.readChunk(this._path, start, end);
139
+ }
140
+ /**
141
+ * 读取文件内容
142
+ * @param start 起始位置
143
+ * @param end 结束位置
144
+ */
145
+ async readTextChunk(start: number, end: number) {
146
+ return await this._vvvfs.readTextChunk(this._path, start, end);
147
+ }
132
148
  /**
133
149
  * 写入文件
134
150
  * @param file 文件对象
@@ -230,15 +246,30 @@ class VVVFSFile {
230
246
  async search(query: string) {
231
247
  return await this._vvvfs.search(this._path, query);
232
248
  }
233
- async watch(handler: (type: string) => Promise<boolean>) {
234
- return await this._vvvfs.watch(this._path, handler);
249
+ /**
250
+ * 监听文件
251
+ */
252
+ watch(handler: (type: string) => Promise<boolean>) {
253
+ return this._vvvfs.watch(this._path, handler);
254
+ }
255
+ /**
256
+ * 锁定文件
257
+ */
258
+ async lock() {
259
+ return await this._vvvfs.lock(this._path);
260
+ }
261
+ /**
262
+ * 解锁文件
263
+ */
264
+ async unlock() {
265
+ return await this._vvvfs.unlock(this._path);
235
266
  }
236
267
  }
237
268
 
238
269
  const version = packageJson.version;
239
270
  class VVVFS {
240
271
  static defaultDBName = "vvvfs";
241
- private db: VVVFSDatabase;
272
+ #db: VVVFSDatabase;
242
273
  options: VVVFSOptions;
243
274
  /**
244
275
  * 虚拟文件系统版本
@@ -251,7 +282,11 @@ class VVVFS {
251
282
  /**
252
283
  * 虚拟文件系统监听器
253
284
  */
254
- watchers: Record<string, Array<(type: string) => Promise<boolean>>> = {};
285
+ #watchers: Record<string, Array<(type: string) => Promise<boolean>>> = {};
286
+ /**
287
+ * 锁定的文件
288
+ */
289
+ #lockedFiles: Record<string, boolean> = {};
255
290
  /**
256
291
  * 创建虚拟文件系统
257
292
  * @param name 虚拟文件系统名称
@@ -265,8 +300,8 @@ class VVVFS {
265
300
  ) {
266
301
  this.options = options;
267
302
  try {
268
- this.db = new Dexie(name || VVVFS.defaultDBName) as VVVFSDatabase;
269
- this.db.version(1).stores({
303
+ this.#db = new Dexie(name || VVVFS.defaultDBName) as VVVFSDatabase;
304
+ this.#db.version(1).stores({
270
305
  files: "++id, name, path, type, file, [name+path+type]",
271
306
  });
272
307
  } catch (error) {
@@ -404,7 +439,7 @@ class VVVFS {
404
439
  try {
405
440
  for (const file of linuxInitFiles) {
406
441
  if (await this.exists(file.path)) continue;
407
- await this.db.files.put(file);
442
+ await this.#db.files.put(file);
408
443
  }
409
444
  } catch (error) {
410
445
  console.error("初始化文件失败", error);
@@ -416,9 +451,9 @@ class VVVFS {
416
451
  */
417
452
  async reset() {
418
453
  try {
419
- await this.db.delete();
420
- this.db = new Dexie(this.db.name) as VVVFSDatabase;
421
- this.db.version(1).stores({
454
+ await this.#db.delete();
455
+ this.#db = new Dexie(this.#db.name) as VVVFSDatabase;
456
+ this.#db.version(1).stores({
422
457
  files: "++id, name, path, type, file, [name+path+type]",
423
458
  });
424
459
  } catch (error) {
@@ -433,8 +468,8 @@ class VVVFS {
433
468
  async createFile(path: string) {
434
469
  const targetPath = joinPath(path);
435
470
  try {
436
- if (this.watchers[targetPath]) {
437
- for (const handler of this.watchers[targetPath]) {
471
+ if (this.#watchers[targetPath]) {
472
+ for (const handler of this.#watchers[targetPath]) {
438
473
  if (await handler("create")) {
439
474
  if (this.options.throwError) {
440
475
  throw new VVVFSError("CreateFile", "创建文件失败:监听器取消了操作");
@@ -451,7 +486,7 @@ class VVVFS {
451
486
  if (!(await this.exists(parent))) {
452
487
  await this.createDir(parent);
453
488
  }
454
- await this.db.files.add({
489
+ await this.#db.files.add({
455
490
  name: name,
456
491
  path: parent,
457
492
  type: "file",
@@ -475,8 +510,8 @@ class VVVFS {
475
510
  async createDir(path: string) {
476
511
  const targetPath = joinPath(path);
477
512
  try {
478
- if (this.watchers[targetPath]) {
479
- for (const handler of this.watchers[targetPath]) {
513
+ if (this.#watchers[targetPath]) {
514
+ for (const handler of this.#watchers[targetPath]) {
480
515
  if (await handler("create")) {
481
516
  if (this.options.throwError) {
482
517
  throw new VVVFSError("CreateDir", "创建目录失败:监听器取消了操作");
@@ -492,7 +527,7 @@ class VVVFS {
492
527
  const { name, parent } = parsePath(targetPath);
493
528
  if (!(await this.exists(parent))) {
494
529
  if (parent == "/") {
495
- await this.db.files.add({
530
+ await this.#db.files.add({
496
531
  name: "",
497
532
  path: "/",
498
533
  type: "dir",
@@ -503,7 +538,7 @@ class VVVFS {
503
538
  await this.createDir(parent);
504
539
  }
505
540
  }
506
- await this.db.files.add({
541
+ await this.#db.files.add({
507
542
  name: name,
508
543
  path: parent,
509
544
  type: "dir",
@@ -527,7 +562,7 @@ class VVVFS {
527
562
  const targetPath = joinPath(path);
528
563
  const { name, parent } = parsePath(targetPath);
529
564
  return (
530
- (await this.db.files
565
+ (await this.#db.files
531
566
  .where({
532
567
  name: name,
533
568
  path: parent,
@@ -549,8 +584,8 @@ class VVVFS {
549
584
  async write(path: string, content: Blob) {
550
585
  try {
551
586
  const targetPath = joinPath(path);
552
- if (this.watchers[targetPath]) {
553
- for (const handler of this.watchers[targetPath]) {
587
+ if (this.#watchers[targetPath]) {
588
+ for (const handler of this.#watchers[targetPath]) {
554
589
  if (await handler("write")) {
555
590
  if (this.options.throwError) {
556
591
  throw new VVVFSError("Write", "写入文件失败:监听器取消了操作");
@@ -559,6 +594,13 @@ class VVVFS {
559
594
  }
560
595
  }
561
596
  }
597
+ if (this.#lockedFiles[targetPath]) {
598
+ console.warn("文件已被锁定");
599
+ if (this.options.throwError) {
600
+ throw new VVVFSError("Write", "文件已被锁定");
601
+ }
602
+ return false;
603
+ }
562
604
  if (!(await this.exists(targetPath))) {
563
605
  const success = await this.createFile(targetPath);
564
606
  if (!success) {
@@ -580,9 +622,9 @@ class VVVFS {
580
622
  const file = new File([content], name, {
581
623
  type: mime.getType(targetPath) || "application/octet-stream",
582
624
  });
583
- const fileRecord = await this.db.files.where({ name, path: parent }).first();
625
+ const fileRecord = await this.#db.files.where({ name, path: parent }).first();
584
626
  if (fileRecord) {
585
- await this.db.files.put({
627
+ await this.#db.files.put({
586
628
  ...fileRecord,
587
629
  file: file,
588
630
  });
@@ -647,8 +689,8 @@ class VVVFS {
647
689
  async append(path: string, content: Blob) {
648
690
  try {
649
691
  const targetPath = joinPath(path);
650
- if (this.watchers[targetPath]) {
651
- for (const handler of this.watchers[targetPath]) {
692
+ if (this.#watchers[targetPath]) {
693
+ for (const handler of this.#watchers[targetPath]) {
652
694
  if (await handler("append")) {
653
695
  if (this.options.throwError) {
654
696
  throw new VVVFSError("Append", "追加文件失败:监听器取消了操作");
@@ -657,6 +699,13 @@ class VVVFS {
657
699
  }
658
700
  }
659
701
  }
702
+ if (this.#lockedFiles[targetPath]) {
703
+ console.warn("文件已被锁定");
704
+ if (this.options.throwError) {
705
+ throw new VVVFSError("Append", "文件已被锁定");
706
+ }
707
+ return false;
708
+ }
660
709
  const existingFile = await this.read(targetPath);
661
710
  if (existingFile) {
662
711
  const blob = new Blob([existingFile, content], {
@@ -698,8 +747,8 @@ class VVVFS {
698
747
  async read(path: string) {
699
748
  try {
700
749
  const targetPath = joinPath(path);
701
- if (this.watchers[targetPath]) {
702
- for (const handler of this.watchers[targetPath]) {
750
+ if (this.#watchers[targetPath]) {
751
+ for (const handler of this.#watchers[targetPath]) {
703
752
  if (await handler("read")) {
704
753
  if (this.options.throwError) {
705
754
  throw new VVVFSError("Read", "读取文件失败:监听器取消了操作");
@@ -708,6 +757,13 @@ class VVVFS {
708
757
  }
709
758
  }
710
759
  }
760
+ if (this.#lockedFiles[targetPath]) {
761
+ console.warn("文件已被锁定");
762
+ if (this.options.throwError) {
763
+ throw new VVVFSError("Read", "文件已被锁定");
764
+ }
765
+ return null;
766
+ }
711
767
  if (!(await this.exists(targetPath))) {
712
768
  console.warn("文件不存在");
713
769
  if (this.options.throwError) {
@@ -716,7 +772,7 @@ class VVVFS {
716
772
  return null;
717
773
  }
718
774
  const { name, parent } = parsePath(targetPath);
719
- return (await this.db.files.where({ name, path: parent }).first())?.file;
775
+ return (await this.#db.files.where({ name, path: parent }).first())?.file;
720
776
  } catch (error) {
721
777
  console.error("读取文件失败", error);
722
778
  if (this.options.throwError) {
@@ -777,6 +833,70 @@ class VVVFS {
777
833
  return null;
778
834
  }
779
835
  }
836
+ /**
837
+ * 读取文件内容
838
+ * @param path 文件路径
839
+ * @param start 开始位置
840
+ * @param end 结束位置
841
+ */
842
+ async readChunk(path: string, start: number, end: number) {
843
+ try {
844
+ const targetPath = joinPath(path);
845
+ if (this.#watchers[targetPath]) {
846
+ for (const handler of this.#watchers[targetPath]) {
847
+ if (await handler("read")) {
848
+ if (this.options.throwError) {
849
+ throw new VVVFSError("Read", "读取文件失败:监听器取消了操作");
850
+ }
851
+ return null;
852
+ }
853
+ }
854
+ }
855
+ if (this.#lockedFiles[targetPath]) {
856
+ console.warn("文件已被锁定");
857
+ if (this.options.throwError) {
858
+ throw new VVVFSError("Read", "文件已被锁定");
859
+ }
860
+ return null;
861
+ }
862
+ if (!(await this.exists(targetPath))) {
863
+ console.warn("文件不存在");
864
+ if (this.options.throwError) {
865
+ throw new VVVFSError("Read", "文件不存在");
866
+ }
867
+ return null;
868
+ }
869
+ const file = await this.read(targetPath);
870
+ return file?.slice(start, end) || null;
871
+ } catch (error) {
872
+ console.error("读取文件失败", error);
873
+ if (this.options.throwError) {
874
+ throw new VVVFSError("Read", "读取文件失败" + error);
875
+ }
876
+ return null;
877
+ }
878
+ }
879
+ /**
880
+ * 读取文件内容
881
+ * @param path 文件路径
882
+ * @param start 读取开始位置
883
+ * @param end 读取结束位置
884
+ */
885
+ async readTextChunk(path: string, start: number, end: number) {
886
+ try {
887
+ const chunk = await this.readChunk(path, start, end);
888
+ if (chunk) {
889
+ return await chunk.text();
890
+ } else {
891
+ return null;
892
+ }
893
+ } catch (error) {
894
+ console.error("读取文件失败", error);
895
+ if (this.options.throwError) {
896
+ throw new VVVFSError("Read", "读取文件失败" + error);
897
+ }
898
+ }
899
+ }
780
900
  /**
781
901
  * 判断是否是文件
782
902
  * @param path 文件路径
@@ -785,7 +905,7 @@ class VVVFS {
785
905
  try {
786
906
  const targetPath = joinPath(path);
787
907
  const { name, parent } = parsePath(targetPath);
788
- return (await this.db.files.where({ name, path: parent, type: "file" }).count()) > 0;
908
+ return (await this.#db.files.where({ name, path: parent, type: "file" }).count()) > 0;
789
909
  } catch (error) {
790
910
  console.error("判断文件类型失败", error);
791
911
  if (this.options.throwError) {
@@ -802,7 +922,7 @@ class VVVFS {
802
922
  try {
803
923
  const targetPath = joinPath(path);
804
924
  const { name, parent } = parsePath(targetPath);
805
- return (await this.db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
925
+ return (await this.#db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
806
926
  } catch (error) {
807
927
  console.error("判断文件类型失败", error);
808
928
  if (this.options.throwError) {
@@ -818,8 +938,8 @@ class VVVFS {
818
938
  async list(path: string) {
819
939
  try {
820
940
  const targetPath = joinPath(path);
821
- if (this.watchers[targetPath]) {
822
- for (const handler of this.watchers[targetPath]) {
941
+ if (this.#watchers[targetPath]) {
942
+ for (const handler of this.#watchers[targetPath]) {
823
943
  if (await handler("list")) {
824
944
  if (this.options.throwError) {
825
945
  throw new VVVFSError("List", "列出目录下的文件失败:监听器取消了操作");
@@ -828,6 +948,13 @@ class VVVFS {
828
948
  }
829
949
  }
830
950
  }
951
+ if (this.#lockedFiles[targetPath]) {
952
+ console.warn("文件已被锁定");
953
+ if (this.options.throwError) {
954
+ throw new VVVFSError("List", "文件已被锁定");
955
+ }
956
+ return [];
957
+ }
831
958
  if (!(await this.exists(targetPath))) {
832
959
  console.warn("路径不存在");
833
960
  if (this.options.throwError) {
@@ -842,7 +969,7 @@ class VVVFS {
842
969
  }
843
970
  return [];
844
971
  }
845
- return (await this.db.files.where({ path: targetPath }).toArray())
972
+ return (await this.#db.files.where({ path: targetPath }).toArray())
846
973
  .map((file) => file.name)
847
974
  .filter((item) => item != "");
848
975
  } catch (error) {
@@ -862,8 +989,8 @@ class VVVFS {
862
989
  try {
863
990
  const sourcePath = joinPath(path);
864
991
  const { name, parent } = parsePath(sourcePath);
865
- if (this.watchers[sourcePath]) {
866
- for (const handler of this.watchers[sourcePath]) {
992
+ if (this.#watchers[sourcePath]) {
993
+ for (const handler of this.#watchers[sourcePath]) {
867
994
  if (await handler("rename")) {
868
995
  if (this.options.throwError) {
869
996
  throw new VVVFSError("Rename", "重命名文件失败:监听器取消了操作");
@@ -872,6 +999,13 @@ class VVVFS {
872
999
  }
873
1000
  }
874
1001
  }
1002
+ if (this.#lockedFiles[sourcePath]) {
1003
+ console.warn("文件已被锁定");
1004
+ if (this.options.throwError) {
1005
+ throw new VVVFSError("Rename", "文件已被锁定");
1006
+ }
1007
+ return false;
1008
+ }
875
1009
  if (!(await this.exists(sourcePath))) {
876
1010
  console.warn("文件不存在");
877
1011
  if (this.options.throwError) {
@@ -890,7 +1024,7 @@ class VVVFS {
890
1024
  }
891
1025
  return false;
892
1026
  }
893
- const fileRecord = await this.db.files.where({ path: parent, name }).first();
1027
+ const fileRecord = await this.#db.files.where({ path: parent, name }).first();
894
1028
  if (!fileRecord) {
895
1029
  console.warn("文件记录未找到");
896
1030
  if (this.options.throwError) {
@@ -899,7 +1033,7 @@ class VVVFS {
899
1033
  return false;
900
1034
  }
901
1035
  if (fileRecord.type === "dir") {
902
- const descendants = await this.db.files
1036
+ const descendants = await this.#db.files
903
1037
  .filter(
904
1038
  (file) =>
905
1039
  file.path === sourcePath || file.path.startsWith(sourcePath + "/"),
@@ -908,10 +1042,10 @@ class VVVFS {
908
1042
  for (const descendant of descendants) {
909
1043
  const relativePath = descendant.path.slice(sourcePath.length);
910
1044
  const updatedPath = joinPath(newPath + relativePath);
911
- await this.db.files.update(descendant.id!, { path: updatedPath });
1045
+ await this.#db.files.update(descendant.id!, { path: updatedPath });
912
1046
  }
913
1047
  }
914
- await this.db.files.update(fileRecord.id!, {
1048
+ await this.#db.files.update(fileRecord.id!, {
915
1049
  name: newName,
916
1050
  path: parent,
917
1051
  });
@@ -931,8 +1065,8 @@ class VVVFS {
931
1065
  async delete(path: string) {
932
1066
  try {
933
1067
  const targetPath = joinPath(path);
934
- if (this.watchers[targetPath]) {
935
- for (const handler of this.watchers[targetPath]) {
1068
+ if (this.#watchers[targetPath]) {
1069
+ for (const handler of this.#watchers[targetPath]) {
936
1070
  if (await handler("delete")) {
937
1071
  if (this.options.throwError) {
938
1072
  throw new VVVFSError("Delete", "删除文件失败:监听器取消了操作");
@@ -941,6 +1075,13 @@ class VVVFS {
941
1075
  }
942
1076
  }
943
1077
  }
1078
+ if (this.#lockedFiles[targetPath]) {
1079
+ console.warn("文件已被锁定");
1080
+ if (this.options.throwError) {
1081
+ throw new VVVFSError("Delete", "文件已被锁定");
1082
+ }
1083
+ return false;
1084
+ }
944
1085
  if (!(await this.exists(targetPath))) {
945
1086
  console.warn("文件不存在");
946
1087
  if (this.options.throwError) {
@@ -954,19 +1095,19 @@ class VVVFS {
954
1095
  for (const file of files) {
955
1096
  await this.delete(joinPath(targetPath, file));
956
1097
  }
957
- const dirRecord = await this.db.files
1098
+ const dirRecord = await this.#db.files
958
1099
  .where({ name, path: parent, type: "dir" })
959
1100
  .first();
960
1101
  if (dirRecord) {
961
- await this.db.files.delete(dirRecord.id!);
1102
+ await this.#db.files.delete(dirRecord.id!);
962
1103
  }
963
1104
  return true;
964
1105
  } else {
965
- const fileRecord = await this.db.files
1106
+ const fileRecord = await this.#db.files
966
1107
  .where({ name, path: parent, type: "file" })
967
1108
  .first();
968
1109
  if (fileRecord) {
969
- await this.db.files.delete(fileRecord.id!);
1110
+ await this.#db.files.delete(fileRecord.id!);
970
1111
  }
971
1112
  return true;
972
1113
  }
@@ -987,8 +1128,8 @@ class VVVFS {
987
1128
  try {
988
1129
  const sourcePath = joinPath(path);
989
1130
  const destinationPath = joinPath(newPath);
990
- if (this.watchers[sourcePath]) {
991
- for (const handler of this.watchers[sourcePath]) {
1131
+ if (this.#watchers[sourcePath]) {
1132
+ for (const handler of this.#watchers[sourcePath]) {
992
1133
  if (await handler("move")) {
993
1134
  if (this.options.throwError) {
994
1135
  throw new VVVFSError("Move", "移动文件失败:监听器取消了操作");
@@ -997,6 +1138,13 @@ class VVVFS {
997
1138
  }
998
1139
  }
999
1140
  }
1141
+ if (this.#lockedFiles[sourcePath]) {
1142
+ console.warn("文件已被锁定");
1143
+ if (this.options.throwError) {
1144
+ throw new VVVFSError("Move", "文件已被锁定");
1145
+ }
1146
+ return false;
1147
+ }
1000
1148
  if (sourcePath === destinationPath) {
1001
1149
  return true;
1002
1150
  }
@@ -1029,20 +1177,20 @@ class VVVFS {
1029
1177
  for (const child of children) {
1030
1178
  await this.move(joinPath(sourcePath, child), joinPath(destinationPath, child));
1031
1179
  }
1032
- const dirRecord = await this.db.files
1180
+ const dirRecord = await this.#db.files
1033
1181
  .where({ name, path: parent, type: "dir" })
1034
1182
  .first();
1035
1183
  if (dirRecord) {
1036
- await this.db.files.delete(dirRecord.id!);
1184
+ await this.#db.files.delete(dirRecord.id!);
1037
1185
  }
1038
1186
  return true;
1039
1187
  } else {
1040
1188
  await this.createDir(newParent);
1041
- const fileRecord = await this.db.files
1189
+ const fileRecord = await this.#db.files
1042
1190
  .where({ name, path: parent, type: "file" })
1043
1191
  .first();
1044
1192
  if (fileRecord) {
1045
- await this.db.files.update(fileRecord.id!, { name: newName, path: newParent });
1193
+ await this.#db.files.update(fileRecord.id!, { name: newName, path: newParent });
1046
1194
  return true;
1047
1195
  }
1048
1196
  return false;
@@ -1064,8 +1212,8 @@ class VVVFS {
1064
1212
  try {
1065
1213
  const sourcePath = joinPath(path);
1066
1214
  const destinationPath = joinPath(newPath);
1067
- if (this.watchers[sourcePath]) {
1068
- for (const handler of this.watchers[sourcePath]) {
1215
+ if (this.#watchers[sourcePath]) {
1216
+ for (const handler of this.#watchers[sourcePath]) {
1069
1217
  if (await handler("copy")) {
1070
1218
  if (this.options.throwError) {
1071
1219
  throw new VVVFSError("Copy", "复制文件失败:监听器取消了操作");
@@ -1074,6 +1222,13 @@ class VVVFS {
1074
1222
  }
1075
1223
  }
1076
1224
  }
1225
+ if (this.#lockedFiles[sourcePath]) {
1226
+ console.warn("文件已被锁定");
1227
+ if (this.options.throwError) {
1228
+ throw new VVVFSError("Copy", "文件已被锁定");
1229
+ }
1230
+ return false;
1231
+ }
1077
1232
  if (await this.exists(destinationPath)) {
1078
1233
  console.warn("目标文件已存在");
1079
1234
  if (this.options.throwError) {
@@ -1105,11 +1260,11 @@ class VVVFS {
1105
1260
  }
1106
1261
  return true;
1107
1262
  } else {
1108
- const fileRecord = await this.db.files
1263
+ const fileRecord = await this.#db.files
1109
1264
  .where({ name, path: parent, type: "file" })
1110
1265
  .first();
1111
1266
  if (fileRecord) {
1112
- await this.db.files.add({
1267
+ await this.#db.files.add({
1113
1268
  name: newName,
1114
1269
  path: newParent,
1115
1270
  type: fileRecord.type,
@@ -1177,14 +1332,69 @@ class VVVFS {
1177
1332
  return null;
1178
1333
  }
1179
1334
  }
1180
- async watch(path: string, handler: (type: string) => Promise<boolean>) {
1335
+ /**
1336
+ * 监听文件
1337
+ * @param path 文件路径
1338
+ * @param handler 监听器
1339
+ */
1340
+ watch(path: string, handler: (type: string) => Promise<boolean>) {
1341
+ path = joinPath(path);
1342
+ if (!this.#watchers[path]) {
1343
+ this.#watchers[path] = [];
1344
+ }
1345
+ this.#watchers[path].push(handler);
1346
+ }
1347
+ /**
1348
+ * 锁定文件
1349
+ * @param path 文件路径
1350
+ */
1351
+ async lock(path: string) {
1352
+ path = joinPath(path);
1353
+ if (!(await this.exists(path))) {
1354
+ console.warn("文件不存在");
1355
+ if (this.options.throwError) {
1356
+ throw new VVVFSError("Lock", "文件不存在");
1357
+ }
1358
+ return false;
1359
+ }
1360
+ if (this.#lockedFiles[path]) {
1361
+ console.warn("文件已被锁定");
1362
+ if (this.options.throwError) {
1363
+ throw new VVVFSError("Lock", "文件已被锁定");
1364
+ }
1365
+ return false;
1366
+ }
1367
+ this.#lockedFiles[path] = true;
1368
+ return true;
1369
+ }
1370
+ /**
1371
+ * 解锁文件
1372
+ * @param path 文件路径
1373
+ */
1374
+ async unlock(path: string) {
1181
1375
  path = joinPath(path);
1182
- if (!this.watchers[path]) {
1183
- this.watchers[path] = [];
1376
+ if (!(await this.exists(path))) {
1377
+ console.warn("文件不存在");
1378
+ if (this.options.throwError) {
1379
+ throw new VVVFSError("Unlock", "文件不存在");
1380
+ }
1381
+ return false;
1184
1382
  }
1185
- this.watchers[path].push(handler);
1383
+ if (!this.#lockedFiles[path]) {
1384
+ console.warn("文件未被锁定");
1385
+ if (this.options.throwError) {
1386
+ throw new VVVFSError("Unlock", "文件未被锁定");
1387
+ }
1388
+ return false;
1389
+ }
1390
+ delete this.#lockedFiles[path];
1391
+ return true;
1186
1392
  }
1187
1393
  }
1394
+ /**
1395
+ * 解析路径
1396
+ * @param path 路径
1397
+ */
1188
1398
  function parsePath(path: string) {
1189
1399
  path = joinPath(path);
1190
1400
  const oldParts = path.split("/");
@@ -1198,6 +1408,11 @@ function parsePath(path: string) {
1198
1408
  if (joinPath(parent, name).length > 4096) throw new VVVFSError("ParsePath", "文件路径过长");
1199
1409
  return { name, parent };
1200
1410
  }
1411
+
1412
+ /**
1413
+ * 合并路径
1414
+ * @param paths 路径
1415
+ */
1201
1416
  function joinPath(...paths: string[]) {
1202
1417
  const segments = paths.map((p) => String(p)).filter((p) => p.length > 0);
1203
1418
  if (segments.length === 0) return ".";
@@ -1229,4 +1444,10 @@ Object.defineProperty(VVVFS, "version", {
1229
1444
  enumerable: true,
1230
1445
  configurable: false,
1231
1446
  });
1447
+ Object.defineProperty(VVVFS, "author", {
1448
+ value: "IFTC",
1449
+ writable: false,
1450
+ enumerable: true,
1451
+ configurable: false,
1452
+ });
1232
1453
  (globalThis as any).VVVFS = VVVFS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vvvfs",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "一个使用IndexDB实现的虚拟文件系统",
5
5
  "keywords": [
6
6
  "Virtual",