vvvfs 0.0.1 → 0.0.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.
package/index.html CHANGED
@@ -11,18 +11,22 @@
11
11
  <body>
12
12
  <script>
13
13
  (async () => {
14
- globalThis.vvvfs = new VVVFS();
15
- await vvvfs.reset();
16
- console.log(vvvfs);
17
- console.log("创建文件", await vvvfs.createFile("/test.txt"));
18
- console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
19
- console.log("文件是否存在", await vvvfs.exists("/test.txt"));
20
- console.log("文件是否存在", await vvvfs.exists("/test2.txt"));
21
- console.log("读取文件", await vvvfs.readText("/test.txt"));
22
- console.log("复制文件", await vvvfs.copy("/test.txt", "/test2.txt"));
23
- console.log("搜索文件", await vvvfs.search("test"));
24
- console.log("移动文件", await vvvfs.move("/test2.txt", "/test3.txt"));
25
- console.log("删除文件", await vvvfs.delete("/test.txt"));
14
+ try {
15
+ globalThis.vvvfs = new VVVFS();
16
+ await vvvfs.reset();
17
+ console.log(vvvfs);
18
+ console.log("创建文件", await vvvfs.createFile("/test.txt"));
19
+ console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
20
+ console.log("文件是否存在", await vvvfs.exists("/test.txt"));
21
+ console.log("文件是否存在", await vvvfs.exists("/test2.txt"));
22
+ console.log("读取文件", await vvvfs.readText("/test.txt"));
23
+ console.log("复制文件", await vvvfs.copy("/test.txt", "/test2.txt"));
24
+ console.log("搜索文件", await vvvfs.search("test"));
25
+ console.log("移动文件", await vvvfs.move("/test2.txt", "/test3.txt"));
26
+ console.log("删除文件", await vvvfs.delete("/test.txt"));
27
+ } catch (error) {
28
+ console.error(error);
29
+ }
26
30
  })();
27
31
  </script>
28
32
  </body>
package/index.ts CHANGED
@@ -2,6 +2,20 @@ import { Dexie, Table } from "dexie";
2
2
  import packageJson from "./package.json" with { type: "json" };
3
3
  import mime from "mime";
4
4
 
5
+ /**
6
+ * 虚拟文件系统
7
+ * @author IFTC
8
+ * @description 虚拟文件系统
9
+ */
10
+
11
+ /**
12
+ * 文件记录
13
+ * @param id 文件id
14
+ * @param name 文件名
15
+ * @param path 文件路径
16
+ * @param type 文件类型(file或dir)
17
+ * @param file 文件对象
18
+ */
5
19
  export interface FileRecord {
6
20
  id?: number;
7
21
  name: string;
@@ -10,14 +24,40 @@ export interface FileRecord {
10
24
  file: File | null;
11
25
  }
12
26
 
27
+ /**
28
+ * 虚拟文件系统数据库
29
+ * @param files 文件存储数据表
30
+ */
13
31
  export interface VVVFSDatabase extends Dexie {
14
32
  files: Table<FileRecord, number>;
15
33
  }
16
34
 
35
+ /**
36
+ * 虚拟文件系统配置项
37
+ * @param throwError 是否抛出错误
38
+ */
39
+ export interface VVVFSOptions {
40
+ throwError: boolean;
41
+ }
42
+
43
+ /**
44
+ * 虚拟文件系统错误类
45
+ */
46
+ class VVVFSError extends Error {
47
+ /**
48
+ * @param type 错误类型
49
+ * @param message 错误信息
50
+ */
51
+ constructor(type: string, message: string) {
52
+ super(message);
53
+ this.name = "VVVFS" + type + "Error";
54
+ }
55
+ }
56
+
17
57
  const version = packageJson.version;
18
58
  class VVVFS {
19
59
  private db: VVVFSDatabase;
20
- options: Record<string, any>;
60
+ options: VVVFSOptions;
21
61
  /**
22
62
  * 虚拟文件系统版本
23
63
  */
@@ -27,22 +67,37 @@ class VVVFS {
27
67
  * @param name 虚拟文件系统名称
28
68
  * @param options 配置项
29
69
  */
30
- constructor(name = "vvvfs", options = {}) {
70
+ constructor(
71
+ name = "vvvfs",
72
+ options = {
73
+ throwError: false,
74
+ },
75
+ ) {
31
76
  this.options = options;
32
- this.db = new Dexie(name) as VVVFSDatabase;
33
- this.db.version(1).stores({
34
- files: "++id, name, path, type, file",
35
- });
77
+ try {
78
+ this.db = new Dexie(name) as VVVFSDatabase;
79
+ this.db.version(1).stores({
80
+ files: "++id, name, path, type, file",
81
+ });
82
+ } catch (error) {
83
+ console.error("创建数据库失败", error);
84
+ throw new VVVFSError("CreateDatabase", "创建数据库失败");
85
+ }
36
86
  }
37
87
  /**
38
88
  * 重置虚拟文件系统
39
89
  */
40
90
  async reset() {
41
- await this.db.delete();
42
- this.db = new Dexie(this.db.name) as VVVFSDatabase;
43
- this.db.version(1).stores({
44
- files: "++id, name, path, type, file",
45
- });
91
+ try {
92
+ await this.db.delete();
93
+ this.db = new Dexie(this.db.name) as VVVFSDatabase;
94
+ this.db.version(1).stores({
95
+ files: "++id, name, path, type, file",
96
+ });
97
+ } catch (error) {
98
+ console.error("重置数据库失败", error);
99
+ throw new VVVFSError("ResetDatabase", "重置数据库失败" + error);
100
+ }
46
101
  }
47
102
  /**
48
103
  * 创建文件
@@ -58,7 +113,7 @@ class VVVFS {
58
113
  if (!(await this.exists(parent))) {
59
114
  await this.createDir(parent);
60
115
  }
61
- console.log(name, parent);
116
+ // console.log(name, parent);
62
117
  await this.db.files.add({
63
118
  name: name,
64
119
  path: parent,
@@ -70,6 +125,9 @@ class VVVFS {
70
125
  return true;
71
126
  } catch (error) {
72
127
  console.error("创建文件失败", error);
128
+ if (this.options.throwError) {
129
+ throw new VVVFSError("CreateFile", "创建文件失败" + error);
130
+ }
73
131
  return false;
74
132
  }
75
133
  }
@@ -107,6 +165,9 @@ class VVVFS {
107
165
  return true;
108
166
  } catch (error) {
109
167
  console.error("创建目录失败", error);
168
+ if (this.options.throwError) {
169
+ throw new VVVFSError("CreateDir", "创建目录失败" + error);
170
+ }
110
171
  return false;
111
172
  }
112
173
  }
@@ -115,33 +176,47 @@ class VVVFS {
115
176
  * @param path 文件路径
116
177
  */
117
178
  async exists(path: string) {
118
- const { name, parent } = parsePath(path);
119
- return (
120
- (await this.db.files
121
- .where({
122
- name: name,
123
- path: parent,
124
- })
125
- .count()) > 0
126
- );
179
+ try {
180
+ const { name, parent } = parsePath(path);
181
+ return (
182
+ (await this.db.files
183
+ .where({
184
+ name: name,
185
+ path: parent,
186
+ })
187
+ .count()) > 0
188
+ );
189
+ } catch (error) {
190
+ console.error("判断文件是否存在失败", error);
191
+ if (this.options.throwError) {
192
+ throw new VVVFSError("Exists", "判断文件是否存在失败" + error);
193
+ }
194
+ return false;
195
+ }
127
196
  }
128
197
  /**
129
198
  * 读取文件内容
130
199
  * @param path 文件路径
131
200
  */
132
201
  async write(path: string, content: Blob) {
133
- if (!(await this.exists(path))) {
134
- const success = await this.createFile(path);
135
- if (!success) {
136
- console.warn("创建文件失败");
137
- return false;
138
- }
139
- }
140
202
  try {
203
+ if (!(await this.exists(path))) {
204
+ const success = await this.createFile(path);
205
+ if (!success) {
206
+ console.warn("创建文件失败");
207
+ if (this.options.throwError) {
208
+ throw new VVVFSError("Write", "创建文件失败");
209
+ }
210
+ return false;
211
+ }
212
+ }
141
213
  const { name, parent } = parsePath(path);
142
214
  console.log(name, parent);
143
215
  if (await this.isDir(parent + "/" + name)) {
144
216
  console.warn("路径已存在");
217
+ if (this.options.throwError) {
218
+ throw new VVVFSError("Write", "路径已存在");
219
+ }
145
220
  return false;
146
221
  }
147
222
  const file = new File([content], name, {
@@ -157,10 +232,16 @@ class VVVFS {
157
232
  return true;
158
233
  } else {
159
234
  console.warn("文件记录未找到,无法写入内容");
235
+ if (this.options.throwError) {
236
+ throw new VVVFSError("Write", "文件记录未找到,无法写入内容");
237
+ }
160
238
  return false;
161
239
  }
162
240
  } catch (error) {
163
241
  console.error("写入文件失败", error);
242
+ if (this.options.throwError) {
243
+ throw new VVVFSError("Write", "写入文件失败" + error);
244
+ }
164
245
  return false;
165
246
  }
166
247
  }
@@ -170,8 +251,16 @@ class VVVFS {
170
251
  * @param content 文本内容
171
252
  */
172
253
  async writeText(path: string, content: string) {
173
- const blob = new Blob([content], { type: "text/plain" });
174
- return await this.write(path, blob);
254
+ try {
255
+ const blob = new Blob([content], { type: "text/plain" });
256
+ return await this.write(path, blob);
257
+ } catch (error) {
258
+ console.error("写入文件失败", error);
259
+ if (this.options.throwError) {
260
+ throw new VVVFSError("Write", "写入文件失败" + error);
261
+ }
262
+ return false;
263
+ }
175
264
  }
176
265
  /**
177
266
  * 写入JSON内容
@@ -180,22 +269,39 @@ class VVVFS {
180
269
  * @param format 是否格式化
181
270
  */
182
271
  async writeJson(path: string, content: Record<string, any>, format?: boolean) {
183
- return await this.writeText(path, JSON.stringify(content, null, format ? 4 : undefined));
272
+ try {
273
+ return await this.writeText(
274
+ path,
275
+ JSON.stringify(content, null, format ? 4 : undefined),
276
+ );
277
+ } catch (error) {
278
+ console.error("写入文件失败", error);
279
+ if (this.options.throwError) {
280
+ throw new VVVFSError("Write", "写入文件失败" + error);
281
+ }
282
+ return false;
283
+ }
184
284
  }
185
285
  /**
186
286
  * 读取文件内容
187
287
  * @param path 文件路径
188
288
  */
189
289
  async read(path: string) {
190
- if (!(await this.exists(path))) {
191
- console.warn("文件不存在");
192
- return null;
193
- }
194
290
  try {
291
+ if (!(await this.exists(path))) {
292
+ console.warn("文件不存在");
293
+ if (this.options.throwError) {
294
+ throw new VVVFSError("Read", "文件不存在");
295
+ }
296
+ return null;
297
+ }
195
298
  const { name, parent } = parsePath(path);
196
299
  return (await this.db.files.where({ name, path: parent }).first())?.file;
197
300
  } catch (error) {
198
301
  console.error("读取文件失败", error);
302
+ if (this.options.throwError) {
303
+ throw new VVVFSError("Read", "读取文件失败" + error);
304
+ }
199
305
  return null;
200
306
  }
201
307
  }
@@ -204,10 +310,22 @@ class VVVFS {
204
310
  * @param path 文件路径
205
311
  */
206
312
  async readText(path: string) {
207
- const file = await this.read(path);
208
- if (file) {
209
- return await file.text();
210
- } else {
313
+ try {
314
+ const file = await this.read(path);
315
+ if (file) {
316
+ return await file.text();
317
+ } else {
318
+ console.warn("文件不存在");
319
+ if (this.options.throwError) {
320
+ throw new VVVFSError("Read", "文件不存在");
321
+ }
322
+ return null;
323
+ }
324
+ } catch (error) {
325
+ console.error("读取文件失败", error);
326
+ if (this.options.throwError) {
327
+ throw new VVVFSError("Read", "读取文件失败" + error);
328
+ }
211
329
  return null;
212
330
  }
213
331
  }
@@ -216,15 +334,26 @@ class VVVFS {
216
334
  * @param path 文件路径
217
335
  */
218
336
  async readJson(path: string) {
219
- const text = await this.readText(path);
220
- if (text) {
221
- try {
222
- return JSON.parse(text);
223
- } catch (error) {
224
- console.error("解析JSON失败", error);
337
+ try {
338
+ const text = await this.readText(path);
339
+ if (text) {
340
+ try {
341
+ return JSON.parse(text);
342
+ } catch (error) {
343
+ console.error("解析JSON失败", error);
344
+ if (this.options.throwError) {
345
+ throw new VVVFSError("Read", "解析JSON失败" + error);
346
+ }
347
+ return null;
348
+ }
349
+ } else {
225
350
  return null;
226
351
  }
227
- } else {
352
+ } catch (error) {
353
+ console.error("读取文件失败", error);
354
+ if (this.options.throwError) {
355
+ throw new VVVFSError("Read", "读取文件失败" + error);
356
+ }
228
357
  return null;
229
358
  }
230
359
  }
@@ -233,24 +362,48 @@ class VVVFS {
233
362
  * @param path 文件路径
234
363
  */
235
364
  async isFile(path: string) {
236
- const { name, parent } = parsePath(path);
237
- return (await this.db.files.where({ name, path: parent, type: "file" }).count()) > 0;
365
+ try {
366
+ const { name, parent } = parsePath(path);
367
+ return (await this.db.files.where({ name, path: parent, type: "file" }).count()) > 0;
368
+ } catch (error) {
369
+ console.error("判断文件类型失败", error);
370
+ if (this.options.throwError) {
371
+ throw new VVVFSError("IsFile", "判断文件类型失败" + error);
372
+ }
373
+ return false;
374
+ }
238
375
  }
239
376
  /**
240
377
  * 判断是否是目录
241
378
  * @param path 文件路径
242
379
  */
243
380
  async isDir(path: string) {
244
- const { name, parent } = parsePath(path);
245
- return (await this.db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
381
+ try {
382
+ const { name, parent } = parsePath(path);
383
+ return (await this.db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
384
+ } catch (error) {
385
+ console.error("判断文件类型失败", error);
386
+ if (this.options.throwError) {
387
+ throw new VVVFSError("IsDir", "判断文件类型失败" + error);
388
+ }
389
+ return false;
390
+ }
246
391
  }
247
392
  /**
248
393
  * 列出目录下的文件
249
394
  * @param path 目录路径
250
395
  */
251
396
  async list(path: string) {
252
- const { name, parent } = parsePath(path);
253
- return (await this.db.files.where({ path: parent }).toArray()).map((file) => file.name);
397
+ try {
398
+ const { name, parent } = parsePath(path);
399
+ return (await this.db.files.where({ path: parent }).toArray()).map((file) => file.name);
400
+ } catch (error) {
401
+ console.error("列出目录下的文件失败", error);
402
+ if (this.options.throwError) {
403
+ throw new VVVFSError("List", "列出目录下的文件失败" + error);
404
+ }
405
+ return [];
406
+ }
254
407
  }
255
408
  /**
256
409
  * 重命名文件
@@ -258,8 +411,14 @@ class VVVFS {
258
411
  * @param newName 新文件名
259
412
  */
260
413
  async rename(path: string, newName: string) {
261
- if (!(await this.exists(path))) throw new Error("File not exists");
262
414
  try {
415
+ if (!(await this.exists(path))) {
416
+ console.warn("文件不存在");
417
+ if (this.options.throwError) {
418
+ throw new VVVFSError("Rename", "文件不存在");
419
+ }
420
+ return false;
421
+ }
263
422
  const { name, parent } = parsePath(path);
264
423
  const file = await this.db.files.get({ path, name });
265
424
  await this.db.files.put({
@@ -271,6 +430,9 @@ class VVVFS {
271
430
  return true;
272
431
  } catch (e) {
273
432
  console.error(e);
433
+ if (this.options.throwError) {
434
+ throw new VVVFSError("Rename", "重命名文件失败" + e);
435
+ }
274
436
  return false;
275
437
  }
276
438
  }
@@ -279,8 +441,14 @@ class VVVFS {
279
441
  * @param path 文件路径
280
442
  */
281
443
  async delete(path: string) {
282
- if (!(await this.exists(path))) return false;
283
444
  try {
445
+ if (!(await this.exists(path))) {
446
+ console.warn("文件不存在");
447
+ if (this.options.throwError) {
448
+ throw new VVVFSError("Delete", "文件不存在");
449
+ }
450
+ return false;
451
+ }
284
452
  const { name, parent } = parsePath(path);
285
453
  if (await this.isDir(path)) {
286
454
  const files = await this.list(path);
@@ -301,6 +469,9 @@ class VVVFS {
301
469
  }
302
470
  } catch (e) {
303
471
  console.error(e);
472
+ if (this.options.throwError) {
473
+ throw new VVVFSError("Delete", "删除文件失败" + e);
474
+ }
304
475
  return false;
305
476
  }
306
477
  }
@@ -310,9 +481,21 @@ class VVVFS {
310
481
  * @param newPath 新路径
311
482
  */
312
483
  async move(path: string, newPath: string) {
313
- if (await this.exists(newPath)) return false;
314
- if (!(await this.exists(path))) return false;
315
484
  try {
485
+ if (await this.exists(newPath)) {
486
+ console.warn("目标文件已存在");
487
+ if (this.options.throwError) {
488
+ throw new VVVFSError("Move", "目标文件已存在");
489
+ }
490
+ return false;
491
+ }
492
+ if (!(await this.exists(path))) {
493
+ console.warn("源文件不存在");
494
+ if (this.options.throwError) {
495
+ throw new VVVFSError("Move", "源文件不存在");
496
+ }
497
+ return false;
498
+ }
316
499
  const { name, parent } = parsePath(path);
317
500
  const { name: newName, parent: newParent } = parsePath(newPath);
318
501
  await this.createDir(newParent);
@@ -329,6 +512,9 @@ class VVVFS {
329
512
  return true;
330
513
  } catch (e) {
331
514
  console.error(e);
515
+ if (this.options.throwError) {
516
+ throw new VVVFSError("Move", "移动文件失败" + e);
517
+ }
332
518
  return false;
333
519
  }
334
520
  }
@@ -338,9 +524,21 @@ class VVVFS {
338
524
  * @param newPath 新路径
339
525
  */
340
526
  async copy(path: string, newPath: string) {
341
- if (await this.exists(newPath)) return false;
342
- if (!(await this.exists(path))) return false;
343
527
  try {
528
+ if (await this.exists(newPath)) {
529
+ console.warn("目标文件已存在");
530
+ if (this.options.throwError) {
531
+ throw new VVVFSError("Copy", "目标文件已存在");
532
+ }
533
+ return false;
534
+ }
535
+ if (!(await this.exists(path))) {
536
+ console.warn("源文件不存在");
537
+ if (this.options.throwError) {
538
+ throw new VVVFSError("Copy", "源文件不存在");
539
+ }
540
+ return false;
541
+ }
344
542
  const { name, parent } = parsePath(path);
345
543
  const { name: newName, parent: newParent } = parsePath(newPath);
346
544
  await this.createDir(newParent);
@@ -356,6 +554,9 @@ class VVVFS {
356
554
  return true;
357
555
  } catch (e) {
358
556
  console.error(e);
557
+ if (this.options.throwError) {
558
+ throw new VVVFSError("Copy", "复制文件失败" + e);
559
+ }
359
560
  return false;
360
561
  }
361
562
  }
@@ -365,33 +566,47 @@ class VVVFS {
365
566
  * @param query 查询字符串
366
567
  */
367
568
  async search(basePath: string, query: string) {
368
- if (!await this.isDir(basePath)) return null;
369
- const that = this;
370
569
  try {
570
+ if (!(await this.isDir(basePath))) {
571
+ console.warn("基础路径不是目录");
572
+ if (this.options.throwError) {
573
+ throw new VVVFSError("Search", "基础路径不是目录");
574
+ }
575
+ return null;
576
+ }
577
+ const that = this;
371
578
  const dirs = await this.list(basePath);
372
579
  return await search(basePath, dirs);
373
- } catch (e) {
374
- console.error(e);
375
- return [];
376
- }
377
- async function search(parent: string, files: string[]) {
378
- const result: string[] = [];
379
- console.log(files)
380
- for (const file of files) {
381
- if (file == "") continue;
382
- console.log(file);
383
- if (await that.isDir(joinPath(parent, file))) {
384
- if (file.includes(query)) {
385
- result.push(joinPath(parent, file) + "/");
386
- }
387
- result.push(...await search(joinPath(parent, file), await that.list(joinPath(parent, file))));
388
- } else {
389
- if (file.includes(query)) {
390
- result.push(joinPath(parent, file));
580
+ async function search(parent: string, files: string[]) {
581
+ const result: string[] = [];
582
+ console.log(files);
583
+ for (const file of files) {
584
+ if (file == "") continue;
585
+ console.log(file);
586
+ if (await that.isDir(joinPath(parent, file))) {
587
+ if (file.includes(query)) {
588
+ result.push(joinPath(parent, file) + "/");
589
+ }
590
+ result.push(
591
+ ...(await search(
592
+ joinPath(parent, file),
593
+ await that.list(joinPath(parent, file)),
594
+ )),
595
+ );
596
+ } else {
597
+ if (file.includes(query)) {
598
+ result.push(joinPath(parent, file));
599
+ }
391
600
  }
392
601
  }
602
+ return result;
393
603
  }
394
- return result;
604
+ } catch (e) {
605
+ console.error(e);
606
+ if (this.options.throwError) {
607
+ throw new VVVFSError("Search", "搜索文件失败" + e);
608
+ }
609
+ return null;
395
610
  }
396
611
  }
397
612
  }
@@ -433,7 +648,7 @@ function joinPath(...paths: string[]) {
433
648
  return result || (isAbsolute ? "/" : ".");
434
649
  }
435
650
  Object.defineProperty(VVVFS, "version", {
436
- value: packageJson.version,
651
+ value: version,
437
652
  writable: false,
438
653
  enumerable: true,
439
654
  configurable: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vvvfs",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "一个使用IndexDB实现的虚拟文件系统",
5
5
  "keywords": [
6
6
  "Virtual",