@zenfs/core 0.0.12 → 0.2.0
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/dist/ApiError.d.ts +52 -15
- package/dist/ApiError.js +77 -50
- package/dist/FileIndex.d.ts +32 -35
- package/dist/FileIndex.js +93 -109
- package/dist/backends/AsyncMirror.d.ts +42 -43
- package/dist/backends/AsyncMirror.js +154 -147
- package/dist/backends/AsyncStore.d.ts +29 -28
- package/dist/backends/AsyncStore.js +375 -482
- package/dist/backends/FolderAdapter.js +8 -19
- package/dist/backends/InMemory.d.ts +16 -13
- package/dist/backends/InMemory.js +29 -14
- package/dist/backends/Locked.d.ts +8 -28
- package/dist/backends/Locked.js +74 -224
- package/dist/backends/OverlayFS.d.ts +26 -34
- package/dist/backends/OverlayFS.js +303 -511
- package/dist/backends/SyncStore.d.ts +54 -72
- package/dist/backends/SyncStore.js +159 -161
- package/dist/backends/backend.d.ts +45 -29
- package/dist/backends/backend.js +83 -13
- package/dist/backends/index.d.ts +6 -7
- package/dist/backends/index.js +5 -6
- package/dist/browser.min.js +21 -6
- package/dist/browser.min.js.map +4 -4
- package/dist/emulation/callbacks.d.ts +119 -113
- package/dist/emulation/callbacks.js +129 -92
- package/dist/emulation/constants.js +1 -1
- package/dist/emulation/dir.d.ts +55 -0
- package/dist/emulation/dir.js +104 -0
- package/dist/emulation/fs.d.ts +1 -2
- package/dist/emulation/fs.js +0 -1
- package/dist/emulation/index.d.ts +3 -0
- package/dist/emulation/index.js +3 -0
- package/dist/emulation/promises.d.ts +265 -145
- package/dist/emulation/promises.js +526 -383
- package/dist/emulation/shared.d.ts +20 -6
- package/dist/emulation/shared.js +22 -23
- package/dist/emulation/streams.d.ts +102 -0
- package/dist/emulation/streams.js +55 -0
- package/dist/emulation/sync.d.ts +98 -69
- package/dist/emulation/sync.js +280 -133
- package/dist/file.d.ts +175 -173
- package/dist/file.js +257 -273
- package/dist/filesystem.d.ts +71 -244
- package/dist/filesystem.js +67 -472
- package/dist/index.d.ts +7 -44
- package/dist/index.js +22 -75
- package/dist/inode.d.ts +37 -28
- package/dist/inode.js +123 -65
- package/dist/stats.d.ts +91 -36
- package/dist/stats.js +138 -110
- package/dist/utils.d.ts +26 -13
- package/dist/utils.js +79 -107
- package/package.json +7 -4
- package/readme.md +2 -40
package/dist/stats.js
CHANGED
|
@@ -10,35 +10,41 @@ export var FileType;
|
|
|
10
10
|
FileType[FileType["SYMLINK"] = S_IFLNK] = "SYMLINK";
|
|
11
11
|
})(FileType = FileType || (FileType = {}));
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* Attribute descriptions are from `man 2 stat'
|
|
16
|
-
* @see http://nodejs.org/api/fs.html#fs_class_fs_stats
|
|
17
|
-
* @see http://man7.org/linux/man-pages/man2/stat.2.html
|
|
13
|
+
* Common code used by both Stats and BigIntStats
|
|
18
14
|
*/
|
|
19
|
-
export class
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const size = view.getUint32(0, true), mode = view.getUint32(4, true), atime = view.getFloat64(8, true), mtime = view.getFloat64(16, true), ctime = view.getFloat64(24, true), uid = view.getUint32(32, true), gid = view.getUint32(36, true);
|
|
23
|
-
return new Stats(mode & S_IFMT, size, mode & ~S_IFMT, atime, mtime, ctime, uid, gid);
|
|
15
|
+
export class StatsCommon {
|
|
16
|
+
get _typename() {
|
|
17
|
+
return this._isBigint ? 'bigint' : 'number';
|
|
24
18
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return
|
|
19
|
+
get _typename_inverse() {
|
|
20
|
+
return this._isBigint ? 'number' : 'bigint';
|
|
21
|
+
}
|
|
22
|
+
_convert(arg) {
|
|
23
|
+
return (this._isBigint ? BigInt(arg) : Number(arg));
|
|
30
24
|
}
|
|
31
25
|
get atime() {
|
|
32
|
-
return new Date(this.atimeMs);
|
|
26
|
+
return new Date(Number(this.atimeMs));
|
|
27
|
+
}
|
|
28
|
+
set atime(value) {
|
|
29
|
+
this.atimeMs = this._convert(value.getTime());
|
|
33
30
|
}
|
|
34
31
|
get mtime() {
|
|
35
|
-
return new Date(this.mtimeMs);
|
|
32
|
+
return new Date(Number(this.mtimeMs));
|
|
33
|
+
}
|
|
34
|
+
set mtime(value) {
|
|
35
|
+
this.mtimeMs = this._convert(value.getTime());
|
|
36
36
|
}
|
|
37
37
|
get ctime() {
|
|
38
|
-
return new Date(this.ctimeMs);
|
|
38
|
+
return new Date(Number(this.ctimeMs));
|
|
39
|
+
}
|
|
40
|
+
set ctime(value) {
|
|
41
|
+
this.ctimeMs = this._convert(value.getTime());
|
|
39
42
|
}
|
|
40
43
|
get birthtime() {
|
|
41
|
-
return new Date(this.birthtimeMs);
|
|
44
|
+
return new Date(Number(this.birthtimeMs));
|
|
45
|
+
}
|
|
46
|
+
set birthtime(value) {
|
|
47
|
+
this.birthtimeMs = this._convert(value.getTime());
|
|
42
48
|
}
|
|
43
49
|
/**
|
|
44
50
|
* Provides information about a particular entry in the file system.
|
|
@@ -53,113 +59,106 @@ export class Stats {
|
|
|
53
59
|
* @param gid the id of the group that owns the file
|
|
54
60
|
* @param birthtimeMs time of file creation, in milliseconds since epoch
|
|
55
61
|
*/
|
|
56
|
-
constructor(itemType, size, mode, atimeMs, mtimeMs, ctimeMs, uid, gid, birthtimeMs) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
this.
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
this.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
62
|
+
constructor(itemType = FileType.FILE, size = -1, mode, atimeMs, mtimeMs, ctimeMs, uid, gid, birthtimeMs) {
|
|
63
|
+
/**
|
|
64
|
+
* ID of device containing file
|
|
65
|
+
*/
|
|
66
|
+
this.dev = this._convert(0);
|
|
67
|
+
/**
|
|
68
|
+
* inode number
|
|
69
|
+
*/
|
|
70
|
+
this.ino = this._convert(0);
|
|
71
|
+
/**
|
|
72
|
+
* device ID (if special file)
|
|
73
|
+
*/
|
|
74
|
+
this.rdev = this._convert(0);
|
|
75
|
+
/**
|
|
76
|
+
* number of hard links
|
|
77
|
+
*/
|
|
78
|
+
this.nlink = this._convert(1);
|
|
79
|
+
/**
|
|
80
|
+
* blocksize for file system I/O
|
|
81
|
+
*/
|
|
82
|
+
this.blksize = this._convert(4096);
|
|
83
|
+
/**
|
|
84
|
+
* user ID of owner
|
|
85
|
+
*/
|
|
86
|
+
this.uid = this._convert(0);
|
|
87
|
+
/**
|
|
88
|
+
* group ID of owner
|
|
89
|
+
*/
|
|
90
|
+
this.gid = this._convert(0);
|
|
91
|
+
/**
|
|
92
|
+
* Some file systems stash data on stats objects.
|
|
93
|
+
*/
|
|
72
94
|
this.fileData = null;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
const currentTime = Date.now();
|
|
96
|
+
const resolveT = (v, def) => (typeof v == this._typename ? v : this._convert(typeof v == this._typename_inverse ? v : def));
|
|
97
|
+
this.atimeMs = resolveT(atimeMs, currentTime);
|
|
98
|
+
this.mtimeMs = resolveT(mtimeMs, currentTime);
|
|
99
|
+
this.ctimeMs = resolveT(ctimeMs, currentTime);
|
|
100
|
+
this.birthtimeMs = resolveT(birthtimeMs, currentTime);
|
|
101
|
+
this.uid = resolveT(uid, 0);
|
|
102
|
+
this.gid = resolveT(gid, 0);
|
|
103
|
+
this.size = this._convert(size);
|
|
104
|
+
if (mode) {
|
|
105
|
+
this.mode = this._convert(mode);
|
|
84
106
|
}
|
|
85
|
-
|
|
86
|
-
if (!currentTime) {
|
|
87
|
-
currentTime = Date.now();
|
|
88
|
-
}
|
|
89
|
-
ctimeMs = currentTime;
|
|
90
|
-
}
|
|
91
|
-
if (typeof birthtimeMs !== 'number') {
|
|
92
|
-
if (!currentTime) {
|
|
93
|
-
currentTime = Date.now();
|
|
94
|
-
}
|
|
95
|
-
birthtimeMs = currentTime;
|
|
96
|
-
}
|
|
97
|
-
if (typeof uid !== 'number') {
|
|
98
|
-
uid = 0;
|
|
99
|
-
}
|
|
100
|
-
if (typeof gid !== 'number') {
|
|
101
|
-
gid = 0;
|
|
102
|
-
}
|
|
103
|
-
this.atimeMs = atimeMs;
|
|
104
|
-
this.ctimeMs = ctimeMs;
|
|
105
|
-
this.mtimeMs = mtimeMs;
|
|
106
|
-
this.birthtimeMs = birthtimeMs;
|
|
107
|
-
if (!mode) {
|
|
107
|
+
else {
|
|
108
108
|
switch (itemType) {
|
|
109
109
|
case FileType.FILE:
|
|
110
|
-
this.mode = 0o644;
|
|
110
|
+
this.mode = this._convert(0o644);
|
|
111
111
|
break;
|
|
112
112
|
case FileType.DIRECTORY:
|
|
113
113
|
default:
|
|
114
|
-
this.mode = 0o777;
|
|
114
|
+
this.mode = this._convert(0o777);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
else {
|
|
118
|
-
this.mode = mode;
|
|
119
|
-
}
|
|
120
117
|
// number of 512B blocks allocated
|
|
121
|
-
this.blocks = Math.ceil(size / 512);
|
|
122
|
-
// Check if mode also includes top-most bits, which indicate the file's
|
|
123
|
-
// type.
|
|
118
|
+
this.blocks = this._convert(Math.ceil(Number(size) / 512));
|
|
119
|
+
// Check if mode also includes top-most bits, which indicate the file's type.
|
|
124
120
|
if ((this.mode & S_IFMT) == 0) {
|
|
125
|
-
this.mode
|
|
121
|
+
this.mode = (this.mode | this._convert(itemType));
|
|
126
122
|
}
|
|
127
123
|
}
|
|
128
|
-
serialize() {
|
|
129
|
-
const data = new Uint8Array(32), view = new DataView(data.buffer);
|
|
130
|
-
view.setUint32(0, this.size, true);
|
|
131
|
-
view.setUint32(4, this.mode, true);
|
|
132
|
-
view.setFloat64(8, this.atime.getTime(), true);
|
|
133
|
-
view.setFloat64(16, this.mtime.getTime(), true);
|
|
134
|
-
view.setFloat64(24, this.ctime.getTime(), true);
|
|
135
|
-
view.setUint32(32, this.uid, true);
|
|
136
|
-
view.setUint32(36, this.gid, true);
|
|
137
|
-
return data;
|
|
138
|
-
}
|
|
139
124
|
/**
|
|
140
|
-
* @
|
|
125
|
+
* @returns true if this item is a file.
|
|
141
126
|
*/
|
|
142
127
|
isFile() {
|
|
143
128
|
return (this.mode & S_IFMT) === S_IFREG;
|
|
144
129
|
}
|
|
145
130
|
/**
|
|
146
|
-
* @
|
|
131
|
+
* @returns True if this item is a directory.
|
|
147
132
|
*/
|
|
148
133
|
isDirectory() {
|
|
149
134
|
return (this.mode & S_IFMT) === S_IFDIR;
|
|
150
135
|
}
|
|
151
136
|
/**
|
|
152
|
-
* @
|
|
137
|
+
* @returns true if this item is a symbolic link
|
|
153
138
|
*/
|
|
154
139
|
isSymbolicLink() {
|
|
155
140
|
return (this.mode & S_IFMT) === S_IFLNK;
|
|
156
141
|
}
|
|
142
|
+
// Currently unsupported
|
|
143
|
+
isSocket() {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
isBlockDevice() {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
isCharacterDevice() {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
isFIFO() {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
157
155
|
/**
|
|
158
156
|
* Checks if a given user/group has access to this item
|
|
159
157
|
* @param mode The request access as 4 bits (unused, read, write, execute)
|
|
160
158
|
* @param uid The requesting UID
|
|
161
159
|
* @param gid The requesting GID
|
|
162
|
-
* @returns
|
|
160
|
+
* @returns True if the request has access, false if the request does not
|
|
161
|
+
* @internal
|
|
163
162
|
*/
|
|
164
163
|
hasAccess(mode, cred) {
|
|
165
164
|
if (cred.euid === 0 || cred.egid === 0) {
|
|
@@ -181,46 +180,75 @@ export class Stats {
|
|
|
181
180
|
/*
|
|
182
181
|
Result = 0b0xxx (read, write, execute)
|
|
183
182
|
If any bits are set that means the request does not have that permission.
|
|
184
|
-
|
|
183
|
+
*/
|
|
185
184
|
const result = uMode & gMode & wMode;
|
|
186
185
|
return !result;
|
|
187
186
|
}
|
|
188
187
|
/**
|
|
189
188
|
* Convert the current stats object into a cred object
|
|
189
|
+
* @internal
|
|
190
190
|
*/
|
|
191
|
-
getCred(uid = this.uid, gid = this.gid) {
|
|
192
|
-
return new Cred(uid, gid, this.uid, this.gid, uid, gid);
|
|
191
|
+
getCred(uid = Number(this.uid), gid = Number(this.gid)) {
|
|
192
|
+
return new Cred(uid, gid, Number(this.uid), Number(this.gid), uid, gid);
|
|
193
193
|
}
|
|
194
194
|
/**
|
|
195
195
|
* Change the mode of the file. We use this helper function to prevent messing
|
|
196
196
|
* up the type of the file, which is encoded in mode.
|
|
197
|
+
* @internal
|
|
197
198
|
*/
|
|
198
199
|
chmod(mode) {
|
|
199
|
-
this.mode = (this.mode & S_IFMT) | mode;
|
|
200
|
+
this.mode = this._convert((this.mode & S_IFMT) | mode);
|
|
200
201
|
}
|
|
201
202
|
/**
|
|
202
203
|
* Change the owner user/group of the file.
|
|
203
204
|
* This function makes sure it is a valid UID/GID (that is, a 32 unsigned int)
|
|
205
|
+
* @internal
|
|
204
206
|
*/
|
|
205
207
|
chown(uid, gid) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
uid = Number(uid);
|
|
209
|
+
gid = Number(gid);
|
|
210
|
+
if (!isNaN(uid) && 0 <= uid && uid < 2 ** 32) {
|
|
211
|
+
this.uid = this._convert(uid);
|
|
208
212
|
}
|
|
209
|
-
if (!isNaN(
|
|
210
|
-
this.gid = gid;
|
|
213
|
+
if (!isNaN(gid) && 0 <= gid && gid < 2 ** 32) {
|
|
214
|
+
this.gid = this._convert(gid);
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Implementation of Node's `Stats`.
|
|
220
|
+
*
|
|
221
|
+
* Attribute descriptions are from `man 2 stat'
|
|
222
|
+
* @see http://nodejs.org/api/fs.html#fs_class_fs_stats
|
|
223
|
+
* @see http://man7.org/linux/man-pages/man2/stat.2.html
|
|
224
|
+
*/
|
|
225
|
+
export class Stats extends StatsCommon {
|
|
226
|
+
constructor() {
|
|
227
|
+
super(...arguments);
|
|
228
|
+
this._isBigint = false;
|
|
216
229
|
}
|
|
217
|
-
|
|
218
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Clones the stats object.
|
|
232
|
+
*/
|
|
233
|
+
static clone(s) {
|
|
234
|
+
return new Stats(s.mode & S_IFMT, s.size, s.mode & ~S_IFMT, s.atimeMs, s.mtimeMs, s.ctimeMs, s.uid, s.gid, s.birthtimeMs);
|
|
219
235
|
}
|
|
220
|
-
|
|
221
|
-
|
|
236
|
+
}
|
|
237
|
+
Stats;
|
|
238
|
+
/**
|
|
239
|
+
* Stats with bigint
|
|
240
|
+
* @todo Implement with bigint instead of wrapping Stats
|
|
241
|
+
*/
|
|
242
|
+
export class BigIntStats extends StatsCommon {
|
|
243
|
+
constructor() {
|
|
244
|
+
super(...arguments);
|
|
245
|
+
this._isBigint = true;
|
|
222
246
|
}
|
|
223
|
-
|
|
224
|
-
|
|
247
|
+
/**
|
|
248
|
+
* Clone a stats object.
|
|
249
|
+
*/
|
|
250
|
+
static clone(s) {
|
|
251
|
+
return new BigIntStats(Number(s.mode) & S_IFMT, BigInt(s.size), BigInt(s.mode) & BigInt(~S_IFMT), BigInt(s.atimeMs), BigInt(s.mtimeMs), BigInt(s.ctimeMs), BigInt(s.uid), BigInt(s.gid), BigInt(s.birthtimeMs));
|
|
225
252
|
}
|
|
226
253
|
}
|
|
254
|
+
BigIntStats;
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
3
|
/**
|
|
3
4
|
* Grab bag of utility functions used across the code.
|
|
4
5
|
*/
|
|
5
6
|
import { FileSystem } from './filesystem.js';
|
|
6
7
|
import { Cred } from './cred.js';
|
|
7
|
-
import type {
|
|
8
|
-
|
|
8
|
+
import type { TextDecoder as _TextDecoder, TextEncoder as _TextEncoder } from 'node:util';
|
|
9
|
+
declare global {
|
|
10
|
+
function setImmediate(callback: () => unknown): void;
|
|
11
|
+
function atob(data: string): string;
|
|
12
|
+
function btoa(data: string): string;
|
|
13
|
+
const TextDecoder: typeof _TextDecoder;
|
|
14
|
+
const TextEncoder: typeof _TextEncoder;
|
|
15
|
+
}
|
|
9
16
|
/**
|
|
10
17
|
* Synchronous recursive makedir.
|
|
11
18
|
* @internal
|
|
12
19
|
*/
|
|
13
20
|
export declare function mkdirpSync(p: string, mode: number, cred: Cred, fs: FileSystem): void;
|
|
14
21
|
/**
|
|
15
|
-
*
|
|
22
|
+
* Calculates levenshtein distance.
|
|
16
23
|
* @internal
|
|
17
24
|
*/
|
|
18
|
-
export declare function
|
|
25
|
+
export declare function levenshtein(a: string, b: string): number;
|
|
19
26
|
/** Waits n ms. */
|
|
20
27
|
export declare function wait(ms: number): Promise<void>;
|
|
21
28
|
/**
|
|
@@ -26,18 +33,24 @@ export declare function toPromise(fn: (...fnArgs: unknown[]) => unknown): (...ar
|
|
|
26
33
|
/**
|
|
27
34
|
* @internal
|
|
28
35
|
*/
|
|
29
|
-
export declare const setImmediate: (
|
|
36
|
+
export declare const setImmediate: typeof globalThis.setImmediate | ((cb: any) => NodeJS.Timeout);
|
|
37
|
+
/**
|
|
38
|
+
* Encodes a string into a buffer
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export declare function encode(input: string, encoding?: BufferEncoding): Uint8Array;
|
|
42
|
+
/**
|
|
43
|
+
* Decodes a string from a buffer
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
export declare function decode(input?: Uint8Array, encoding?: BufferEncoding): string;
|
|
30
47
|
/**
|
|
48
|
+
* Decodes a directory listing
|
|
31
49
|
* @internal
|
|
32
50
|
*/
|
|
33
|
-
export declare
|
|
34
|
-
declare const textEncoder: TextEncoderType;
|
|
35
|
-
export declare const encode: typeof textEncoder.encode;
|
|
36
|
-
declare const textDecoder: TextDecoderType;
|
|
37
|
-
export declare const decode: typeof textDecoder.decode;
|
|
51
|
+
export declare function decodeDirListing(data: Uint8Array): Record<string, bigint>;
|
|
38
52
|
/**
|
|
39
|
-
*
|
|
53
|
+
* Encodes a directory listing
|
|
40
54
|
* @internal
|
|
41
55
|
*/
|
|
42
|
-
export declare function
|
|
43
|
-
export {};
|
|
56
|
+
export declare function encodeDirListing(data: Record<string, bigint>): Uint8Array;
|
package/dist/utils.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
|
-
import { ErrorCode, ApiError } from './ApiError.js';
|
|
1
|
+
import { ApiError, ErrorCode } from './ApiError.js';
|
|
11
2
|
import * as path from './emulation/path.js';
|
|
12
3
|
/**
|
|
13
4
|
* Synchronous recursive makedir.
|
|
@@ -28,10 +19,9 @@ function _min(d0, d1, d2, bx, ay) {
|
|
|
28
19
|
}
|
|
29
20
|
/**
|
|
30
21
|
* Calculates levenshtein distance.
|
|
31
|
-
* @
|
|
32
|
-
* @param b
|
|
22
|
+
* @internal
|
|
33
23
|
*/
|
|
34
|
-
function levenshtein(a, b) {
|
|
24
|
+
export function levenshtein(a, b) {
|
|
35
25
|
if (a === b) {
|
|
36
26
|
return 0;
|
|
37
27
|
}
|
|
@@ -97,88 +87,6 @@ function levenshtein(a, b) {
|
|
|
97
87
|
}
|
|
98
88
|
return dd;
|
|
99
89
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Checks that the given options object is valid for the file system options.
|
|
102
|
-
* @internal
|
|
103
|
-
*/
|
|
104
|
-
export function checkOptions(backend, opts) {
|
|
105
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
106
|
-
const optsInfo = backend.Options;
|
|
107
|
-
const fsName = backend.Name;
|
|
108
|
-
let pendingValidators = 0;
|
|
109
|
-
let callbackCalled = false;
|
|
110
|
-
let loopEnded = false;
|
|
111
|
-
// Check for required options.
|
|
112
|
-
for (const optName in optsInfo) {
|
|
113
|
-
if (Object.prototype.hasOwnProperty.call(optsInfo, optName)) {
|
|
114
|
-
const opt = optsInfo[optName];
|
|
115
|
-
const providedValue = opts && opts[optName];
|
|
116
|
-
if (providedValue === undefined || providedValue === null) {
|
|
117
|
-
if (!opt.optional) {
|
|
118
|
-
// Required option, not provided.
|
|
119
|
-
// Any incorrect options provided? Which ones are close to the provided one?
|
|
120
|
-
// (edit distance 5 === close)
|
|
121
|
-
const incorrectOptions = Object.keys(opts)
|
|
122
|
-
.filter(o => !(o in optsInfo))
|
|
123
|
-
.map((a) => {
|
|
124
|
-
return { str: a, distance: levenshtein(optName, a) };
|
|
125
|
-
})
|
|
126
|
-
.filter(o => o.distance < 5)
|
|
127
|
-
.sort((a, b) => a.distance - b.distance);
|
|
128
|
-
// Validators may be synchronous.
|
|
129
|
-
if (callbackCalled) {
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
callbackCalled = true;
|
|
133
|
-
throw new ApiError(ErrorCode.EINVAL, `[${fsName}] Required option '${optName}' not provided.${incorrectOptions.length > 0 ? ` You provided unrecognized option '${incorrectOptions[0].str}'; perhaps you meant to type '${optName}'.` : ''}\nOption description: ${opt.description}`);
|
|
134
|
-
}
|
|
135
|
-
// Else: Optional option, not provided. That is OK.
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
// Option provided! Check type.
|
|
139
|
-
let typeMatches = false;
|
|
140
|
-
if (Array.isArray(opt.type)) {
|
|
141
|
-
typeMatches = opt.type.indexOf(typeof providedValue) !== -1;
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
typeMatches = typeof providedValue === opt.type;
|
|
145
|
-
}
|
|
146
|
-
if (!typeMatches) {
|
|
147
|
-
// Validators may be synchronous.
|
|
148
|
-
if (callbackCalled) {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
callbackCalled = true;
|
|
152
|
-
throw new ApiError(ErrorCode.EINVAL, `[${fsName}] Value provided for option ${optName} is not the proper type. Expected ${Array.isArray(opt.type) ? `one of {${opt.type.join(', ')}}` : opt.type}, but received ${typeof providedValue}\nOption description: ${opt.description}`);
|
|
153
|
-
}
|
|
154
|
-
else if (opt.validator) {
|
|
155
|
-
pendingValidators++;
|
|
156
|
-
try {
|
|
157
|
-
yield opt.validator(providedValue);
|
|
158
|
-
}
|
|
159
|
-
catch (e) {
|
|
160
|
-
if (!callbackCalled) {
|
|
161
|
-
if (e) {
|
|
162
|
-
callbackCalled = true;
|
|
163
|
-
throw e;
|
|
164
|
-
}
|
|
165
|
-
pendingValidators--;
|
|
166
|
-
if (pendingValidators === 0 && loopEnded) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// Otherwise: All good!
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
loopEnded = true;
|
|
177
|
-
if (pendingValidators === 0 && !callbackCalled) {
|
|
178
|
-
return;
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
90
|
/** Waits n ms. */
|
|
183
91
|
export function wait(ms) {
|
|
184
92
|
return new Promise(resolve => {
|
|
@@ -209,22 +117,86 @@ export function toPromise(fn) {
|
|
|
209
117
|
*/
|
|
210
118
|
export const setImmediate = typeof globalThis.setImmediate == 'function' ? globalThis.setImmediate : cb => setTimeout(cb, 0);
|
|
211
119
|
/**
|
|
120
|
+
* Encodes a string into a buffer
|
|
212
121
|
* @internal
|
|
213
122
|
*/
|
|
214
|
-
export
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
123
|
+
export function encode(input, encoding = 'utf8') {
|
|
124
|
+
switch (encoding) {
|
|
125
|
+
case 'ascii':
|
|
126
|
+
return new TextEncoder().encode(input).map(v => v & 0x7f);
|
|
127
|
+
case 'latin1':
|
|
128
|
+
case 'binary':
|
|
129
|
+
case 'utf8':
|
|
130
|
+
case 'utf-8':
|
|
131
|
+
case 'base64':
|
|
132
|
+
case 'base64url':
|
|
133
|
+
case 'hex':
|
|
134
|
+
return new TextEncoder().encode(input);
|
|
135
|
+
case 'utf16le':
|
|
136
|
+
case 'ucs2':
|
|
137
|
+
case 'ucs-2':
|
|
138
|
+
return new TextEncoder().encode(input).slice(0, -1);
|
|
139
|
+
default:
|
|
140
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid encoding: ' + encoding);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Decodes a string from a buffer
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
147
|
+
export function decode(input, encoding = 'utf8') {
|
|
148
|
+
switch (encoding) {
|
|
149
|
+
case 'ascii':
|
|
150
|
+
case 'utf8':
|
|
151
|
+
case 'utf-8':
|
|
152
|
+
return new TextDecoder().decode(input);
|
|
153
|
+
case 'latin1':
|
|
154
|
+
case 'binary':
|
|
155
|
+
return new TextDecoder('latin1').decode(input);
|
|
156
|
+
case 'utf16le':
|
|
157
|
+
case 'ucs2':
|
|
158
|
+
case 'ucs-2':
|
|
159
|
+
let utf16leString = '';
|
|
160
|
+
for (let i = 0; i < input.length; i += 2) {
|
|
161
|
+
const code = input[i] | (input[i + 1] << 8);
|
|
162
|
+
utf16leString += String.fromCharCode(code);
|
|
163
|
+
}
|
|
164
|
+
return utf16leString;
|
|
165
|
+
case 'base64':
|
|
166
|
+
return btoa(Array.from(input)
|
|
167
|
+
.map(v => String.fromCharCode(v))
|
|
168
|
+
.join(''));
|
|
169
|
+
case 'base64url':
|
|
170
|
+
return decode(input, 'base64').replace('/', '_').replace('+', '-');
|
|
171
|
+
case 'hex':
|
|
172
|
+
return Array.from(input)
|
|
173
|
+
.map(e => e.toString(16))
|
|
174
|
+
.join('');
|
|
175
|
+
default:
|
|
176
|
+
throw new ApiError(ErrorCode.EINVAL, 'Invalid encoding: ' + encoding);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
219
179
|
/**
|
|
220
|
-
*
|
|
180
|
+
* Decodes a directory listing
|
|
221
181
|
* @internal
|
|
222
182
|
*/
|
|
223
|
-
export function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return v
|
|
183
|
+
export function decodeDirListing(data) {
|
|
184
|
+
return JSON.parse(decode(data), (k, v) => {
|
|
185
|
+
if (k == '') {
|
|
186
|
+
return v;
|
|
187
|
+
}
|
|
188
|
+
return BigInt(v);
|
|
229
189
|
});
|
|
230
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Encodes a directory listing
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
export function encodeDirListing(data) {
|
|
196
|
+
return encode(JSON.stringify(data, (k, v) => {
|
|
197
|
+
if (k == '') {
|
|
198
|
+
return v;
|
|
199
|
+
}
|
|
200
|
+
return v.toString();
|
|
201
|
+
}));
|
|
202
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A filesystem in your browser",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist",
|
|
@@ -36,14 +36,17 @@
|
|
|
36
36
|
"scripts": {
|
|
37
37
|
"format": "prettier --write src test",
|
|
38
38
|
"format:check": "prettier --check src test",
|
|
39
|
-
"lint": "eslint src test",
|
|
39
|
+
"lint": "eslint src test && tsc -p tsconfig.json --noEmit",
|
|
40
40
|
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules npx jest",
|
|
41
|
-
"build": "node scripts/build.
|
|
41
|
+
"build": "node scripts/build.js",
|
|
42
42
|
"build:docs": "typedoc --out docs --name ZenFS src/index.ts",
|
|
43
|
+
"dev": "node scripts/build.js --watch",
|
|
43
44
|
"prepublishOnly": "npm run build"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@types/node": "^14.0.0"
|
|
47
|
+
"@types/node": "^14.0.0",
|
|
48
|
+
"@types/readable-stream": "^4.0.10",
|
|
49
|
+
"readable-stream": "^4.5.2"
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
49
52
|
"@jest/globals": "^29.5.0",
|