vvvfs 0.0.2 → 0.0.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.
- package/README.md +13 -3
- package/dist/vvvfs.min.js +3 -3
- package/esbuild.config.js +2 -1
- package/index.html +2 -1
- package/index.ts +145 -5
- package/package.json +1 -1
- package/index.js +0 -142
package/esbuild.config.js
CHANGED
|
@@ -9,10 +9,11 @@ build({
|
|
|
9
9
|
outfile: 'dist/vvvfs.min.js',
|
|
10
10
|
format: 'iife',
|
|
11
11
|
define: {
|
|
12
|
-
'process.env.PACKAGE_VERSION': JSON.stringify(pkg.version),
|
|
12
|
+
'process.env.PACKAGE_VERSION': JSON.stringify(pkg.version),
|
|
13
13
|
},
|
|
14
14
|
minify: true,
|
|
15
15
|
platform: 'browser',
|
|
16
16
|
target: ["chrome80", "firefox70", "safari13", "edge80"],
|
|
17
17
|
logLevel: 'info',
|
|
18
|
+
drop: ['console', 'debugger'],
|
|
18
19
|
}).catch(() => process.exit(1));
|
package/index.html
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
try {
|
|
15
15
|
globalThis.vvvfs = new VVVFS();
|
|
16
16
|
await vvvfs.reset();
|
|
17
|
+
await vvvfs.init("IFTC");
|
|
17
18
|
console.log(vvvfs);
|
|
18
19
|
console.log("创建文件", await vvvfs.createFile("/test.txt"));
|
|
19
20
|
console.log("写入文件", await vvvfs.writeText("/test.txt", "Hello World"));
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
console.log("文件是否存在", await vvvfs.exists("/test2.txt"));
|
|
22
23
|
console.log("读取文件", await vvvfs.readText("/test.txt"));
|
|
23
24
|
console.log("复制文件", await vvvfs.copy("/test.txt", "/test2.txt"));
|
|
24
|
-
console.log("搜索文件", await vvvfs.search("
|
|
25
|
+
console.log("搜索文件", await vvvfs.search("/", "t"));
|
|
25
26
|
console.log("移动文件", await vvvfs.move("/test2.txt", "/test3.txt"));
|
|
26
27
|
console.log("删除文件", await vvvfs.delete("/test.txt"));
|
|
27
28
|
} catch (error) {
|
package/index.ts
CHANGED
|
@@ -77,13 +77,149 @@ class VVVFS {
|
|
|
77
77
|
try {
|
|
78
78
|
this.db = new Dexie(name) as VVVFSDatabase;
|
|
79
79
|
this.db.version(1).stores({
|
|
80
|
-
files: "++id, name, path, type, file",
|
|
80
|
+
files: "++id, name, path, type, file, [name+path+type]",
|
|
81
81
|
});
|
|
82
82
|
} catch (error) {
|
|
83
83
|
console.error("创建数据库失败", error);
|
|
84
84
|
throw new VVVFSError("CreateDatabase", "创建数据库失败");
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* 初始化虚拟文件系统
|
|
89
|
+
* @description 将Linux的系统初始文件初始化到数据库中
|
|
90
|
+
*/
|
|
91
|
+
async init(user?: string) {
|
|
92
|
+
const linuxInitFiles = [
|
|
93
|
+
{
|
|
94
|
+
name: "root",
|
|
95
|
+
path: "/",
|
|
96
|
+
type: "dir",
|
|
97
|
+
file: new File([], "root"),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "boot",
|
|
101
|
+
path: "/",
|
|
102
|
+
type: "dir",
|
|
103
|
+
file: new File([], "boot"),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "bin",
|
|
107
|
+
path: "/",
|
|
108
|
+
type: "dir",
|
|
109
|
+
file: new File([], "bin"),
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "dev",
|
|
113
|
+
path: "/",
|
|
114
|
+
type: "dir",
|
|
115
|
+
file: new File([], "dev"),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "etc",
|
|
119
|
+
path: "/",
|
|
120
|
+
type: "dir",
|
|
121
|
+
file: new File([], "etc"),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "home",
|
|
125
|
+
path: "/",
|
|
126
|
+
type: "dir",
|
|
127
|
+
file: new File([], "home"),
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: user || "root",
|
|
131
|
+
path: "/home",
|
|
132
|
+
type: "dir",
|
|
133
|
+
file: new File([], "home"),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "lib",
|
|
137
|
+
path: "/",
|
|
138
|
+
type: "dir",
|
|
139
|
+
file: new File([], "lib"),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "lib64",
|
|
143
|
+
path: "/",
|
|
144
|
+
type: "dir",
|
|
145
|
+
file: new File([], "lib64"),
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "media",
|
|
149
|
+
path: "/",
|
|
150
|
+
type: "dir",
|
|
151
|
+
file: new File([], "media"),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "mnt",
|
|
155
|
+
path: "/",
|
|
156
|
+
type: "dir",
|
|
157
|
+
file: new File([], "mnt"),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "opt",
|
|
161
|
+
path: "/",
|
|
162
|
+
type: "dir",
|
|
163
|
+
file: new File([], "opt"),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "proc",
|
|
167
|
+
path: "/",
|
|
168
|
+
type: "dir",
|
|
169
|
+
file: new File([], "proc"),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "run",
|
|
173
|
+
path: "/",
|
|
174
|
+
type: "dir",
|
|
175
|
+
file: new File([], "run"),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "sbin",
|
|
179
|
+
path: "/",
|
|
180
|
+
type: "dir",
|
|
181
|
+
file: new File([], "sbin"),
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "srv",
|
|
185
|
+
path: "/",
|
|
186
|
+
type: "dir",
|
|
187
|
+
file: new File([], "srv"),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "sys",
|
|
191
|
+
path: "/",
|
|
192
|
+
type: "dir",
|
|
193
|
+
file: new File([], "sys"),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "tmp",
|
|
197
|
+
path: "/",
|
|
198
|
+
type: "dir",
|
|
199
|
+
file: new File([], "tmp"),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "usr",
|
|
203
|
+
path: "/",
|
|
204
|
+
type: "dir",
|
|
205
|
+
file: new File([], "usr"),
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "var",
|
|
209
|
+
path: "/",
|
|
210
|
+
type: "dir",
|
|
211
|
+
file: new File([], "var"),
|
|
212
|
+
},
|
|
213
|
+
];
|
|
214
|
+
try {
|
|
215
|
+
for (const file of linuxInitFiles) {
|
|
216
|
+
await this.db.files.put(file);
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error("初始化文件失败", error);
|
|
220
|
+
throw new VVVFSError("InitFiles", "初始化文件失败" + error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
87
223
|
/**
|
|
88
224
|
* 重置虚拟文件系统
|
|
89
225
|
*/
|
|
@@ -92,7 +228,7 @@ class VVVFS {
|
|
|
92
228
|
await this.db.delete();
|
|
93
229
|
this.db = new Dexie(this.db.name) as VVVFSDatabase;
|
|
94
230
|
this.db.version(1).stores({
|
|
95
|
-
files: "++id, name, path, type, file",
|
|
231
|
+
files: "++id, name, path, type, file, [name+path+type]",
|
|
96
232
|
});
|
|
97
233
|
} catch (error) {
|
|
98
234
|
console.error("重置数据库失败", error);
|
|
@@ -396,7 +532,9 @@ class VVVFS {
|
|
|
396
532
|
async list(path: string) {
|
|
397
533
|
try {
|
|
398
534
|
const { name, parent } = parsePath(path);
|
|
399
|
-
return (await this.db.files.where({ path: parent }).toArray())
|
|
535
|
+
return (await this.db.files.where({ path: parent }).toArray())
|
|
536
|
+
.map((file) => file.name)
|
|
537
|
+
.filter((item) => item != "");
|
|
400
538
|
} catch (error) {
|
|
401
539
|
console.error("列出目录下的文件失败", error);
|
|
402
540
|
if (this.options.throwError) {
|
|
@@ -593,8 +731,9 @@ class VVVFS {
|
|
|
593
731
|
await that.list(joinPath(parent, file)),
|
|
594
732
|
)),
|
|
595
733
|
);
|
|
596
|
-
} else {
|
|
734
|
+
} else if (await that.isFile(joinPath(parent, file))) {
|
|
597
735
|
if (file.includes(query)) {
|
|
736
|
+
console.log(joinPath(parent, file));
|
|
598
737
|
result.push(joinPath(parent, file));
|
|
599
738
|
}
|
|
600
739
|
}
|
|
@@ -615,11 +754,12 @@ function parsePath(path: string) {
|
|
|
615
754
|
const oldParts = path.split("/");
|
|
616
755
|
const parts: string[] = [];
|
|
617
756
|
for (let i = 0; i < oldParts.length; i++) {
|
|
757
|
+
if (oldParts[i].length > 255) throw new VVVFSError("ParsePath", "文件名过长");
|
|
618
758
|
if (oldParts[i]) parts.push(oldParts[i]);
|
|
619
759
|
}
|
|
620
760
|
const name = parts.pop() || "";
|
|
621
761
|
const parent = "/" + parts.join("/");
|
|
622
|
-
if ((parent
|
|
762
|
+
if (joinPath(parent, name).length > 4096) throw new VVVFSError("ParsePath", "文件路径过长");
|
|
623
763
|
return { name, parent };
|
|
624
764
|
}
|
|
625
765
|
function joinPath(...paths: string[]) {
|
package/package.json
CHANGED
package/index.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
const { Dexie } = require('dexie');
|
|
2
|
-
// globalThis.Dexie = Dexie;
|
|
3
|
-
|
|
4
|
-
(async function () {
|
|
5
|
-
globalThis.VVVFS = class {
|
|
6
|
-
constructor(name = 'vvvfs', options = {}) {
|
|
7
|
-
this.options = options;
|
|
8
|
-
this.db = new Dexie(name);
|
|
9
|
-
this.db.version(1).stores({
|
|
10
|
-
files: '++id, name, path, type, file, mode, readonly, hidden'
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
async createUser(username, password) {
|
|
14
|
-
if (await this.exists(username)) throw new Error('User already exists');
|
|
15
|
-
await this.mkdirs("/home/" + username);
|
|
16
|
-
}
|
|
17
|
-
async write(path, file) {
|
|
18
|
-
if (!(file instanceof File)) throw new Error('file must be a File object');
|
|
19
|
-
if (path.endsWith('/')) throw new Error('Path cannot end with a slash');
|
|
20
|
-
if (!await this.exists(path)) await this.createFile(path);
|
|
21
|
-
const fileEntry = await this.db.files.where('path').equals(path).first();
|
|
22
|
-
if (fileEntry.readonly) throw new Error('File is read-only');
|
|
23
|
-
await this.db.files.update(path, { file });
|
|
24
|
-
}
|
|
25
|
-
async mkdirs(path) {
|
|
26
|
-
if (path.endsWith('/')) path = path.slice(0, -1);
|
|
27
|
-
const exists = await this.exists(path);
|
|
28
|
-
console.log('exists', exists);
|
|
29
|
-
if (exists) return;
|
|
30
|
-
const dirs = path.split('/').filter(Boolean);
|
|
31
|
-
console.log('mkdirs', dirs);
|
|
32
|
-
for (let i = 0; i < dirs.length; i++) {
|
|
33
|
-
if (dirs[i] === '') continue;
|
|
34
|
-
const dir = "/" + dirs.slice(0, i + 1).join('/');
|
|
35
|
-
if (await this.exists(dir)) continue;
|
|
36
|
-
console.log('mkdir', dir);
|
|
37
|
-
await this.db.files.add({
|
|
38
|
-
name: dirs[i],
|
|
39
|
-
path: dir,
|
|
40
|
-
type: 'directory',
|
|
41
|
-
file: new File([], dir),
|
|
42
|
-
mode: 0o755,
|
|
43
|
-
readonly: false,
|
|
44
|
-
hidden: false
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
async createFile(path) {
|
|
49
|
-
if (path.endsWith('/')) throw new Error('Path cannot end with a slash');
|
|
50
|
-
const dir = path.split('/').slice(0, -1).join('/');
|
|
51
|
-
console.log('createFile', path, dir);
|
|
52
|
-
if (dir) await this.mkdirs(dir);
|
|
53
|
-
const name = path.split('/').slice(-1)[0];
|
|
54
|
-
console.log('createFile', name);
|
|
55
|
-
await this.db.files.add({
|
|
56
|
-
name,
|
|
57
|
-
path,
|
|
58
|
-
type: 'file',
|
|
59
|
-
file: new File([], path),
|
|
60
|
-
mode: 0o644,
|
|
61
|
-
readonly: false,
|
|
62
|
-
hidden: false
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
async exists(path) {
|
|
66
|
-
const file = await this.db.files.where('path').equals(path).first();
|
|
67
|
-
return !!file;
|
|
68
|
-
}
|
|
69
|
-
async isDirectory(path) {
|
|
70
|
-
const file = await this.db.files.where('path').equals(path).first();
|
|
71
|
-
return file && file.type === 'directory';
|
|
72
|
-
}
|
|
73
|
-
async isFile(path) {
|
|
74
|
-
const file = await this.db.files.where('path').equals(path).first();
|
|
75
|
-
return file && file.type === 'file';
|
|
76
|
-
}
|
|
77
|
-
async read(path) {
|
|
78
|
-
const file = await this.db.files.where('path').equals(path).first();
|
|
79
|
-
return file;
|
|
80
|
-
}
|
|
81
|
-
async delete(path) {
|
|
82
|
-
await this.db.files.where('path').equals(path).delete();
|
|
83
|
-
}
|
|
84
|
-
async list(path) {
|
|
85
|
-
const files = await this.db.files.where('path').startsWith(path).toArray();
|
|
86
|
-
return files;
|
|
87
|
-
}
|
|
88
|
-
async chmod(path, mode) {
|
|
89
|
-
await this.db.files.update(path, { mode });
|
|
90
|
-
}
|
|
91
|
-
async rename(oldPath, newPath) {
|
|
92
|
-
const file = await this.db.files.where('path').equals(oldPath).first();
|
|
93
|
-
if (!file) throw new Error('File not found');
|
|
94
|
-
if (await this.exists(newPath)) throw new Error('File already exists');
|
|
95
|
-
await this.db.files.where('path').equals(oldPath).delete();
|
|
96
|
-
console.log('rename', oldPath, newPath);
|
|
97
|
-
const name = newPath.split('/').slice(-1)[0];
|
|
98
|
-
console.log('rename', name);
|
|
99
|
-
await this.db.files.add({
|
|
100
|
-
...file,
|
|
101
|
-
name,
|
|
102
|
-
path: newPath
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
async reset() {
|
|
106
|
-
await this.db.files.clear();
|
|
107
|
-
}
|
|
108
|
-
async move(oldPath, newPath) {
|
|
109
|
-
await this.rename(oldPath, newPath);
|
|
110
|
-
}
|
|
111
|
-
async copy(oldPath, newPath) {
|
|
112
|
-
const file = await this.db.files.where('path').equals(oldPath).first();
|
|
113
|
-
if (!file) throw new Error('File not found');
|
|
114
|
-
if (await this.exists(newPath)) throw new Error('File already exists');
|
|
115
|
-
await this.db.files.add({
|
|
116
|
-
...file,
|
|
117
|
-
path: newPath
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
async chown(path, owner, group) {
|
|
121
|
-
await this.db.files.update(path, { owner, group });
|
|
122
|
-
}
|
|
123
|
-
async stat(path) {
|
|
124
|
-
const file = await this.db.files.where('path').equals(path).first();
|
|
125
|
-
return file;
|
|
126
|
-
}
|
|
127
|
-
async isReadonly(path) {
|
|
128
|
-
const file = await this.db.files.where('path').equals(path).first();
|
|
129
|
-
return file && file.readonly;
|
|
130
|
-
}
|
|
131
|
-
async setReadonly(path, readonly) {
|
|
132
|
-
await this.db.files.update(path, { readonly });
|
|
133
|
-
}
|
|
134
|
-
async isHidden(path) {
|
|
135
|
-
const file = await this.db.files.where('path').equals(path).first();
|
|
136
|
-
return file && file.hidden;
|
|
137
|
-
}
|
|
138
|
-
async setHidden(path, hidden) {
|
|
139
|
-
await this.db.files.update(path, { hidden });
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
})();
|