vvvfs 0.1.3 → 0.1.4

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 (3) hide show
  1. package/README.md +5 -0
  2. package/index.ts +248 -684
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -86,7 +86,12 @@ const vvvfs = new VVVFS("vvvfs", {
86
86
 
87
87
  ## 更新日志
88
88
 
89
+ ### 0.1.4
90
+
91
+ - 使用 AI 优化代码
92
+
89
93
  ### 0.1.3
94
+
90
95
  - 新增 `lock` 和 `unlock` 方法,可以锁定文件,防止其他代码访问该文件
91
96
 
92
97
  ### 0.1.2
package/index.ts CHANGED
@@ -163,7 +163,7 @@ class VVVFSFile {
163
163
  * 写入文件JSON内容
164
164
  * @param json 文件JSON内容
165
165
  */
166
- async writeJSON(json: Record<string, any>, format: boolean = true) {
166
+ async writeJSON(json: Record<string, unknown>, format: boolean = true) {
167
167
  return await this._vvvfs.writeJson(this._path, json, format);
168
168
  }
169
169
  /**
@@ -267,6 +267,7 @@ class VVVFSFile {
267
267
  }
268
268
 
269
269
  const version = packageJson.version;
270
+
270
271
  class VVVFS {
271
272
  static defaultDBName = "vvvfs";
272
273
  #db: VVVFSDatabase;
@@ -287,6 +288,65 @@ class VVVFS {
287
288
  * 锁定的文件
288
289
  */
289
290
  #lockedFiles: Record<string, boolean> = {};
291
+
292
+ /**
293
+ * 检查文件访问权限(监听器和锁定状态)
294
+ * @param path 文件路径
295
+ * @param operation 操作类型
296
+ * @returns 是否允许访问
297
+ */
298
+ async #checkAccess(path: string, operation: string): Promise<boolean> {
299
+ if (this.#watchers[path]) {
300
+ for (const handler of this.#watchers[path]) {
301
+ if (await handler(operation)) {
302
+ if (this.options.throwError) {
303
+ throw new VVVFSError(
304
+ operation.charAt(0).toUpperCase() + operation.slice(1),
305
+ `${operation}操作被监听器取消`,
306
+ );
307
+ }
308
+ return false;
309
+ }
310
+ }
311
+ }
312
+ if (this.#lockedFiles[path]) {
313
+ if (this.options.throwError) {
314
+ throw new VVVFSError(
315
+ operation.charAt(0).toUpperCase() + operation.slice(1),
316
+ "文件已被锁定",
317
+ );
318
+ }
319
+ return false;
320
+ }
321
+ return true;
322
+ }
323
+
324
+ /**
325
+ * 错误处理包装器
326
+ * @param operation 操作名称
327
+ * @param fn 操作函数
328
+ * @param fallback 失败时的默认返回值
329
+ */
330
+ async #withErrorHandling<T>(
331
+ operation: string,
332
+ fn: () => Promise<T>,
333
+ fallback: T,
334
+ ): Promise<T> {
335
+ try {
336
+ return await fn();
337
+ } catch (error) {
338
+ if (error instanceof VVVFSError) throw error;
339
+ console.error(`${operation}失败`, error);
340
+ if (this.options.throwError) {
341
+ throw new VVVFSError(
342
+ operation.charAt(0).toUpperCase() + operation.slice(1),
343
+ `${operation}失败${error}`,
344
+ );
345
+ }
346
+ return fallback;
347
+ }
348
+ }
349
+
290
350
  /**
291
351
  * 创建虚拟文件系统
292
352
  * @param name 虚拟文件系统名称
@@ -314,128 +374,23 @@ class VVVFS {
314
374
  * @description 将Linux的系统初始文件初始化到数据库中
315
375
  */
316
376
  async init(user?: string) {
317
- const linuxInitFiles = [
318
- {
319
- name: "root",
320
- path: "/",
321
- type: "dir",
322
- file: new File([], "root"),
323
- },
324
- {
325
- name: "boot",
326
- path: "/",
327
- type: "dir",
328
- file: new File([], "boot"),
329
- },
330
- {
331
- name: "bin",
332
- path: "/",
333
- type: "dir",
334
- file: new File([], "bin"),
335
- },
336
- {
337
- name: "dev",
338
- path: "/",
339
- type: "dir",
340
- file: new File([], "dev"),
341
- },
342
- {
343
- name: "etc",
344
- path: "/",
345
- type: "dir",
346
- file: new File([], "etc"),
347
- },
348
- {
349
- name: "home",
350
- path: "/",
351
- type: "dir",
352
- file: new File([], "home"),
353
- },
354
- {
355
- name: user || "root",
356
- path: "/home",
357
- type: "dir",
358
- file: new File([], "home"),
359
- },
360
- {
361
- name: "lib",
362
- path: "/",
363
- type: "dir",
364
- file: new File([], "lib"),
365
- },
366
- {
367
- name: "lib64",
368
- path: "/",
369
- type: "dir",
370
- file: new File([], "lib64"),
371
- },
372
- {
373
- name: "media",
374
- path: "/",
375
- type: "dir",
376
- file: new File([], "media"),
377
- },
378
- {
379
- name: "mnt",
380
- path: "/",
381
- type: "dir",
382
- file: new File([], "mnt"),
383
- },
384
- {
385
- name: "opt",
386
- path: "/",
387
- type: "dir",
388
- file: new File([], "opt"),
389
- },
390
- {
391
- name: "proc",
392
- path: "/",
393
- type: "dir",
394
- file: new File([], "proc"),
395
- },
396
- {
397
- name: "run",
398
- path: "/",
399
- type: "dir",
400
- file: new File([], "run"),
401
- },
402
- {
403
- name: "sbin",
404
- path: "/",
405
- type: "dir",
406
- file: new File([], "sbin"),
407
- },
408
- {
409
- name: "srv",
410
- path: "/",
411
- type: "dir",
412
- file: new File([], "srv"),
413
- },
414
- {
415
- name: "sys",
416
- path: "/",
417
- type: "dir",
418
- file: new File([], "sys"),
419
- },
420
- {
421
- name: "tmp",
422
- path: "/",
423
- type: "dir",
424
- file: new File([], "tmp"),
425
- },
426
- {
427
- name: "usr",
428
- path: "/",
429
- type: "dir",
430
- file: new File([], "usr"),
431
- },
432
- {
433
- name: "var",
434
- path: "/",
435
- type: "dir",
436
- file: new File([], "var"),
437
- },
377
+ const linuxDirs = [
378
+ "root", "boot", "bin", "dev", "etc", "home",
379
+ "lib", "lib64", "media", "mnt", "opt", "proc",
380
+ "run", "sbin", "srv", "sys", "tmp", "usr", "var",
438
381
  ];
382
+ const linuxInitFiles = linuxDirs.map((name) => ({
383
+ name,
384
+ path: "/",
385
+ type: "dir" as const,
386
+ file: new File([], name),
387
+ }));
388
+ linuxInitFiles.push({
389
+ name: user || "root",
390
+ path: "/home",
391
+ type: "dir" as const,
392
+ file: new File([], "home"),
393
+ });
439
394
  try {
440
395
  for (const file of linuxInitFiles) {
441
396
  if (await this.exists(file.path)) continue;
@@ -466,18 +421,9 @@ class VVVFS {
466
421
  * @param path 文件路径
467
422
  */
468
423
  async createFile(path: string) {
469
- const targetPath = joinPath(path);
470
- try {
471
- if (this.#watchers[targetPath]) {
472
- for (const handler of this.#watchers[targetPath]) {
473
- if (await handler("create")) {
474
- if (this.options.throwError) {
475
- throw new VVVFSError("CreateFile", "创建文件失败:监听器取消了操作");
476
- }
477
- return false;
478
- }
479
- }
480
- }
424
+ return this.#withErrorHandling("createFile", async () => {
425
+ const targetPath = joinPath(path);
426
+ if (!(await this.#checkAccess(targetPath, "create"))) return false;
481
427
  if (await this.exists(targetPath)) {
482
428
  console.warn("文件已存在");
483
429
  return true;
@@ -487,7 +433,7 @@ class VVVFS {
487
433
  await this.createDir(parent);
488
434
  }
489
435
  await this.#db.files.add({
490
- name: name,
436
+ name,
491
437
  path: parent,
492
438
  type: "file",
493
439
  file: new File([], name, {
@@ -495,154 +441,90 @@ class VVVFS {
495
441
  }),
496
442
  });
497
443
  return true;
498
- } catch (error) {
499
- console.error("创建文件失败", error);
500
- if (this.options.throwError) {
501
- throw new VVVFSError("CreateFile", "创建文件失败" + error);
502
- }
503
- return false;
504
- }
444
+ }, false);
505
445
  }
506
446
  /**
507
447
  * 创建目录
508
448
  * @param path 目录路径
509
449
  */
510
450
  async createDir(path: string) {
511
- const targetPath = joinPath(path);
512
- try {
513
- if (this.#watchers[targetPath]) {
514
- for (const handler of this.#watchers[targetPath]) {
515
- if (await handler("create")) {
516
- if (this.options.throwError) {
517
- throw new VVVFSError("CreateDir", "创建目录失败:监听器取消了操作");
518
- }
519
- return false;
520
- }
521
- }
522
- }
451
+ return this.#withErrorHandling("createDir", async () => {
452
+ const targetPath = joinPath(path);
453
+ if (!(await this.#checkAccess(targetPath, "create"))) return false;
523
454
  if (await this.exists(targetPath)) {
524
455
  console.warn("目录已存在");
525
456
  return true;
526
457
  }
527
458
  const { name, parent } = parsePath(targetPath);
528
459
  if (!(await this.exists(parent))) {
529
- if (parent == "/") {
460
+ if (parent === "/") {
530
461
  await this.#db.files.add({
531
462
  name: "",
532
463
  path: "/",
533
464
  type: "dir",
534
465
  file: new File([], ""),
535
466
  });
536
- if (name == "") return true;
467
+ if (name === "") return true;
537
468
  } else {
538
469
  await this.createDir(parent);
539
470
  }
540
471
  }
541
472
  await this.#db.files.add({
542
- name: name,
473
+ name,
543
474
  path: parent,
544
475
  type: "dir",
545
476
  file: new File([], name),
546
477
  });
547
478
  return true;
548
- } catch (error) {
549
- console.error("创建目录失败", error);
550
- if (this.options.throwError) {
551
- throw new VVVFSError("CreateDir", "创建目录失败" + error);
552
- }
553
- return false;
554
- }
479
+ }, false);
555
480
  }
556
481
  /**
557
482
  * 判断文件是否存在
558
483
  * @param path 文件路径
559
484
  */
560
485
  async exists(path: string) {
561
- try {
562
- const targetPath = joinPath(path);
563
- const { name, parent } = parsePath(targetPath);
486
+ return this.#withErrorHandling("exists", async () => {
487
+ const { name, parent } = parsePath(path);
564
488
  return (
565
489
  (await this.#db.files
566
- .where({
567
- name: name,
568
- path: parent,
569
- })
490
+ .where({ name, path: parent })
570
491
  .count()) > 0
571
492
  );
572
- } catch (error) {
573
- console.error("判断文件是否存在失败", error);
574
- if (this.options.throwError) {
575
- throw new VVVFSError("Exists", "判断文件是否存在失败" + error);
576
- }
493
+ }, false);
494
+ }
495
+ /**
496
+ * 内部写入(跳过访问检查,供内部方法调用)
497
+ */
498
+ async #internalWrite(targetPath: string, content: Blob): Promise<boolean> {
499
+ if (!(await this.exists(targetPath))) {
500
+ const success = await this.createFile(targetPath);
501
+ if (!success) return false;
502
+ }
503
+ if (await this.isDir(targetPath)) {
504
+ console.warn("路径已存在");
577
505
  return false;
578
506
  }
507
+ const { name, parent } = parsePath(targetPath);
508
+ const file = new File([content], name, {
509
+ type: mime.getType(targetPath) || "application/octet-stream",
510
+ });
511
+ const fileRecord = await this.#db.files.where({ name, path: parent }).first();
512
+ if (fileRecord) {
513
+ await this.#db.files.put({ ...fileRecord, file });
514
+ return true;
515
+ }
516
+ return false;
579
517
  }
580
518
  /**
581
519
  * 读取文件内容
582
520
  * @param path 文件路径
583
521
  */
584
522
  async write(path: string, content: Blob) {
585
- try {
523
+ return this.#withErrorHandling("write", async () => {
586
524
  const targetPath = joinPath(path);
587
- if (this.#watchers[targetPath]) {
588
- for (const handler of this.#watchers[targetPath]) {
589
- if (await handler("write")) {
590
- if (this.options.throwError) {
591
- throw new VVVFSError("Write", "写入文件失败:监听器取消了操作");
592
- }
593
- return false;
594
- }
595
- }
596
- }
597
- if (this.#lockedFiles[targetPath]) {
598
- console.warn("文件已被锁定");
599
- if (this.options.throwError) {
600
- throw new VVVFSError("Write", "文件已被锁定");
601
- }
602
- return false;
603
- }
604
- if (!(await this.exists(targetPath))) {
605
- const success = await this.createFile(targetPath);
606
- if (!success) {
607
- console.warn("创建文件失败");
608
- if (this.options.throwError) {
609
- throw new VVVFSError("Write", "创建文件失败");
610
- }
611
- return false;
612
- }
613
- }
614
- if (await this.isDir(targetPath)) {
615
- console.warn("路径已存在");
616
- if (this.options.throwError) {
617
- throw new VVVFSError("Write", "路径已存在");
618
- }
619
- return false;
620
- }
621
- const { name, parent } = parsePath(targetPath);
622
- const file = new File([content], name, {
623
- type: mime.getType(targetPath) || "application/octet-stream",
624
- });
625
- const fileRecord = await this.#db.files.where({ name, path: parent }).first();
626
- if (fileRecord) {
627
- await this.#db.files.put({
628
- ...fileRecord,
629
- file: file,
630
- });
631
- return true;
632
- } else {
633
- console.warn("文件记录未找到,无法写入内容");
634
- if (this.options.throwError) {
635
- throw new VVVFSError("Write", "文件记录未找到,无法写入内容");
636
- }
637
- return false;
638
- }
639
- } catch (error) {
640
- console.error("写入文件失败", error);
641
- if (this.options.throwError) {
642
- throw new VVVFSError("Write", "写入文件失败" + error);
643
- }
644
- return false;
645
- }
525
+ if (!(await this.#checkAccess(targetPath, "write"))) return false;
526
+ return await this.#internalWrite(targetPath, content);
527
+ }, false);
646
528
  }
647
529
  /**
648
530
  * 写入文本内容
@@ -650,16 +532,10 @@ class VVVFS {
650
532
  * @param content 文本内容
651
533
  */
652
534
  async writeText(path: string, content: string) {
653
- try {
535
+ return this.#withErrorHandling("writeText", async () => {
654
536
  const blob = new Blob([content], { type: "text/plain" });
655
537
  return await this.write(path, blob);
656
- } catch (error) {
657
- console.error("写入文件失败", error);
658
- if (this.options.throwError) {
659
- throw new VVVFSError("Write", "写入文件失败" + error);
660
- }
661
- return false;
662
- }
538
+ }, false);
663
539
  }
664
540
  /**
665
541
  * 写入JSON内容
@@ -667,19 +543,13 @@ class VVVFS {
667
543
  * @param content JSON内容
668
544
  * @param format 是否格式化
669
545
  */
670
- async writeJson(path: string, content: Record<string, any>, format: boolean = true) {
671
- try {
546
+ async writeJson(path: string, content: Record<string, unknown>, format: boolean = true) {
547
+ return this.#withErrorHandling("writeJson", async () => {
672
548
  return await this.writeText(
673
549
  path,
674
550
  JSON.stringify(content, null, format ? 4 : undefined),
675
551
  );
676
- } catch (error) {
677
- console.error("写入文件失败", error);
678
- if (this.options.throwError) {
679
- throw new VVVFSError("Write", "写入文件失败" + error);
680
- }
681
- return false;
682
- }
552
+ }, false);
683
553
  }
684
554
  /**
685
555
  * 追加内容
@@ -687,41 +557,18 @@ class VVVFS {
687
557
  * @param content 追加内容
688
558
  */
689
559
  async append(path: string, content: Blob) {
690
- try {
560
+ return this.#withErrorHandling("append", async () => {
691
561
  const targetPath = joinPath(path);
692
- if (this.#watchers[targetPath]) {
693
- for (const handler of this.#watchers[targetPath]) {
694
- if (await handler("append")) {
695
- if (this.options.throwError) {
696
- throw new VVVFSError("Append", "追加文件失败:监听器取消了操作");
697
- }
698
- return false;
699
- }
700
- }
701
- }
702
- if (this.#lockedFiles[targetPath]) {
703
- console.warn("文件已被锁定");
704
- if (this.options.throwError) {
705
- throw new VVVFSError("Append", "文件已被锁定");
706
- }
707
- return false;
708
- }
709
- const existingFile = await this.read(targetPath);
562
+ if (!(await this.#checkAccess(targetPath, "append"))) return false;
563
+ const existingFile = await this.#internalRead(targetPath);
710
564
  if (existingFile) {
711
565
  const blob = new Blob([existingFile, content], {
712
566
  type: "application/octet-stream",
713
567
  });
714
- return await this.write(targetPath, blob);
715
- } else {
716
- return await this.write(targetPath, content);
717
- }
718
- } catch (error) {
719
- console.error("追加文件失败", error);
720
- if (this.options.throwError) {
721
- throw new VVVFSError("Append", "追加文件失败" + error);
568
+ return await this.#internalWrite(targetPath, blob);
722
569
  }
723
- return false;
724
- }
570
+ return await this.#internalWrite(targetPath, content);
571
+ }, false);
725
572
  }
726
573
  /**
727
574
  * 追加文本内容
@@ -729,109 +576,58 @@ class VVVFS {
729
576
  * @param content 追加内容
730
577
  */
731
578
  async appendText(path: string, content: string) {
732
- try {
579
+ return this.#withErrorHandling("appendText", async () => {
733
580
  const blob = new Blob([content], { type: "text/plain" });
734
581
  return await this.append(path, blob);
735
- } catch (error) {
736
- console.error("追加文件失败", error);
737
- if (this.options.throwError) {
738
- throw new VVVFSError("Append", "追加文件失败" + error);
739
- }
740
- return false;
741
- }
582
+ }, false);
583
+ }
584
+ /**
585
+ * 内部读取(跳过访问检查,供内部方法调用)
586
+ */
587
+ async #internalRead(targetPath: string): Promise<File | null> {
588
+ if (!(await this.exists(targetPath))) return null;
589
+ const { name, parent } = parsePath(targetPath);
590
+ return (await this.#db.files.where({ name, path: parent }).first())?.file ?? null;
742
591
  }
743
592
  /**
744
593
  * 读取文件内容
745
594
  * @param path 文件路径
746
595
  */
747
596
  async read(path: string) {
748
- try {
597
+ return this.#withErrorHandling("read", async () => {
749
598
  const targetPath = joinPath(path);
750
- if (this.#watchers[targetPath]) {
751
- for (const handler of this.#watchers[targetPath]) {
752
- if (await handler("read")) {
753
- if (this.options.throwError) {
754
- throw new VVVFSError("Read", "读取文件失败:监听器取消了操作");
755
- }
756
- return null;
757
- }
758
- }
759
- }
760
- if (this.#lockedFiles[targetPath]) {
761
- console.warn("文件已被锁定");
762
- if (this.options.throwError) {
763
- throw new VVVFSError("Read", "文件已被锁定");
764
- }
765
- return null;
766
- }
767
- if (!(await this.exists(targetPath))) {
768
- console.warn("文件不存在");
769
- if (this.options.throwError) {
770
- throw new VVVFSError("Read", "文件不存在");
771
- }
772
- return null;
773
- }
774
- const { name, parent } = parsePath(targetPath);
775
- return (await this.#db.files.where({ name, path: parent }).first())?.file;
776
- } catch (error) {
777
- console.error("读取文件失败", error);
778
- if (this.options.throwError) {
779
- throw new VVVFSError("Read", "读取文件失败" + error);
780
- }
781
- return null;
782
- }
599
+ if (!(await this.#checkAccess(targetPath, "read"))) return null;
600
+ return await this.#internalRead(targetPath);
601
+ }, null as File | null);
783
602
  }
784
603
  /**
785
604
  * 读取文件内容
786
605
  * @param path 文件路径
787
606
  */
788
607
  async readText(path: string) {
789
- try {
608
+ return this.#withErrorHandling("readText", async () => {
790
609
  const file = await this.read(path);
791
- if (file) {
792
- return await file.text();
793
- } else {
794
- console.warn("文件不存在");
795
- if (this.options.throwError) {
796
- throw new VVVFSError("Read", "文件不存在");
797
- }
798
- return null;
799
- }
800
- } catch (error) {
801
- console.error("读取文件失败", error);
802
- if (this.options.throwError) {
803
- throw new VVVFSError("Read", "读取文件失败" + error);
804
- }
805
- return null;
806
- }
610
+ return file ? await file.text() : null;
611
+ }, null as string | null);
807
612
  }
808
613
  /**
809
614
  * 读取JSON内容
810
615
  * @param path 文件路径
811
616
  */
812
617
  async readJson(path: string) {
813
- try {
618
+ return this.#withErrorHandling("readJson", async () => {
814
619
  const text = await this.readText(path);
815
- if (text) {
816
- try {
817
- return JSON.parse(text);
818
- } catch (error) {
819
- console.error("解析JSON失败", error);
820
- if (this.options.throwError) {
821
- throw new VVVFSError("Read", "解析JSON失败" + error);
822
- }
823
- return null;
620
+ if (!text) return null;
621
+ try {
622
+ return JSON.parse(text);
623
+ } catch (error) {
624
+ console.error("解析JSON失败", error);
625
+ if (this.options.throwError) {
626
+ throw new VVVFSError("Read", "解析JSON失败" + error);
824
627
  }
825
- } else {
826
628
  return null;
827
629
  }
828
- } catch (error) {
829
- console.error("读取文件失败", error);
830
- if (this.options.throwError) {
831
- throw new VVVFSError("Read", "读取文件失败" + error);
832
- }
833
- return null;
834
- }
630
+ }, null as Record<string, unknown> | null);
835
631
  }
836
632
  /**
837
633
  * 读取文件内容
@@ -840,41 +636,10 @@ class VVVFS {
840
636
  * @param end 结束位置
841
637
  */
842
638
  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
- }
639
+ return this.#withErrorHandling("readChunk", async () => {
640
+ const file = await this.read(path);
641
+ return file?.slice(start, end) ?? null;
642
+ }, null as Blob | null);
878
643
  }
879
644
  /**
880
645
  * 读取文件内容
@@ -883,102 +648,51 @@ class VVVFS {
883
648
  * @param end 读取结束位置
884
649
  */
885
650
  async readTextChunk(path: string, start: number, end: number) {
886
- try {
651
+ return this.#withErrorHandling("readTextChunk", async () => {
887
652
  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
- }
653
+ return chunk ? await chunk.text() : null;
654
+ }, null as string | null);
899
655
  }
900
656
  /**
901
657
  * 判断是否是文件
902
658
  * @param path 文件路径
903
659
  */
904
660
  async isFile(path: string) {
905
- try {
906
- const targetPath = joinPath(path);
907
- const { name, parent } = parsePath(targetPath);
661
+ return this.#withErrorHandling("isFile", async () => {
662
+ const { name, parent } = parsePath(path);
908
663
  return (await this.#db.files.where({ name, path: parent, type: "file" }).count()) > 0;
909
- } catch (error) {
910
- console.error("判断文件类型失败", error);
911
- if (this.options.throwError) {
912
- throw new VVVFSError("IsFile", "判断文件类型失败" + error);
913
- }
914
- return false;
915
- }
664
+ }, false);
916
665
  }
917
666
  /**
918
667
  * 判断是否是目录
919
668
  * @param path 文件路径
920
669
  */
921
670
  async isDir(path: string) {
922
- try {
923
- const targetPath = joinPath(path);
924
- const { name, parent } = parsePath(targetPath);
925
- return (await this.#db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
926
- } catch (error) {
927
- console.error("判断文件类型失败", error);
928
- if (this.options.throwError) {
929
- throw new VVVFSError("IsDir", "判断文件类型失败" + error);
930
- }
931
- return false;
932
- }
671
+ return this.#withErrorHandling("isDir", async () => {
672
+ const { name, parent } = parsePath(path);
673
+ return (await this.#db.files.where({ name, path: parent, type: "dir" }).count()) > 0;
674
+ }, false);
933
675
  }
934
676
  /**
935
677
  * 列出目录下的文件
936
678
  * @param path 目录路径
937
679
  */
938
680
  async list(path: string) {
939
- try {
681
+ return this.#withErrorHandling("list", async () => {
940
682
  const targetPath = joinPath(path);
941
- if (this.#watchers[targetPath]) {
942
- for (const handler of this.#watchers[targetPath]) {
943
- if (await handler("list")) {
944
- if (this.options.throwError) {
945
- throw new VVVFSError("List", "列出目录下的文件失败:监听器取消了操作");
946
- }
947
- return [];
948
- }
949
- }
950
- }
951
- if (this.#lockedFiles[targetPath]) {
952
- console.warn("文件已被锁定");
953
- if (this.options.throwError) {
954
- throw new VVVFSError("List", "文件已被锁定");
955
- }
956
- return [];
957
- }
683
+ if (!(await this.#checkAccess(targetPath, "list"))) return [];
958
684
  if (!(await this.exists(targetPath))) {
959
685
  console.warn("路径不存在");
960
- if (this.options.throwError) {
961
- throw new VVVFSError("List", "路径不存在");
962
- }
963
686
  return [];
964
687
  }
965
688
  if (!(await this.isDir(targetPath))) {
966
689
  console.warn("路径不是目录");
967
- if (this.options.throwError) {
968
- throw new VVVFSError("List", "路径不是目录");
969
- }
970
690
  return [];
971
691
  }
972
692
  return (await this.#db.files.where({ path: targetPath }).toArray())
973
693
  .map((file) => file.name)
974
- .filter((item) => item != "");
975
- } catch (error) {
976
- console.error("列出目录下的文件失败", error);
977
- if (this.options.throwError) {
978
- throw new VVVFSError("List", "列出目录下的文件失败" + error);
979
- }
980
- return [];
981
- }
694
+ .filter((item) => item !== "");
695
+ }, [] as string[]);
982
696
  }
983
697
  /**
984
698
  * 重命名文件
@@ -986,50 +700,23 @@ class VVVFS {
986
700
  * @param newName 新文件名
987
701
  */
988
702
  async rename(path: string, newName: string) {
989
- try {
703
+ return this.#withErrorHandling("rename", async () => {
990
704
  const sourcePath = joinPath(path);
991
- const { name, parent } = parsePath(sourcePath);
992
- if (this.#watchers[sourcePath]) {
993
- for (const handler of this.#watchers[sourcePath]) {
994
- if (await handler("rename")) {
995
- if (this.options.throwError) {
996
- throw new VVVFSError("Rename", "重命名文件失败:监听器取消了操作");
997
- }
998
- return false;
999
- }
1000
- }
1001
- }
1002
- if (this.#lockedFiles[sourcePath]) {
1003
- console.warn("文件已被锁定");
1004
- if (this.options.throwError) {
1005
- throw new VVVFSError("Rename", "文件已被锁定");
1006
- }
1007
- return false;
1008
- }
705
+ if (!(await this.#checkAccess(sourcePath, "rename"))) return false;
1009
706
  if (!(await this.exists(sourcePath))) {
1010
707
  console.warn("文件不存在");
1011
- if (this.options.throwError) {
1012
- throw new VVVFSError("Rename", "文件不存在");
1013
- }
1014
708
  return false;
1015
709
  }
710
+ const { name, parent } = parsePath(sourcePath);
1016
711
  const newPath = joinPath(parent, newName);
1017
- if (newPath === sourcePath) {
1018
- return true;
1019
- }
712
+ if (newPath === sourcePath) return true;
1020
713
  if (await this.exists(newPath)) {
1021
714
  console.warn("目标文件已存在");
1022
- if (this.options.throwError) {
1023
- throw new VVVFSError("Rename", "目标文件已存在");
1024
- }
1025
715
  return false;
1026
716
  }
1027
717
  const fileRecord = await this.#db.files.where({ path: parent, name }).first();
1028
718
  if (!fileRecord) {
1029
719
  console.warn("文件记录未找到");
1030
- if (this.options.throwError) {
1031
- throw new VVVFSError("Rename", "文件记录未找到");
1032
- }
1033
720
  return false;
1034
721
  }
1035
722
  if (fileRecord.type === "dir") {
@@ -1045,48 +732,20 @@ class VVVFS {
1045
732
  await this.#db.files.update(descendant.id!, { path: updatedPath });
1046
733
  }
1047
734
  }
1048
- await this.#db.files.update(fileRecord.id!, {
1049
- name: newName,
1050
- path: parent,
1051
- });
735
+ await this.#db.files.update(fileRecord.id!, { name: newName, path: parent });
1052
736
  return true;
1053
- } catch (e) {
1054
- console.error(e);
1055
- if (this.options.throwError) {
1056
- throw new VVVFSError("Rename", "重命名文件失败" + e);
1057
- }
1058
- return false;
1059
- }
737
+ }, false);
1060
738
  }
1061
739
  /**
1062
740
  * 删除文件
1063
741
  * @param path 文件路径
1064
742
  */
1065
743
  async delete(path: string) {
1066
- try {
744
+ return this.#withErrorHandling("delete", async () => {
1067
745
  const targetPath = joinPath(path);
1068
- if (this.#watchers[targetPath]) {
1069
- for (const handler of this.#watchers[targetPath]) {
1070
- if (await handler("delete")) {
1071
- if (this.options.throwError) {
1072
- throw new VVVFSError("Delete", "删除文件失败:监听器取消了操作");
1073
- }
1074
- return false;
1075
- }
1076
- }
1077
- }
1078
- if (this.#lockedFiles[targetPath]) {
1079
- console.warn("文件已被锁定");
1080
- if (this.options.throwError) {
1081
- throw new VVVFSError("Delete", "文件已被锁定");
1082
- }
1083
- return false;
1084
- }
746
+ if (!(await this.#checkAccess(targetPath, "delete"))) return false;
1085
747
  if (!(await this.exists(targetPath))) {
1086
748
  console.warn("文件不存在");
1087
- if (this.options.throwError) {
1088
- throw new VVVFSError("Delete", "文件不存在");
1089
- }
1090
749
  return false;
1091
750
  }
1092
751
  const { name, parent } = parsePath(targetPath);
@@ -1102,22 +761,15 @@ class VVVFS {
1102
761
  await this.#db.files.delete(dirRecord.id!);
1103
762
  }
1104
763
  return true;
1105
- } else {
1106
- const fileRecord = await this.#db.files
1107
- .where({ name, path: parent, type: "file" })
1108
- .first();
1109
- if (fileRecord) {
1110
- await this.#db.files.delete(fileRecord.id!);
1111
- }
1112
- return true;
1113
764
  }
1114
- } catch (e) {
1115
- console.error(e);
1116
- if (this.options.throwError) {
1117
- throw new VVVFSError("Delete", "删除文件失败" + e);
765
+ const fileRecord = await this.#db.files
766
+ .where({ name, path: parent, type: "file" })
767
+ .first();
768
+ if (fileRecord) {
769
+ await this.#db.files.delete(fileRecord.id!);
1118
770
  }
1119
- return false;
1120
- }
771
+ return true;
772
+ }, false);
1121
773
  }
1122
774
  /**
1123
775
  * 移动文件
@@ -1125,48 +777,21 @@ class VVVFS {
1125
777
  * @param newPath 新路径
1126
778
  */
1127
779
  async move(path: string, newPath: string) {
1128
- try {
780
+ return this.#withErrorHandling("move", async () => {
1129
781
  const sourcePath = joinPath(path);
1130
782
  const destinationPath = joinPath(newPath);
1131
- if (this.#watchers[sourcePath]) {
1132
- for (const handler of this.#watchers[sourcePath]) {
1133
- if (await handler("move")) {
1134
- if (this.options.throwError) {
1135
- throw new VVVFSError("Move", "移动文件失败:监听器取消了操作");
1136
- }
1137
- return false;
1138
- }
1139
- }
1140
- }
1141
- if (this.#lockedFiles[sourcePath]) {
1142
- console.warn("文件已被锁定");
1143
- if (this.options.throwError) {
1144
- throw new VVVFSError("Move", "文件已被锁定");
1145
- }
1146
- return false;
1147
- }
1148
- if (sourcePath === destinationPath) {
1149
- return true;
1150
- }
783
+ if (!(await this.#checkAccess(sourcePath, "move"))) return false;
784
+ if (sourcePath === destinationPath) return true;
1151
785
  if (await this.exists(destinationPath)) {
1152
786
  console.warn("目标文件已存在");
1153
- if (this.options.throwError) {
1154
- throw new VVVFSError("Move", "目标文件已存在");
1155
- }
1156
787
  return false;
1157
788
  }
1158
789
  if (!(await this.exists(sourcePath))) {
1159
790
  console.warn("源文件不存在");
1160
- if (this.options.throwError) {
1161
- throw new VVVFSError("Move", "源文件不存在");
1162
- }
1163
791
  return false;
1164
792
  }
1165
793
  if (destinationPath.startsWith(sourcePath + "/")) {
1166
794
  console.warn("目标路径是源路径的子路径");
1167
- if (this.options.throwError) {
1168
- throw new VVVFSError("Move", "目标路径是源路径的子路径");
1169
- }
1170
795
  return false;
1171
796
  }
1172
797
  const { name, parent } = parsePath(sourcePath);
@@ -1184,24 +809,17 @@ class VVVFS {
1184
809
  await this.#db.files.delete(dirRecord.id!);
1185
810
  }
1186
811
  return true;
1187
- } else {
1188
- await this.createDir(newParent);
1189
- const fileRecord = await this.#db.files
1190
- .where({ name, path: parent, type: "file" })
1191
- .first();
1192
- if (fileRecord) {
1193
- await this.#db.files.update(fileRecord.id!, { name: newName, path: newParent });
1194
- return true;
1195
- }
1196
- return false;
1197
812
  }
1198
- } catch (e) {
1199
- console.error(e);
1200
- if (this.options.throwError) {
1201
- throw new VVVFSError("Move", "移动文件失败" + e);
813
+ await this.createDir(newParent);
814
+ const fileRecord = await this.#db.files
815
+ .where({ name, path: parent, type: "file" })
816
+ .first();
817
+ if (fileRecord) {
818
+ await this.#db.files.update(fileRecord.id!, { name: newName, path: newParent });
819
+ return true;
1202
820
  }
1203
821
  return false;
1204
- }
822
+ }, false);
1205
823
  }
1206
824
  /**
1207
825
  * 复制文件
@@ -1209,45 +827,20 @@ class VVVFS {
1209
827
  * @param newPath 新路径
1210
828
  */
1211
829
  async copy(path: string, newPath: string) {
1212
- try {
830
+ return this.#withErrorHandling("copy", async () => {
1213
831
  const sourcePath = joinPath(path);
1214
832
  const destinationPath = joinPath(newPath);
1215
- if (this.#watchers[sourcePath]) {
1216
- for (const handler of this.#watchers[sourcePath]) {
1217
- if (await handler("copy")) {
1218
- if (this.options.throwError) {
1219
- throw new VVVFSError("Copy", "复制文件失败:监听器取消了操作");
1220
- }
1221
- return false;
1222
- }
1223
- }
1224
- }
1225
- if (this.#lockedFiles[sourcePath]) {
1226
- console.warn("文件已被锁定");
1227
- if (this.options.throwError) {
1228
- throw new VVVFSError("Copy", "文件已被锁定");
1229
- }
1230
- return false;
1231
- }
833
+ if (!(await this.#checkAccess(sourcePath, "copy"))) return false;
1232
834
  if (await this.exists(destinationPath)) {
1233
835
  console.warn("目标文件已存在");
1234
- if (this.options.throwError) {
1235
- throw new VVVFSError("Copy", "目标文件已存在");
1236
- }
1237
836
  return false;
1238
837
  }
1239
838
  if (!(await this.exists(sourcePath))) {
1240
839
  console.warn("源文件不存在");
1241
- if (this.options.throwError) {
1242
- throw new VVVFSError("Copy", "源文件不存在");
1243
- }
1244
840
  return false;
1245
841
  }
1246
842
  if (destinationPath.startsWith(sourcePath + "/")) {
1247
843
  console.warn("目标路径是源路径的子路径");
1248
- if (this.options.throwError) {
1249
- throw new VVVFSError("Copy", "目标路径是源路径的子路径");
1250
- }
1251
844
  return false;
1252
845
  }
1253
846
  const { name, parent } = parsePath(sourcePath);
@@ -1259,28 +852,21 @@ class VVVFS {
1259
852
  await this.copy(joinPath(sourcePath, child), joinPath(destinationPath, child));
1260
853
  }
1261
854
  return true;
1262
- } else {
1263
- const fileRecord = await this.#db.files
1264
- .where({ name, path: parent, type: "file" })
1265
- .first();
1266
- if (fileRecord) {
1267
- await this.#db.files.add({
1268
- name: newName,
1269
- path: newParent,
1270
- type: fileRecord.type,
1271
- file: fileRecord.file,
1272
- });
1273
- return true;
1274
- }
1275
- return false;
1276
855
  }
1277
- } catch (e) {
1278
- console.error(e);
1279
- if (this.options.throwError) {
1280
- throw new VVVFSError("Copy", "复制文件失败" + e);
856
+ const fileRecord = await this.#db.files
857
+ .where({ name, path: parent, type: "file" })
858
+ .first();
859
+ if (fileRecord) {
860
+ await this.#db.files.add({
861
+ name: newName,
862
+ path: newParent,
863
+ type: fileRecord.type,
864
+ file: fileRecord.file,
865
+ });
866
+ return true;
1281
867
  }
1282
868
  return false;
1283
- }
869
+ }, false);
1284
870
  }
1285
871
  /**
1286
872
  * 搜索文件
@@ -1288,49 +874,35 @@ class VVVFS {
1288
874
  * @param query 查询字符串
1289
875
  */
1290
876
  async search(basePath: string, query: string) {
1291
- try {
877
+ return this.#withErrorHandling("search", async () => {
1292
878
  if (!(await this.isDir(basePath))) {
1293
879
  console.warn("基础路径不是目录");
1294
- if (this.options.throwError) {
1295
- throw new VVVFSError("Search", "基础路径不是目录");
1296
- }
1297
880
  return null;
1298
881
  }
1299
882
  const that = this;
1300
883
  const dirs = await this.list(basePath);
1301
- return await search(basePath, dirs);
1302
- async function search(parent: string, files: string[]) {
884
+ return await searchRecursive(basePath, dirs);
885
+ async function searchRecursive(parent: string, files: string[]) {
1303
886
  const result: string[] = [];
1304
- console.log(files);
1305
887
  for (const file of files) {
1306
- if (file == "") continue;
1307
- console.log(file);
1308
- if (await that.isDir(joinPath(parent, file))) {
888
+ if (file === "") continue;
889
+ const fullPath = joinPath(parent, file);
890
+ if (await that.isDir(fullPath)) {
1309
891
  if (file.includes(query)) {
1310
- result.push(joinPath(parent, file) + "/");
892
+ result.push(fullPath + "/");
1311
893
  }
1312
894
  result.push(
1313
- ...(await search(
1314
- joinPath(parent, file),
1315
- await that.list(joinPath(parent, file)),
1316
- )),
895
+ ...(await searchRecursive(fullPath, await that.list(fullPath))),
1317
896
  );
1318
- } else if (await that.isFile(joinPath(parent, file))) {
897
+ } else if (await that.isFile(fullPath)) {
1319
898
  if (file.includes(query)) {
1320
- console.log(joinPath(parent, file));
1321
- result.push(joinPath(parent, file));
899
+ result.push(fullPath);
1322
900
  }
1323
901
  }
1324
902
  }
1325
903
  return result;
1326
904
  }
1327
- } catch (e) {
1328
- console.error(e);
1329
- if (this.options.throwError) {
1330
- throw new VVVFSError("Search", "搜索文件失败" + e);
1331
- }
1332
- return null;
1333
- }
905
+ }, null as string[] | null);
1334
906
  }
1335
907
  /**
1336
908
  * 监听文件
@@ -1349,46 +921,38 @@ class VVVFS {
1349
921
  * @param path 文件路径
1350
922
  */
1351
923
  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", "文件不存在");
924
+ return this.#withErrorHandling("lock", async () => {
925
+ path = joinPath(path);
926
+ if (!(await this.exists(path))) {
927
+ console.warn("文件不存在");
928
+ return false;
1357
929
  }
1358
- return false;
1359
- }
1360
- if (this.#lockedFiles[path]) {
1361
- console.warn("文件已被锁定");
1362
- if (this.options.throwError) {
1363
- throw new VVVFSError("Lock", "文件已被锁定");
930
+ if (this.#lockedFiles[path]) {
931
+ console.warn("文件已被锁定");
932
+ return false;
1364
933
  }
1365
- return false;
1366
- }
1367
- this.#lockedFiles[path] = true;
1368
- return true;
934
+ this.#lockedFiles[path] = true;
935
+ return true;
936
+ }, false);
1369
937
  }
1370
938
  /**
1371
939
  * 解锁文件
1372
940
  * @param path 文件路径
1373
941
  */
1374
942
  async unlock(path: string) {
1375
- path = joinPath(path);
1376
- if (!(await this.exists(path))) {
1377
- console.warn("文件不存在");
1378
- if (this.options.throwError) {
1379
- throw new VVVFSError("Unlock", "文件不存在");
943
+ return this.#withErrorHandling("unlock", async () => {
944
+ path = joinPath(path);
945
+ if (!(await this.exists(path))) {
946
+ console.warn("文件不存在");
947
+ return false;
1380
948
  }
1381
- return false;
1382
- }
1383
- if (!this.#lockedFiles[path]) {
1384
- console.warn("文件未被锁定");
1385
- if (this.options.throwError) {
1386
- throw new VVVFSError("Unlock", "文件未被锁定");
949
+ if (!this.#lockedFiles[path]) {
950
+ console.warn("文件未被锁定");
951
+ return false;
1387
952
  }
1388
- return false;
1389
- }
1390
- delete this.#lockedFiles[path];
1391
- return true;
953
+ delete this.#lockedFiles[path];
954
+ return true;
955
+ }, false);
1392
956
  }
1393
957
  }
1394
958
  /**
@@ -1418,7 +982,7 @@ function joinPath(...paths: string[]) {
1418
982
  if (segments.length === 0) return ".";
1419
983
  const isAbsolute = segments[0].startsWith("/");
1420
984
  const parts = segments.join("/").split("/");
1421
- const stack = [];
985
+ const stack: string[] = [];
1422
986
  for (const part of parts) {
1423
987
  if (part === "" || part === ".") {
1424
988
  continue;
@@ -1450,4 +1014,4 @@ Object.defineProperty(VVVFS, "author", {
1450
1014
  enumerable: true,
1451
1015
  configurable: false,
1452
1016
  });
1453
- (globalThis as any).VVVFS = VVVFS;
1017
+ (globalThis as any).VVVFS = VVVFS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vvvfs",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "一个使用IndexDB实现的虚拟文件系统",
5
5
  "keywords": [
6
6
  "Virtual",