@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.
Files changed (54) hide show
  1. package/dist/ApiError.d.ts +52 -15
  2. package/dist/ApiError.js +77 -50
  3. package/dist/FileIndex.d.ts +32 -35
  4. package/dist/FileIndex.js +93 -109
  5. package/dist/backends/AsyncMirror.d.ts +42 -43
  6. package/dist/backends/AsyncMirror.js +154 -147
  7. package/dist/backends/AsyncStore.d.ts +29 -28
  8. package/dist/backends/AsyncStore.js +375 -482
  9. package/dist/backends/FolderAdapter.js +8 -19
  10. package/dist/backends/InMemory.d.ts +16 -13
  11. package/dist/backends/InMemory.js +29 -14
  12. package/dist/backends/Locked.d.ts +8 -28
  13. package/dist/backends/Locked.js +74 -224
  14. package/dist/backends/OverlayFS.d.ts +26 -34
  15. package/dist/backends/OverlayFS.js +303 -511
  16. package/dist/backends/SyncStore.d.ts +54 -72
  17. package/dist/backends/SyncStore.js +159 -161
  18. package/dist/backends/backend.d.ts +45 -29
  19. package/dist/backends/backend.js +83 -13
  20. package/dist/backends/index.d.ts +6 -7
  21. package/dist/backends/index.js +5 -6
  22. package/dist/browser.min.js +21 -6
  23. package/dist/browser.min.js.map +4 -4
  24. package/dist/emulation/callbacks.d.ts +119 -113
  25. package/dist/emulation/callbacks.js +129 -92
  26. package/dist/emulation/constants.js +1 -1
  27. package/dist/emulation/dir.d.ts +55 -0
  28. package/dist/emulation/dir.js +104 -0
  29. package/dist/emulation/fs.d.ts +1 -2
  30. package/dist/emulation/fs.js +0 -1
  31. package/dist/emulation/index.d.ts +3 -0
  32. package/dist/emulation/index.js +3 -0
  33. package/dist/emulation/promises.d.ts +265 -145
  34. package/dist/emulation/promises.js +526 -383
  35. package/dist/emulation/shared.d.ts +20 -6
  36. package/dist/emulation/shared.js +22 -23
  37. package/dist/emulation/streams.d.ts +102 -0
  38. package/dist/emulation/streams.js +55 -0
  39. package/dist/emulation/sync.d.ts +98 -69
  40. package/dist/emulation/sync.js +280 -133
  41. package/dist/file.d.ts +175 -173
  42. package/dist/file.js +257 -273
  43. package/dist/filesystem.d.ts +71 -244
  44. package/dist/filesystem.js +67 -472
  45. package/dist/index.d.ts +7 -44
  46. package/dist/index.js +22 -75
  47. package/dist/inode.d.ts +37 -28
  48. package/dist/inode.js +123 -65
  49. package/dist/stats.d.ts +91 -36
  50. package/dist/stats.js +138 -110
  51. package/dist/utils.d.ts +26 -13
  52. package/dist/utils.js +79 -107
  53. package/package.json +7 -4
  54. 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
- * Implementation of Node's `Stats`.
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 Stats {
20
- static Deserialize(data) {
21
- const view = new DataView('buffer' in data ? data.buffer : data);
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
- * Clones the stats object.
27
- */
28
- static clone(s) {
29
- 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);
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
- // ID of device containing file
58
- this.dev = 0;
59
- // inode number
60
- this.ino = 0;
61
- // device ID (if special file)
62
- this.rdev = 0;
63
- // number of hard links
64
- this.nlink = 1;
65
- // blocksize for file system I/O
66
- this.blksize = 4096;
67
- // user ID of owner
68
- this.uid = 0;
69
- // group ID of owner
70
- this.gid = 0;
71
- // Some file systems stash data on stats objects.
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
- this.size = size;
74
- let currentTime = 0;
75
- if (typeof atimeMs !== 'number') {
76
- currentTime = Date.now();
77
- atimeMs = currentTime;
78
- }
79
- if (typeof mtimeMs !== 'number') {
80
- if (!currentTime) {
81
- currentTime = Date.now();
82
- }
83
- mtimeMs = currentTime;
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
- if (typeof ctimeMs !== 'number') {
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 |= itemType;
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
- * @return [Boolean] True if this item is a file.
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
- * @return [Boolean] True if this item is a directory.
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
- * @return [Boolean] True if this item is a symbolic link (only valid through lstat)
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 [Boolean] True if the request has access, false if the request does not
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
- if (!isNaN(+uid) && 0 <= +uid && +uid < Math.pow(2, 32)) {
207
- this.uid = uid;
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(+gid) && 0 <= +gid && +gid < Math.pow(2, 32)) {
210
- this.gid = gid;
213
+ if (!isNaN(gid) && 0 <= gid && gid < 2 ** 32) {
214
+ this.gid = this._convert(gid);
211
215
  }
212
216
  }
213
- // We don't support the following types of files.
214
- isSocket() {
215
- return false;
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
- isBlockDevice() {
218
- return false;
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
- isCharacterDevice() {
221
- return false;
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
- isFIFO() {
224
- return false;
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 { BaseBackendConstructor } from './backends/backend.js';
8
- import type { TextEncoder as TextEncoderType, TextDecoder as TextDecoderType } from 'node:util';
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
- * Checks that the given options object is valid for the file system options.
22
+ * Calculates levenshtein distance.
16
23
  * @internal
17
24
  */
18
- export declare function checkOptions(backend: BaseBackendConstructor, opts: object): Promise<void>;
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: (callback: () => unknown) => void;
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 const ROOT_NODE_ID: string;
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
- * Generates a random ID.
53
+ * Encodes a directory listing
40
54
  * @internal
41
55
  */
42
- export declare function randomUUID(): string;
43
- export {};
56
+ export declare function encodeDirListing(data: Record<string, bigint>): Uint8Array;
package/dist/utils.js CHANGED
@@ -1,13 +1,4 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
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
- * @param a
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 const ROOT_NODE_ID = '/';
215
- const textEncoder = new globalThis.TextEncoder();
216
- export const encode = textEncoder.encode.bind(textEncoder);
217
- const textDecoder = new globalThis.TextDecoder();
218
- export const decode = textDecoder.decode.bind(textDecoder);
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
- * Generates a random ID.
180
+ * Decodes a directory listing
221
181
  * @internal
222
182
  */
223
- export function randomUUID() {
224
- // From http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
225
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
226
- const r = (Math.random() * 16) | 0;
227
- const v = c === 'x' ? r : (r & 0x3) | 0x8;
228
- return v.toString(16);
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.12",
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.mjs",
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",