@zenfs/core 0.16.0 → 0.16.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.
Files changed (46) hide show
  1. package/dist/backends/fetch.js +5 -4
  2. package/dist/backends/index/fs.js +12 -5
  3. package/dist/backends/overlay.d.ts +1 -1
  4. package/dist/backends/overlay.js +9 -9
  5. package/dist/backends/port/fs.d.ts +22 -0
  6. package/dist/backends/port/fs.js +12 -1
  7. package/dist/backends/port/rpc.d.ts +2 -0
  8. package/dist/backends/port/rpc.js +13 -1
  9. package/dist/backends/store/fs.js +1 -1
  10. package/dist/browser.min.js +4 -4
  11. package/dist/browser.min.js.map +4 -4
  12. package/dist/config.d.ts +1 -2
  13. package/dist/config.js +2 -2
  14. package/dist/emulation/async.d.ts +4 -5
  15. package/dist/emulation/async.js +9 -9
  16. package/dist/emulation/dir.js +1 -1
  17. package/dist/emulation/promises.js +10 -5
  18. package/dist/emulation/sync.d.ts +1 -0
  19. package/dist/emulation/sync.js +1 -1
  20. package/dist/file.js +6 -6
  21. package/dist/filesystem.js +1 -1
  22. package/dist/inode.d.ts +1 -1
  23. package/dist/inode.js +54 -37
  24. package/dist/polyfills.js +1 -0
  25. package/dist/utils.d.ts +1 -1
  26. package/dist/utils.js +1 -1
  27. package/eslint.shared.js +53 -0
  28. package/package.json +16 -6
  29. package/src/backends/fetch.ts +6 -5
  30. package/src/backends/index/fs.ts +12 -5
  31. package/src/backends/index/index.ts +1 -1
  32. package/src/backends/overlay.ts +9 -9
  33. package/src/backends/port/fs.ts +18 -3
  34. package/src/backends/port/readme.md +7 -12
  35. package/src/backends/port/rpc.ts +16 -1
  36. package/src/backends/store/fs.ts +2 -2
  37. package/src/config.ts +3 -3
  38. package/src/emulation/async.ts +31 -18
  39. package/src/emulation/dir.ts +1 -1
  40. package/src/emulation/promises.ts +11 -6
  41. package/src/emulation/sync.ts +5 -4
  42. package/src/file.ts +6 -6
  43. package/src/filesystem.ts +2 -1
  44. package/src/inode.ts +56 -37
  45. package/src/polyfills.ts +1 -0
  46. package/src/utils.ts +5 -2
package/dist/config.d.ts CHANGED
@@ -9,7 +9,7 @@ export type MountConfiguration<T extends Backend> = FilesystemOf<T> | BackendCon
9
9
  * @see MountConfiguration
10
10
  */
11
11
  export declare function resolveMountConfig<T extends Backend>(config: MountConfiguration<T>, _depth?: number): Promise<FilesystemOf<T>>;
12
- type ConfigMounts = {
12
+ export type ConfigMounts = {
13
13
  [K in AbsolutePath]: Backend;
14
14
  };
15
15
  /**
@@ -40,4 +40,3 @@ export declare function configureSingle<T extends Backend>(config: MountConfigur
40
40
  * @see Configuration
41
41
  */
42
42
  export declare function configure<T extends ConfigMounts>(config: Partial<Configuration<T>>): Promise<void>;
43
- export {};
package/dist/config.js CHANGED
@@ -37,9 +37,9 @@ export async function resolveMountConfig(config, _depth = 0) {
37
37
  }
38
38
  const { backend } = config;
39
39
  if (!(await backend.isAvailable())) {
40
- throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
40
+ throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend.name);
41
41
  }
42
- checkOptions(backend, config);
42
+ await checkOptions(backend, config);
43
43
  const mount = (await backend.create(config));
44
44
  if ('_disableSync' in mount) {
45
45
  mount._disableSync = config.disableAsyncCache || false;
@@ -141,12 +141,11 @@ export declare function writeFile(filename: fs.PathLike, data: FileContents, opt
141
141
  * @param callback
142
142
  */
143
143
  export declare function appendFile(filename: fs.PathLike, data: FileContents, cb?: Callback): void;
144
- export declare function appendFile(filename: fs.PathLike, data: FileContents, options?: {
145
- encoding?: string;
146
- mode?: number | string;
147
- flag?: string;
144
+ export declare function appendFile(filename: fs.PathLike, data: FileContents, options?: fs.EncodingOption & {
145
+ mode?: fs.Mode;
146
+ flag?: fs.OpenMode;
148
147
  }, cb?: Callback): void;
149
- export declare function appendFile(filename: fs.PathLike, data: FileContents, encoding?: string, cb?: Callback): void;
148
+ export declare function appendFile(filename: fs.PathLike, data: FileContents, encoding?: BufferEncoding, cb?: Callback): void;
150
149
  /**
151
150
  * Asynchronous `fstat`.
152
151
  * `fstat()` is identical to `stat()`, except that the file to be stat-ed is
@@ -97,9 +97,10 @@ export function writeFile(filename, data, cbEncOpts, cb = nop) {
97
97
  }
98
98
  writeFile;
99
99
  export function appendFile(filename, data, cbEncOpts, cb = nop) {
100
+ const optionsOrEncoding = typeof cbEncOpts != 'function' ? cbEncOpts : undefined;
100
101
  cb = typeof cbEncOpts === 'function' ? cbEncOpts : cb;
101
102
  promises
102
- .appendFile(filename, data, typeof cbEncOpts === 'function' ? null : cbEncOpts)
103
+ .appendFile(filename, data, optionsOrEncoding)
103
104
  .then(() => cb())
104
105
  .catch(cb);
105
106
  }
@@ -160,7 +161,7 @@ export function fdatasync(fd, cb = nop) {
160
161
  .catch(cb);
161
162
  }
162
163
  fdatasync;
163
- export function write(fd, data, cbPosOff, cbLenEnc, cbPos, cb = nop) {
164
+ export function write(fd, data, cbPosOff, cbLenEnc, cbPosEnc, cb = nop) {
164
165
  let buffer, offset, length, position, encoding;
165
166
  const handle = new promises.FileHandle(fd);
166
167
  if (typeof data === 'string') {
@@ -175,11 +176,11 @@ export function write(fd, data, cbPosOff, cbLenEnc, cbPos, cb = nop) {
175
176
  // (fd, string, position, encoding?, cb?)
176
177
  position = cbPosOff;
177
178
  encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
178
- cb = typeof cbPos === 'function' ? cbPos : cb;
179
+ cb = typeof cbPosEnc === 'function' ? cbPosEnc : cb;
179
180
  break;
180
181
  default:
181
182
  // ...try to find the callback and get out of here!
182
- cb = typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPos === 'function' ? cbPos : cb;
183
+ cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb);
183
184
  cb(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
184
185
  return;
185
186
  }
@@ -197,9 +198,9 @@ export function write(fd, data, cbPosOff, cbLenEnc, cbPos, cb = nop) {
197
198
  buffer = Buffer.from(data.buffer);
198
199
  offset = cbPosOff;
199
200
  length = cbLenEnc;
200
- position = typeof cbPos === 'number' ? cbPos : null;
201
- const _cb = typeof cbPos === 'function' ? cbPos : cb;
202
- handle
201
+ position = typeof cbPosEnc === 'number' ? cbPosEnc : null;
202
+ const _cb = (typeof cbPosEnc === 'function' ? cbPosEnc : cb);
203
+ void handle
203
204
  .write(buffer, offset, length, position)
204
205
  .then(({ bytesWritten }) => _cb(undefined, bytesWritten, buffer))
205
206
  .catch(_cb);
@@ -297,7 +298,6 @@ export function readdir(path, _options, cb = nop) {
297
298
  const options = typeof _options != 'function' ? _options : {};
298
299
  promises
299
300
  .readdir(path, options)
300
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
301
301
  .then(entries => cb(undefined, entries))
302
302
  .catch(cb);
303
303
  }
@@ -426,7 +426,7 @@ export function access(path, cbMode, cb = nop) {
426
426
  const mode = typeof cbMode === 'number' ? cbMode : R_OK;
427
427
  cb = typeof cbMode === 'function' ? cbMode : cb;
428
428
  promises
429
- .access(path, typeof cbMode === 'function' ? null : cbMode)
429
+ .access(path, mode)
430
430
  .then(() => cb())
431
431
  .catch(cb);
432
432
  }
@@ -76,7 +76,7 @@ export class Dir {
76
76
  if (!cb) {
77
77
  return this._read();
78
78
  }
79
- this._read().then(value => cb(undefined, value));
79
+ void this._read().then(value => cb(undefined, value));
80
80
  }
81
81
  /**
82
82
  * Synchronously read the next directory entry via `readdir(3)` as a `Dirent`.
@@ -169,17 +169,17 @@ export class FileHandle {
169
169
  */
170
170
  readableWebStream(options = {}) {
171
171
  // Note: using an arrow function to preserve `this`
172
- const start = async ({ close, enqueue, error }) => {
172
+ const start = async (controller) => {
173
173
  try {
174
174
  const chunkSize = 64 * 1024, maxChunks = 1e7;
175
175
  let i = 0, position = 0, bytesRead = NaN;
176
176
  while (bytesRead > 0) {
177
177
  const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize, position);
178
178
  if (!result.bytesRead) {
179
- close();
179
+ controller.close();
180
180
  return;
181
181
  }
182
- enqueue(result.buffer.slice(0, result.bytesRead));
182
+ controller.enqueue(result.buffer.slice(0, result.bytesRead));
183
183
  position += result.bytesRead;
184
184
  if (++i >= maxChunks) {
185
185
  throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
@@ -188,10 +188,14 @@ export class FileHandle {
188
188
  }
189
189
  }
190
190
  catch (e) {
191
- error(e);
191
+ controller.error(e);
192
192
  }
193
193
  };
194
- return new globalThis.ReadableStream({ start, type: options.type });
194
+ const _gt = globalThis;
195
+ if (!('ReadableStream' in _gt)) {
196
+ throw new ErrnoError(Errno.ENOSYS, 'ReadableStream is missing on globalThis');
197
+ }
198
+ return new _gt.ReadableStream({ start, type: options.type });
195
199
  }
196
200
  readLines(options) {
197
201
  throw ErrnoError.With('ENOSYS', this.file.path, 'FileHandle.readLines');
@@ -290,6 +294,7 @@ export class FileHandle {
290
294
  const stream = new ReadStream({
291
295
  highWaterMark: options?.highWaterMark || 64 * 1024,
292
296
  encoding: options.encoding,
297
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
293
298
  read: async (size) => {
294
299
  try {
295
300
  const result = await this.read(new Uint8Array(size), 0, size, this.file.position);
@@ -247,6 +247,7 @@ export declare function symlinkSync(target: fs.PathLike, path: fs.PathLike, type
247
247
  */
248
248
  export declare function readlinkSync(path: fs.PathLike, options?: fs.BufferEncodingOption): Buffer;
249
249
  export declare function readlinkSync(path: fs.PathLike, options: fs.EncodingOption | BufferEncoding): string;
250
+ export declare function readlinkSync(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Buffer | string;
250
251
  /**
251
252
  * Synchronous `chown`.
252
253
  * @param path
@@ -618,7 +618,7 @@ export function realpathSync(path, options) {
618
618
  if (!stats.isSymbolicLink()) {
619
619
  return lpath;
620
620
  }
621
- return realpathSync(mountPoint + readlinkSync(lpath));
621
+ return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
622
622
  }
623
623
  catch (e) {
624
624
  throw fixError(e, { [resolvedPath]: lpath });
package/dist/file.js CHANGED
@@ -228,8 +228,8 @@ export class PreloadFile extends File {
228
228
  /**
229
229
  * Asynchronous `stat`.
230
230
  */
231
- async stat() {
232
- return new Stats(this.stats);
231
+ stat() {
232
+ return Promise.resolve(new Stats(this.stats));
233
233
  }
234
234
  /**
235
235
  * Synchronous `stat`.
@@ -453,8 +453,8 @@ export class NoSyncFile extends PreloadFile {
453
453
  /**
454
454
  * Asynchronous sync. Doesn't do anything, simply calls the cb.
455
455
  */
456
- async sync() {
457
- return;
456
+ sync() {
457
+ return Promise.resolve();
458
458
  }
459
459
  /**
460
460
  * Synchronous sync. Doesn't do anything.
@@ -465,8 +465,8 @@ export class NoSyncFile extends PreloadFile {
465
465
  /**
466
466
  * Asynchronous close. Doesn't do anything, simply calls the cb.
467
467
  */
468
- async close() {
469
- return;
468
+ close() {
469
+ return Promise.resolve();
470
470
  }
471
471
  /**
472
472
  * Synchronous close. Doesn't do anything.
@@ -301,7 +301,7 @@ export function Async(FS) {
301
301
  */
302
302
  queue(...op) {
303
303
  this._queue.push(op);
304
- this._next();
304
+ void this._next();
305
305
  }
306
306
  }
307
307
  return AsyncFS;
package/dist/inode.d.ts CHANGED
@@ -13,7 +13,7 @@ export declare const size_max: number;
13
13
  * Room inode
14
14
  * @hidden
15
15
  */
16
- export declare const rootIno: 0n;
16
+ export declare const rootIno = 0n;
17
17
  /**
18
18
  * Generate a random ino
19
19
  * @internal
package/dist/inode.js CHANGED
@@ -25,20 +25,37 @@ export function randomIno() {
25
25
  /**
26
26
  * Offsets for inode members
27
27
  */
28
- var Offset;
29
- (function (Offset) {
30
- Offset[Offset["ino"] = 0] = "ino";
31
- Offset[Offset["size"] = 8] = "size";
32
- Offset[Offset["mode"] = 12] = "mode";
33
- Offset[Offset["nlink"] = 14] = "nlink";
34
- Offset[Offset["uid"] = 18] = "uid";
35
- Offset[Offset["gid"] = 22] = "gid";
36
- Offset[Offset["atime"] = 26] = "atime";
37
- Offset[Offset["birthtime"] = 34] = "birthtime";
38
- Offset[Offset["mtime"] = 42] = "mtime";
39
- Offset[Offset["ctime"] = 50] = "ctime";
40
- Offset[Offset["end"] = 58] = "end";
41
- })(Offset || (Offset = {}));
28
+ const offsets = {
29
+ ino: 0,
30
+ size: 8,
31
+ mode: 12,
32
+ nlink: 14,
33
+ uid: 18,
34
+ gid: 22,
35
+ atime: 26,
36
+ birthtime: 34,
37
+ mtime: 42,
38
+ ctime: 50,
39
+ end: 58,
40
+ };
41
+ /**
42
+ * Offsets for a 64-bit inode's members
43
+ * Currently unused
44
+ */
45
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
46
+ const offsets_64 = {
47
+ ino: 0,
48
+ size: 8,
49
+ mode: 16,
50
+ nlink: 18,
51
+ uid: 22,
52
+ gid: 26,
53
+ atime: 30,
54
+ birthtime: 38,
55
+ mtime: 46,
56
+ ctime: 54,
57
+ end: 62,
58
+ };
42
59
  /**
43
60
  * Generic inode definition that can easily be serialized.
44
61
  */
@@ -48,9 +65,9 @@ export class Inode {
48
65
  }
49
66
  constructor(buffer) {
50
67
  const setDefaults = !buffer;
51
- buffer ?? (buffer = new ArrayBuffer(Offset.end));
52
- if (buffer?.byteLength < Offset.end) {
53
- throw new RangeError(`Can not create an inode from a buffer less than ${Offset.end} bytes`);
68
+ buffer ?? (buffer = new ArrayBuffer(offsets.end));
69
+ if (buffer?.byteLength < offsets.end) {
70
+ throw new RangeError(`Can not create an inode from a buffer less than ${offsets.end} bytes`);
54
71
  }
55
72
  this.view = new DataView(buffer);
56
73
  this.buffer = buffer;
@@ -68,64 +85,64 @@ export class Inode {
68
85
  this.birthtimeMs = now;
69
86
  }
70
87
  get ino() {
71
- return this.view.getBigUint64(Offset.ino, true);
88
+ return this.view.getBigUint64(offsets.ino, true);
72
89
  }
73
90
  set ino(value) {
74
- this.view.setBigUint64(Offset.ino, value, true);
91
+ this.view.setBigUint64(offsets.ino, value, true);
75
92
  }
76
93
  get size() {
77
- return this.view.getUint32(Offset.size, true);
94
+ return this.view.getUint32(offsets.size, true);
78
95
  }
79
96
  set size(value) {
80
- this.view.setUint32(Offset.size, value, true);
97
+ this.view.setUint32(offsets.size, value, true);
81
98
  }
82
99
  get mode() {
83
- return this.view.getUint16(Offset.mode, true);
100
+ return this.view.getUint16(offsets.mode, true);
84
101
  }
85
102
  set mode(value) {
86
- this.view.setUint16(Offset.mode, value, true);
103
+ this.view.setUint16(offsets.mode, value, true);
87
104
  }
88
105
  get nlink() {
89
- return this.view.getUint32(Offset.nlink, true);
106
+ return this.view.getUint32(offsets.nlink, true);
90
107
  }
91
108
  set nlink(value) {
92
- this.view.setUint32(Offset.nlink, value, true);
109
+ this.view.setUint32(offsets.nlink, value, true);
93
110
  }
94
111
  get uid() {
95
- return this.view.getUint32(Offset.uid, true);
112
+ return this.view.getUint32(offsets.uid, true);
96
113
  }
97
114
  set uid(value) {
98
- this.view.setUint32(Offset.uid, value, true);
115
+ this.view.setUint32(offsets.uid, value, true);
99
116
  }
100
117
  get gid() {
101
- return this.view.getUint32(Offset.gid, true);
118
+ return this.view.getUint32(offsets.gid, true);
102
119
  }
103
120
  set gid(value) {
104
- this.view.setUint32(Offset.gid, value, true);
121
+ this.view.setUint32(offsets.gid, value, true);
105
122
  }
106
123
  get atimeMs() {
107
- return this.view.getFloat64(Offset.atime, true);
124
+ return this.view.getFloat64(offsets.atime, true);
108
125
  }
109
126
  set atimeMs(value) {
110
- this.view.setFloat64(Offset.atime, value, true);
127
+ this.view.setFloat64(offsets.atime, value, true);
111
128
  }
112
129
  get birthtimeMs() {
113
- return this.view.getFloat64(Offset.birthtime, true);
130
+ return this.view.getFloat64(offsets.birthtime, true);
114
131
  }
115
132
  set birthtimeMs(value) {
116
- this.view.setFloat64(Offset.birthtime, value, true);
133
+ this.view.setFloat64(offsets.birthtime, value, true);
117
134
  }
118
135
  get mtimeMs() {
119
- return this.view.getFloat64(Offset.mtime, true);
136
+ return this.view.getFloat64(offsets.mtime, true);
120
137
  }
121
138
  set mtimeMs(value) {
122
- this.view.setFloat64(Offset.mtime, value, true);
139
+ this.view.setFloat64(offsets.mtime, value, true);
123
140
  }
124
141
  get ctimeMs() {
125
- return this.view.getFloat64(Offset.ctime, true);
142
+ return this.view.getFloat64(offsets.ctime, true);
126
143
  }
127
144
  set ctimeMs(value) {
128
- this.view.setFloat64(Offset.ctime, value, true);
145
+ this.view.setFloat64(offsets.ctime, value, true);
129
146
  }
130
147
  /**
131
148
  * Handy function that converts the Inode to a Node Stats object.
package/dist/polyfills.js CHANGED
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line @typescript-eslint/unbound-method
1
2
  Promise.withResolvers ?? (Promise.withResolvers = function () {
2
3
  let _resolve,
3
4
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
package/dist/utils.d.ts CHANGED
@@ -56,7 +56,7 @@ export declare function _toUnixTimestamp(time: Date | number): number;
56
56
  * Normalizes a mode
57
57
  * @internal
58
58
  */
59
- export declare function normalizeMode(mode: string | number | unknown, def?: number): number;
59
+ export declare function normalizeMode(mode: unknown, def?: number): number;
60
60
  /**
61
61
  * Normalizes a time
62
62
  * @internal
package/dist/utils.js CHANGED
@@ -146,7 +146,7 @@ export function _toUnixTimestamp(time) {
146
146
  if (time instanceof Date) {
147
147
  return Math.floor(time.getTime() / 1000);
148
148
  }
149
- throw new Error('Cannot parse time: ' + time);
149
+ throw new Error('Cannot parse time');
150
150
  }
151
151
  /**
152
152
  * Normalizes a mode
@@ -0,0 +1,53 @@
1
+ /*
2
+ Shared eslint rules
3
+ */
4
+
5
+ import eslint from '@eslint/js';
6
+ import stylistic from '@stylistic/eslint-plugin';
7
+ import globals from 'globals';
8
+ import tseslint from 'typescript-eslint';
9
+
10
+ export default {
11
+ name: 'ZenFS',
12
+ extends: [eslint.configs.recommended, ...tseslint.configs.recommendedTypeChecked],
13
+ files: ['src/**/*.ts', 'tests/**/*.ts'],
14
+ languageOptions: {
15
+ globals: { ...globals.browser, ...globals.node },
16
+ ecmaVersion: 'latest',
17
+ sourceType: 'module',
18
+ },
19
+ plugins: { stylistic },
20
+ rules: {
21
+ 'no-useless-escape': 'warn',
22
+ 'stylistic/no-mixed-spaces-and-tabs': 'warn',
23
+ 'no-unreachable': 'warn',
24
+ 'stylistic/no-extra-semi': 'warn',
25
+ 'no-fallthrough': 'warn',
26
+ 'no-empty': 'warn',
27
+ 'no-case-declarations': 'warn',
28
+ 'prefer-const': 'warn',
29
+ 'prefer-rest-params': 'warn',
30
+ 'prefer-spread': 'warn',
31
+ 'no-unused-vars': 'off',
32
+ '@typescript-eslint/no-unused-vars': 'warn',
33
+ '@typescript-eslint/no-inferrable-types': 'off',
34
+ '@typescript-eslint/no-this-alias': 'off',
35
+ '@typescript-eslint/no-unsafe-function-type': 'warn',
36
+ '@typescript-eslint/no-wrapper-object-types': 'warn',
37
+ '@typescript-eslint/triple-slash-reference': 'warn',
38
+ '@typescript-eslint/no-non-null-assertion': 'off',
39
+ '@typescript-eslint/no-namespace': 'warn',
40
+ '@typescript-eslint/prefer-as-const': 'warn',
41
+ '@typescript-eslint/no-explicit-any': 'warn',
42
+ '@typescript-eslint/consistent-type-assertions': 'warn',
43
+ '@typescript-eslint/consistent-type-imports': 'warn',
44
+ '@typescript-eslint/no-unnecessary-type-assertion': 'warn',
45
+ '@typescript-eslint/require-await': 'warn',
46
+ '@typescript-eslint/no-unsafe-return': 'warn',
47
+ '@typescript-eslint/no-unsafe-assignment': 'warn',
48
+ '@typescript-eslint/no-unsafe-member-access': 'warn',
49
+ '@typescript-eslint/no-unsafe-argument': 'warn',
50
+ '@typescript-eslint/no-redundant-type-constituents': 'warn',
51
+ '@typescript-eslint/no-unsafe-call': 'warn',
52
+ },
53
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "0.16.0",
3
+ "version": "0.16.2",
4
4
  "description": "A filesystem, anywhere",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,7 +17,8 @@
17
17
  "dist",
18
18
  "src",
19
19
  "license.md",
20
- "tsconfig.json"
20
+ "tsconfig.json",
21
+ "eslint.shared.js"
21
22
  ],
22
23
  "type": "module",
23
24
  "homepage": "https://github.com/zen-fs/core",
@@ -51,6 +52,11 @@
51
52
  "dev": "npm run build -- --watch",
52
53
  "prepublishOnly": "npm run build"
53
54
  },
55
+ "lint-staged": {
56
+ "*": [
57
+ "prettier --write"
58
+ ]
59
+ },
54
60
  "dependencies": {
55
61
  "@types/node": "^20.12.12",
56
62
  "@types/readable-stream": "^4.0.10",
@@ -61,19 +67,23 @@
61
67
  "utilium": "^0.4.0"
62
68
  },
63
69
  "devDependencies": {
70
+ "@eslint/js": "^9.8.0",
64
71
  "@fal-works/esbuild-plugin-global-externals": "^2.1.2",
65
72
  "@jest/globals": "^29.5.0",
73
+ "@stylistic/eslint-plugin": "^2.6.1",
74
+ "@types/eslint__js": "^8.42.3",
66
75
  "@types/jest": "^29.5.1",
67
- "@typescript-eslint/eslint-plugin": "^7.7.0",
68
- "@typescript-eslint/parser": "^7.7.0",
69
76
  "cross-env": "^7.0.3",
70
77
  "esbuild": "^0.21.0",
71
- "eslint": "^8.36.0",
78
+ "eslint": "^9.8.0",
79
+ "globals": "^15.9.0",
72
80
  "jest": "^29.7.0",
81
+ "lint-staged": "^15.2.7",
73
82
  "prettier": "^3.2.5",
74
83
  "ts-jest": "^29.1.5",
75
84
  "typedoc": "^0.25.13",
76
85
  "typedoc-plugin-remove-references": "^0.0.6",
77
- "typescript": "^5.4.0"
86
+ "typescript": "^5.4.0",
87
+ "typescript-eslint": "^8.0.0"
78
88
  }
79
89
  }
@@ -15,21 +15,22 @@ import type { IndexData } from './index/index.js';
15
15
  async function fetchFile(path: string, type: 'buffer'): Promise<Uint8Array>;
16
16
  async function fetchFile<T extends object>(path: string, type: 'json'): Promise<T>;
17
17
  async function fetchFile<T extends object>(path: string, type: 'buffer' | 'json'): Promise<T | Uint8Array>;
18
- async function fetchFile<T extends object>(path: string, type: 'buffer' | 'json'): Promise<T | Uint8Array> {
19
- const response = await fetch(path).catch(e => {
18
+ async function fetchFile<T extends object>(path: string, type: string): Promise<T | Uint8Array> {
19
+ const response = await fetch(path).catch((e: Error) => {
20
20
  throw new ErrnoError(Errno.EIO, e.message);
21
21
  });
22
22
  if (!response.ok) {
23
23
  throw new ErrnoError(Errno.EIO, 'fetch failed: response returned code ' + response.status);
24
24
  }
25
25
  switch (type) {
26
- case 'buffer':
27
- const arrayBuffer = await response.arrayBuffer().catch(e => {
26
+ case 'buffer': {
27
+ const arrayBuffer = await response.arrayBuffer().catch((e: Error) => {
28
28
  throw new ErrnoError(Errno.EIO, e.message);
29
29
  });
30
30
  return new Uint8Array(arrayBuffer);
31
+ }
31
32
  case 'json':
32
- return response.json().catch(e => {
33
+ return response.json().catch((e: Error) => {
33
34
  throw new ErrnoError(Errno.EIO, e.message);
34
35
  }) as Promise<T>;
35
36
  default:
@@ -39,8 +39,8 @@ export abstract class IndexFS extends Readonly(FileSystem) {
39
39
  }
40
40
  }
41
41
 
42
- public async stat(path: string): Promise<Stats> {
43
- return this.statSync(path);
42
+ public stat(path: string): Promise<Stats> {
43
+ return Promise.resolve(this.statSync(path));
44
44
  }
45
45
 
46
46
  public statSync(path: string): Stats {
@@ -91,8 +91,8 @@ export abstract class IndexFS extends Readonly(FileSystem) {
91
91
  return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));
92
92
  }
93
93
 
94
- public async readdir(path: string): Promise<string[]> {
95
- return this.readdirSync(path);
94
+ public readdir(path: string): Promise<string[]> {
95
+ return Promise.resolve(this.readdirSync(path));
96
96
  }
97
97
 
98
98
  public readdirSync(path: string): string[] {
@@ -106,7 +106,14 @@ export abstract class IndexFS extends Readonly(FileSystem) {
106
106
  throw ErrnoError.With('ENOTDIR', path, 'readdir');
107
107
  }
108
108
 
109
- return JSON.parse(decode(stats.fileData));
109
+ const content: unknown = JSON.parse(decode(stats.fileData));
110
+ if (!Array.isArray(content)) {
111
+ throw ErrnoError.With('ENODATA', path, 'readdir');
112
+ }
113
+ if (!content.every(item => typeof item == 'string')) {
114
+ throw ErrnoError.With('ENODATA', path, 'readdir');
115
+ }
116
+ return content as string[];
110
117
  }
111
118
 
112
119
  protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
@@ -96,7 +96,7 @@ export class Index extends Map<string, Stats> {
96
96
  throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
97
97
  }
98
98
 
99
- const json = JSON.parse(data);
99
+ const json = JSON.parse(data) as IndexData;
100
100
  const index = new Index();
101
101
  index.fromJSON(json);
102
102
  return index;