vvvfs 0.1.0 → 0.1.2

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 +10 -0
  2. package/index.html +3 -1
  3. package/index.ts +192 -53
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -48,7 +48,9 @@ const vvvfs = new VVVFS("vvvfs", {
48
48
  });
49
49
  await vvvfs.createDir("/home/user/Desktop"); // 创建目录,返回true和false
50
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
51
+ await vvvfs.appendText("/home/user/Desktop/test.txt", "Hello World!"); // 追加文本文件,返回true和false
51
52
  console.log(await vvvfs.readText("/home/user/Desktop/test.txt")); // 读取文本文件,读取文件还包括read(path: string): Blob | null和readJson(path: string): Record<string, any> | null方法
53
+ console.log(await vvvfs.readChunkText("/home/user/Desktop/test.txt", 0, 5)); // 读取文件块,返回string | null
52
54
  await vvvfs.delete("/home/user/Desktop/test.txt"); // 删除文件,返回true和false
53
55
  if (await vvvfs.exists("/home/user/Desktop")) {
54
56
  // 判断文件或目录是否存在
@@ -82,6 +84,14 @@ const vvvfs = new VVVFS("vvvfs", {
82
84
 
83
85
  ## 更新日志
84
86
 
87
+ ### 0.1.2
88
+
89
+ - 新增 `readChunk` 和 `readChunkText` 方法,用于读取文件块
90
+
91
+ ### 0.1.1
92
+
93
+ - 新增 `append` 和 `appendText` 方法,用于追加内容
94
+
85
95
  ### 0.1.0
86
96
 
87
97
  - 修复 `init` 重复初始化时重复写入已存在文件和目录
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">
@@ -23,9 +23,11 @@
23
23
  });
24
24
  console.log("创建文件", await vvvfs.createFile("/test.txt"));
25
25
  console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
26
+ console.log("追加文件", await vvvfs.appendText("/test.txt", " - Appended"));
26
27
  console.log("文件是否存在", await vvvfs.exists("/test.txt"));
27
28
  console.log("文件是否存在", await vvvfs.exists("/test2.txt"));
28
29
  console.log("读取文件", await vvvfs.readText("/test.txt"));
30
+ console.log("读取文件块", await vvvfs.readTextChunk("/test.txt", 0, 5));
29
31
  console.log("复制文件", await vvvfs.copy("/test.txt", "/test2.txt"));
30
32
  console.log("搜索文件", await vvvfs.search("/", "t"));
31
33
  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 文件对象
@@ -150,6 +166,20 @@ class VVVFSFile {
150
166
  async writeJSON(json: Record<string, any>, format: boolean = true) {
151
167
  return await this._vvvfs.writeJson(this._path, json, format);
152
168
  }
169
+ /**
170
+ * 追加文件内容
171
+ * @param file 文件对象
172
+ */
173
+ async append(file: Blob) {
174
+ return await this._vvvfs.append(this._path, file);
175
+ }
176
+ /**
177
+ * 追加文件文本内容
178
+ * @param text 文件文本内容
179
+ */
180
+ async appendText(text: string) {
181
+ return await this._vvvfs.appendText(this._path, text);
182
+ }
153
183
  /**
154
184
  * 创建文件
155
185
  */
@@ -224,7 +254,7 @@ class VVVFSFile {
224
254
  const version = packageJson.version;
225
255
  class VVVFS {
226
256
  static defaultDBName = "vvvfs";
227
- private db: VVVFSDatabase;
257
+ #db: VVVFSDatabase;
228
258
  options: VVVFSOptions;
229
259
  /**
230
260
  * 虚拟文件系统版本
@@ -237,7 +267,7 @@ class VVVFS {
237
267
  /**
238
268
  * 虚拟文件系统监听器
239
269
  */
240
- watchers: Record<string, Array<(type: string) => Promise<boolean>>> = {};
270
+ #watchers: Record<string, Array<(type: string) => Promise<boolean>>> = {};
241
271
  /**
242
272
  * 创建虚拟文件系统
243
273
  * @param name 虚拟文件系统名称
@@ -251,8 +281,8 @@ class VVVFS {
251
281
  ) {
252
282
  this.options = options;
253
283
  try {
254
- this.db = new Dexie(name || VVVFS.defaultDBName) as VVVFSDatabase;
255
- this.db.version(1).stores({
284
+ this.#db = new Dexie(name || VVVFS.defaultDBName) as VVVFSDatabase;
285
+ this.#db.version(1).stores({
256
286
  files: "++id, name, path, type, file, [name+path+type]",
257
287
  });
258
288
  } catch (error) {
@@ -390,7 +420,7 @@ class VVVFS {
390
420
  try {
391
421
  for (const file of linuxInitFiles) {
392
422
  if (await this.exists(file.path)) continue;
393
- await this.db.files.put(file);
423
+ await this.#db.files.put(file);
394
424
  }
395
425
  } catch (error) {
396
426
  console.error("初始化文件失败", error);
@@ -402,9 +432,9 @@ class VVVFS {
402
432
  */
403
433
  async reset() {
404
434
  try {
405
- await this.db.delete();
406
- this.db = new Dexie(this.db.name) as VVVFSDatabase;
407
- this.db.version(1).stores({
435
+ await this.#db.delete();
436
+ this.#db = new Dexie(this.#db.name) as VVVFSDatabase;
437
+ this.#db.version(1).stores({
408
438
  files: "++id, name, path, type, file, [name+path+type]",
409
439
  });
410
440
  } catch (error) {
@@ -419,8 +449,8 @@ class VVVFS {
419
449
  async createFile(path: string) {
420
450
  const targetPath = joinPath(path);
421
451
  try {
422
- if (this.watchers[targetPath]) {
423
- for (const handler of this.watchers[targetPath]) {
452
+ if (this.#watchers[targetPath]) {
453
+ for (const handler of this.#watchers[targetPath]) {
424
454
  if (await handler("create")) {
425
455
  if (this.options.throwError) {
426
456
  throw new VVVFSError("CreateFile", "创建文件失败:监听器取消了操作");
@@ -437,7 +467,7 @@ class VVVFS {
437
467
  if (!(await this.exists(parent))) {
438
468
  await this.createDir(parent);
439
469
  }
440
- await this.db.files.add({
470
+ await this.#db.files.add({
441
471
  name: name,
442
472
  path: parent,
443
473
  type: "file",
@@ -461,8 +491,8 @@ class VVVFS {
461
491
  async createDir(path: string) {
462
492
  const targetPath = joinPath(path);
463
493
  try {
464
- if (this.watchers[targetPath]) {
465
- for (const handler of this.watchers[targetPath]) {
494
+ if (this.#watchers[targetPath]) {
495
+ for (const handler of this.#watchers[targetPath]) {
466
496
  if (await handler("create")) {
467
497
  if (this.options.throwError) {
468
498
  throw new VVVFSError("CreateDir", "创建目录失败:监听器取消了操作");
@@ -478,7 +508,7 @@ class VVVFS {
478
508
  const { name, parent } = parsePath(targetPath);
479
509
  if (!(await this.exists(parent))) {
480
510
  if (parent == "/") {
481
- await this.db.files.add({
511
+ await this.#db.files.add({
482
512
  name: "",
483
513
  path: "/",
484
514
  type: "dir",
@@ -489,7 +519,7 @@ class VVVFS {
489
519
  await this.createDir(parent);
490
520
  }
491
521
  }
492
- await this.db.files.add({
522
+ await this.#db.files.add({
493
523
  name: name,
494
524
  path: parent,
495
525
  type: "dir",
@@ -513,7 +543,7 @@ class VVVFS {
513
543
  const targetPath = joinPath(path);
514
544
  const { name, parent } = parsePath(targetPath);
515
545
  return (
516
- (await this.db.files
546
+ (await this.#db.files
517
547
  .where({
518
548
  name: name,
519
549
  path: parent,
@@ -535,8 +565,8 @@ class VVVFS {
535
565
  async write(path: string, content: Blob) {
536
566
  try {
537
567
  const targetPath = joinPath(path);
538
- if (this.watchers[targetPath]) {
539
- for (const handler of this.watchers[targetPath]) {
568
+ if (this.#watchers[targetPath]) {
569
+ for (const handler of this.#watchers[targetPath]) {
540
570
  if (await handler("write")) {
541
571
  if (this.options.throwError) {
542
572
  throw new VVVFSError("Write", "写入文件失败:监听器取消了操作");
@@ -566,9 +596,9 @@ class VVVFS {
566
596
  const file = new File([content], name, {
567
597
  type: mime.getType(targetPath) || "application/octet-stream",
568
598
  });
569
- const fileRecord = await this.db.files.where({ name, path: parent }).first();
599
+ const fileRecord = await this.#db.files.where({ name, path: parent }).first();
570
600
  if (fileRecord) {
571
- await this.db.files.put({
601
+ await this.#db.files.put({
572
602
  ...fileRecord,
573
603
  file: file,
574
604
  });
@@ -625,6 +655,58 @@ class VVVFS {
625
655
  return false;
626
656
  }
627
657
  }
658
+ /**
659
+ * 追加内容
660
+ * @param path 文件路径
661
+ * @param content 追加内容
662
+ */
663
+ async append(path: string, content: Blob) {
664
+ try {
665
+ const targetPath = joinPath(path);
666
+ if (this.#watchers[targetPath]) {
667
+ for (const handler of this.#watchers[targetPath]) {
668
+ if (await handler("append")) {
669
+ if (this.options.throwError) {
670
+ throw new VVVFSError("Append", "追加文件失败:监听器取消了操作");
671
+ }
672
+ return false;
673
+ }
674
+ }
675
+ }
676
+ const existingFile = await this.read(targetPath);
677
+ if (existingFile) {
678
+ const blob = new Blob([existingFile, content], {
679
+ type: "application/octet-stream",
680
+ });
681
+ return await this.write(targetPath, blob);
682
+ } else {
683
+ return await this.write(targetPath, content);
684
+ }
685
+ } catch (error) {
686
+ console.error("追加文件失败", error);
687
+ if (this.options.throwError) {
688
+ throw new VVVFSError("Append", "追加文件失败" + error);
689
+ }
690
+ return false;
691
+ }
692
+ }
693
+ /**
694
+ * 追加文本内容
695
+ * @param path 文件路径
696
+ * @param content 追加内容
697
+ */
698
+ async appendText(path: string, content: string) {
699
+ try {
700
+ const blob = new Blob([content], { type: "text/plain" });
701
+ return await this.append(path, blob);
702
+ } catch (error) {
703
+ console.error("追加文件失败", error);
704
+ if (this.options.throwError) {
705
+ throw new VVVFSError("Append", "追加文件失败" + error);
706
+ }
707
+ return false;
708
+ }
709
+ }
628
710
  /**
629
711
  * 读取文件内容
630
712
  * @param path 文件路径
@@ -632,8 +714,8 @@ class VVVFS {
632
714
  async read(path: string) {
633
715
  try {
634
716
  const targetPath = joinPath(path);
635
- if (this.watchers[targetPath]) {
636
- for (const handler of this.watchers[targetPath]) {
717
+ if (this.#watchers[targetPath]) {
718
+ for (const handler of this.#watchers[targetPath]) {
637
719
  if (await handler("read")) {
638
720
  if (this.options.throwError) {
639
721
  throw new VVVFSError("Read", "读取文件失败:监听器取消了操作");
@@ -650,7 +732,7 @@ class VVVFS {
650
732
  return null;
651
733
  }
652
734
  const { name, parent } = parsePath(targetPath);
653
- return (await this.db.files.where({ name, path: parent }).first())?.file;
735
+ return (await this.#db.files.where({ name, path: parent }).first())?.file;
654
736
  } catch (error) {
655
737
  console.error("读取文件失败", error);
656
738
  if (this.options.throwError) {
@@ -711,6 +793,63 @@ class VVVFS {
711
793
  return null;
712
794
  }
713
795
  }
796
+ /**
797
+ * 读取文件内容
798
+ * @param path 文件路径
799
+ * @param start 开始位置
800
+ * @param end 结束位置
801
+ */
802
+ async readChunk(path: string, start: number, end: number) {
803
+ try {
804
+ const targetPath = joinPath(path);
805
+ if (this.#watchers[targetPath]) {
806
+ for (const handler of this.#watchers[targetPath]) {
807
+ if (await handler("read")) {
808
+ if (this.options.throwError) {
809
+ throw new VVVFSError("Read", "读取文件失败:监听器取消了操作");
810
+ }
811
+ return null;
812
+ }
813
+ }
814
+ }
815
+ if (!(await this.exists(targetPath))) {
816
+ console.warn("文件不存在");
817
+ if (this.options.throwError) {
818
+ throw new VVVFSError("Read", "文件不存在");
819
+ }
820
+ return null;
821
+ }
822
+ const file = await this.read(targetPath);
823
+ return file?.slice(start, end) || null;
824
+ } catch (error) {
825
+ console.error("读取文件失败", error);
826
+ if (this.options.throwError) {
827
+ throw new VVVFSError("Read", "读取文件失败" + error);
828
+ }
829
+ return null;
830
+ }
831
+ }
832
+ /**
833
+ * 读取文件内容
834
+ * @param path 文件路径
835
+ * @param start 读取开始位置
836
+ * @param end 读取结束位置
837
+ */
838
+ async readTextChunk(path: string, start: number, end: number) {
839
+ try {
840
+ const chunk = await this.readChunk(path, start, end);
841
+ if (chunk) {
842
+ return await chunk.text();
843
+ } else {
844
+ return null;
845
+ }
846
+ } catch (error) {
847
+ console.error("读取文件失败", error);
848
+ if (this.options.throwError) {
849
+ throw new VVVFSError("Read", "读取文件失败" + error);
850
+ }
851
+ }
852
+ }
714
853
  /**
715
854
  * 判断是否是文件
716
855
  * @param path 文件路径
@@ -719,7 +858,7 @@ class VVVFS {
719
858
  try {
720
859
  const targetPath = joinPath(path);
721
860
  const { name, parent } = parsePath(targetPath);
722
- return (await this.db.files.where({ name, path: parent, type: "file" }).count()) > 0;
861
+ return (await this.#db.files.where({ name, path: parent, type: "file" }).count()) > 0;
723
862
  } catch (error) {
724
863
  console.error("判断文件类型失败", error);
725
864
  if (this.options.throwError) {
@@ -736,7 +875,7 @@ class VVVFS {
736
875
  try {
737
876
  const targetPath = joinPath(path);
738
877
  const { name, parent } = parsePath(targetPath);
739
- return (await this.db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
878
+ return (await this.#db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
740
879
  } catch (error) {
741
880
  console.error("判断文件类型失败", error);
742
881
  if (this.options.throwError) {
@@ -752,8 +891,8 @@ class VVVFS {
752
891
  async list(path: string) {
753
892
  try {
754
893
  const targetPath = joinPath(path);
755
- if (this.watchers[targetPath]) {
756
- for (const handler of this.watchers[targetPath]) {
894
+ if (this.#watchers[targetPath]) {
895
+ for (const handler of this.#watchers[targetPath]) {
757
896
  if (await handler("list")) {
758
897
  if (this.options.throwError) {
759
898
  throw new VVVFSError("List", "列出目录下的文件失败:监听器取消了操作");
@@ -776,7 +915,7 @@ class VVVFS {
776
915
  }
777
916
  return [];
778
917
  }
779
- return (await this.db.files.where({ path: targetPath }).toArray())
918
+ return (await this.#db.files.where({ path: targetPath }).toArray())
780
919
  .map((file) => file.name)
781
920
  .filter((item) => item != "");
782
921
  } catch (error) {
@@ -796,8 +935,8 @@ class VVVFS {
796
935
  try {
797
936
  const sourcePath = joinPath(path);
798
937
  const { name, parent } = parsePath(sourcePath);
799
- if (this.watchers[sourcePath]) {
800
- for (const handler of this.watchers[sourcePath]) {
938
+ if (this.#watchers[sourcePath]) {
939
+ for (const handler of this.#watchers[sourcePath]) {
801
940
  if (await handler("rename")) {
802
941
  if (this.options.throwError) {
803
942
  throw new VVVFSError("Rename", "重命名文件失败:监听器取消了操作");
@@ -824,7 +963,7 @@ class VVVFS {
824
963
  }
825
964
  return false;
826
965
  }
827
- const fileRecord = await this.db.files.where({ path: parent, name }).first();
966
+ const fileRecord = await this.#db.files.where({ path: parent, name }).first();
828
967
  if (!fileRecord) {
829
968
  console.warn("文件记录未找到");
830
969
  if (this.options.throwError) {
@@ -833,7 +972,7 @@ class VVVFS {
833
972
  return false;
834
973
  }
835
974
  if (fileRecord.type === "dir") {
836
- const descendants = await this.db.files
975
+ const descendants = await this.#db.files
837
976
  .filter(
838
977
  (file) =>
839
978
  file.path === sourcePath || file.path.startsWith(sourcePath + "/"),
@@ -842,10 +981,10 @@ class VVVFS {
842
981
  for (const descendant of descendants) {
843
982
  const relativePath = descendant.path.slice(sourcePath.length);
844
983
  const updatedPath = joinPath(newPath + relativePath);
845
- await this.db.files.update(descendant.id!, { path: updatedPath });
984
+ await this.#db.files.update(descendant.id!, { path: updatedPath });
846
985
  }
847
986
  }
848
- await this.db.files.update(fileRecord.id!, {
987
+ await this.#db.files.update(fileRecord.id!, {
849
988
  name: newName,
850
989
  path: parent,
851
990
  });
@@ -865,8 +1004,8 @@ class VVVFS {
865
1004
  async delete(path: string) {
866
1005
  try {
867
1006
  const targetPath = joinPath(path);
868
- if (this.watchers[targetPath]) {
869
- for (const handler of this.watchers[targetPath]) {
1007
+ if (this.#watchers[targetPath]) {
1008
+ for (const handler of this.#watchers[targetPath]) {
870
1009
  if (await handler("delete")) {
871
1010
  if (this.options.throwError) {
872
1011
  throw new VVVFSError("Delete", "删除文件失败:监听器取消了操作");
@@ -888,19 +1027,19 @@ class VVVFS {
888
1027
  for (const file of files) {
889
1028
  await this.delete(joinPath(targetPath, file));
890
1029
  }
891
- const dirRecord = await this.db.files
1030
+ const dirRecord = await this.#db.files
892
1031
  .where({ name, path: parent, type: "dir" })
893
1032
  .first();
894
1033
  if (dirRecord) {
895
- await this.db.files.delete(dirRecord.id!);
1034
+ await this.#db.files.delete(dirRecord.id!);
896
1035
  }
897
1036
  return true;
898
1037
  } else {
899
- const fileRecord = await this.db.files
1038
+ const fileRecord = await this.#db.files
900
1039
  .where({ name, path: parent, type: "file" })
901
1040
  .first();
902
1041
  if (fileRecord) {
903
- await this.db.files.delete(fileRecord.id!);
1042
+ await this.#db.files.delete(fileRecord.id!);
904
1043
  }
905
1044
  return true;
906
1045
  }
@@ -921,8 +1060,8 @@ class VVVFS {
921
1060
  try {
922
1061
  const sourcePath = joinPath(path);
923
1062
  const destinationPath = joinPath(newPath);
924
- if (this.watchers[sourcePath]) {
925
- for (const handler of this.watchers[sourcePath]) {
1063
+ if (this.#watchers[sourcePath]) {
1064
+ for (const handler of this.#watchers[sourcePath]) {
926
1065
  if (await handler("move")) {
927
1066
  if (this.options.throwError) {
928
1067
  throw new VVVFSError("Move", "移动文件失败:监听器取消了操作");
@@ -963,20 +1102,20 @@ class VVVFS {
963
1102
  for (const child of children) {
964
1103
  await this.move(joinPath(sourcePath, child), joinPath(destinationPath, child));
965
1104
  }
966
- const dirRecord = await this.db.files
1105
+ const dirRecord = await this.#db.files
967
1106
  .where({ name, path: parent, type: "dir" })
968
1107
  .first();
969
1108
  if (dirRecord) {
970
- await this.db.files.delete(dirRecord.id!);
1109
+ await this.#db.files.delete(dirRecord.id!);
971
1110
  }
972
1111
  return true;
973
1112
  } else {
974
1113
  await this.createDir(newParent);
975
- const fileRecord = await this.db.files
1114
+ const fileRecord = await this.#db.files
976
1115
  .where({ name, path: parent, type: "file" })
977
1116
  .first();
978
1117
  if (fileRecord) {
979
- await this.db.files.update(fileRecord.id!, { name: newName, path: newParent });
1118
+ await this.#db.files.update(fileRecord.id!, { name: newName, path: newParent });
980
1119
  return true;
981
1120
  }
982
1121
  return false;
@@ -998,8 +1137,8 @@ class VVVFS {
998
1137
  try {
999
1138
  const sourcePath = joinPath(path);
1000
1139
  const destinationPath = joinPath(newPath);
1001
- if (this.watchers[sourcePath]) {
1002
- for (const handler of this.watchers[sourcePath]) {
1140
+ if (this.#watchers[sourcePath]) {
1141
+ for (const handler of this.#watchers[sourcePath]) {
1003
1142
  if (await handler("copy")) {
1004
1143
  if (this.options.throwError) {
1005
1144
  throw new VVVFSError("Copy", "复制文件失败:监听器取消了操作");
@@ -1039,11 +1178,11 @@ class VVVFS {
1039
1178
  }
1040
1179
  return true;
1041
1180
  } else {
1042
- const fileRecord = await this.db.files
1181
+ const fileRecord = await this.#db.files
1043
1182
  .where({ name, path: parent, type: "file" })
1044
1183
  .first();
1045
1184
  if (fileRecord) {
1046
- await this.db.files.add({
1185
+ await this.#db.files.add({
1047
1186
  name: newName,
1048
1187
  path: newParent,
1049
1188
  type: fileRecord.type,
@@ -1113,10 +1252,10 @@ class VVVFS {
1113
1252
  }
1114
1253
  async watch(path: string, handler: (type: string) => Promise<boolean>) {
1115
1254
  path = joinPath(path);
1116
- if (!this.watchers[path]) {
1117
- this.watchers[path] = [];
1255
+ if (!this.#watchers[path]) {
1256
+ this.#watchers[path] = [];
1118
1257
  }
1119
- this.watchers[path].push(handler);
1258
+ this.#watchers[path].push(handler);
1120
1259
  }
1121
1260
  }
1122
1261
  function parsePath(path: string) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vvvfs",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "一个使用IndexDB实现的虚拟文件系统",
5
5
  "keywords": [
6
6
  "Virtual",