@zenfs/core 0.11.2 → 0.12.1

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 (45) hide show
  1. package/dist/backends/backend.d.ts +15 -5
  2. package/dist/backends/fetch.d.ts +19 -32
  3. package/dist/backends/fetch.js +38 -85
  4. package/dist/backends/index/fs.d.ts +49 -0
  5. package/dist/backends/index/fs.js +86 -0
  6. package/dist/backends/index/index.d.ts +42 -0
  7. package/dist/backends/index/index.js +83 -0
  8. package/dist/backends/port/fs.d.ts +2 -1
  9. package/dist/browser.min.js +8 -4
  10. package/dist/browser.min.js.map +4 -4
  11. package/dist/config.d.ts +33 -11
  12. package/dist/config.js +16 -7
  13. package/dist/emulation/async.d.ts +1 -0
  14. package/dist/emulation/async.js +3 -2
  15. package/dist/emulation/path.d.ts +4 -1
  16. package/dist/emulation/promises.js +26 -14
  17. package/dist/emulation/sync.js +27 -15
  18. package/dist/file.d.ts +1 -1
  19. package/dist/file.js +10 -10
  20. package/dist/filesystem.d.ts +22 -2
  21. package/dist/filesystem.js +37 -8
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +1 -1
  24. package/dist/stats.d.ts +10 -10
  25. package/dist/stats.js +1 -1
  26. package/package.json +2 -2
  27. package/scripts/make-index.js +31 -19
  28. package/src/backends/backend.ts +15 -6
  29. package/src/backends/fetch.ts +42 -101
  30. package/src/backends/index/fs.ts +113 -0
  31. package/src/backends/index/index.ts +103 -0
  32. package/src/backends/index/readme.md +3 -0
  33. package/src/config.ts +62 -21
  34. package/src/emulation/async.ts +14 -13
  35. package/src/emulation/path.ts +4 -1
  36. package/src/emulation/promises.ts +25 -14
  37. package/src/emulation/sync.ts +29 -18
  38. package/src/error.ts +1 -1
  39. package/src/file.ts +11 -10
  40. package/src/filesystem.ts +106 -51
  41. package/src/index.ts +1 -1
  42. package/src/stats.ts +13 -13
  43. package/dist/backends/Index.d.ts +0 -204
  44. package/dist/backends/Index.js +0 -410
  45. package/src/backends/Index.ts +0 -504
@@ -4,7 +4,7 @@ type OptionType = 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undef
4
4
  /**
5
5
  * Resolves the type of Backend.options from the options interface
6
6
  */
7
- type OptionsConfig<T> = {
7
+ export type OptionsConfig<T> = {
8
8
  [K in keyof T]: {
9
9
  /**
10
10
  * The basic JavaScript type(s) for this option.
@@ -54,7 +54,16 @@ export interface Backend<FS extends FileSystem = FileSystem, TOptions extends ob
54
54
  */
55
55
  isAvailable(): boolean | Promise<boolean>;
56
56
  }
57
- type OptionsOf<T extends Backend> = T extends Backend<FileSystem, infer TOptions> ? TOptions : never;
57
+ /**
58
+ * Gets the options type of a backend
59
+ * @internal
60
+ */
61
+ export type OptionsOf<T extends Backend> = T extends Backend<FileSystem, infer TOptions> ? TOptions : never;
62
+ /**
63
+ * Gets the FileSystem type for a backend
64
+ * @internal
65
+ */
66
+ export type FilesystemOf<T extends Backend> = T extends Backend<infer FS> ? FS : never;
58
67
  /**
59
68
  * @internal
60
69
  */
@@ -63,7 +72,7 @@ export declare function isBackend(arg: unknown): arg is Backend;
63
72
  * Checks that the given options object is valid for the file system options.
64
73
  * @internal
65
74
  */
66
- export declare function checkOptions<T extends Backend>(backend: T, opts: Partial<OptionsOf<T>> & Record<string, unknown>): Promise<void>;
75
+ export declare function checkOptions<T extends Backend>(backend: T, opts: Record<string, unknown>): Promise<void>;
67
76
  /**
68
77
  * Specifies a file system backend type and its options.
69
78
  *
@@ -72,11 +81,12 @@ export declare function checkOptions<T extends Backend>(backend: T, opts: Partia
72
81
  *
73
82
  * The option object for each file system corresponds to that file system's option object passed to its `Create()` method.
74
83
  */
75
- export type BackendConfiguration<T extends Backend = Backend> = OptionsOf<T> & {
84
+ export type BackendConfiguration<T extends Backend> = OptionsOf<T> & {
76
85
  backend: T;
86
+ disableAsyncCache?: boolean;
77
87
  };
78
88
  /**
79
89
  * @internal
80
90
  */
81
- export declare function isBackendConfig(arg: unknown): arg is BackendConfiguration;
91
+ export declare function isBackendConfig<T extends Backend>(arg: unknown): arg is BackendConfiguration<T>;
82
92
  export {};
@@ -1,7 +1,7 @@
1
- import { NoSyncFile } from '../file.js';
2
1
  import type { FileSystemMetadata } from '../filesystem.js';
3
2
  import { Stats } from '../stats.js';
4
- import { type ListingTree, type IndexFileInode, AsyncIndexFS } from './Index.js';
3
+ import { IndexFS } from './index/fs.js';
4
+ import type { IndexData } from './index/index.js';
5
5
  /**
6
6
  * Configuration options for FetchFS.
7
7
  */
@@ -10,60 +10,47 @@ export interface FetchOptions {
10
10
  * URL to a file index as a JSON file or the file index object itself.
11
11
  * Defaults to `index.json`.
12
12
  */
13
- index?: string | ListingTree;
13
+ index?: string | IndexData;
14
14
  /** Used as the URL prefix for fetched files.
15
15
  * Default: Fetch files relative to the index.
16
16
  */
17
17
  baseUrl?: string;
18
18
  }
19
19
  /**
20
- * A simple filesystem backed by HTTP using the fetch API.
20
+ * A simple filesystem backed by HTTP using the `fetch` API.
21
21
  *
22
22
  *
23
- * Listings objects look like the following:
23
+ * Index objects look like the following:
24
24
  *
25
25
  * ```json
26
26
  * {
27
- * "home": {
28
- * "jvilk": {
29
- * "someFile.txt": null,
30
- * "someDir": {
31
- * // Empty directory
32
- * }
33
- * }
34
- * }
27
+ * "version": 1,
28
+ * "entries": {
29
+ * "/home": { ... },
30
+ * "/home/jvilk": { ... },
31
+ * "/home/james": { ... }
32
+ * }
35
33
  * }
36
34
  * ```
37
35
  *
38
- * This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.
36
+ * Each entry contains the stats associated with the file.
39
37
  */
40
- export declare class FetchFS extends AsyncIndexFS<Stats> {
41
- readonly prefixUrl: string;
42
- protected _init: Promise<void>;
43
- protected _initialize(index: string | ListingTree): Promise<void>;
38
+ export declare class FetchFS extends IndexFS {
39
+ readonly baseUrl: string;
44
40
  ready(): Promise<void>;
45
41
  constructor({ index, baseUrl }: FetchOptions);
46
42
  metadata(): FileSystemMetadata;
47
- empty(): void;
48
43
  /**
49
- * Special function: Preload the given file into the index.
44
+ * Preload the given file into the index.
50
45
  * @param path
51
46
  * @param buffer
52
47
  */
53
- preloadFile(path: string, buffer: Uint8Array): void;
54
- protected statFileInode(inode: IndexFileInode<Stats>, path: string): Promise<Stats>;
55
- protected openFileInode(inode: IndexFileInode<Stats>, path: string, flag: string): Promise<NoSyncFile<this>>;
56
- private _getRemotePath;
48
+ preload(path: string, buffer: Uint8Array): void;
57
49
  /**
58
- * Asynchronously download the given file.
50
+ * @todo Be lazier about actually requesting the data?
59
51
  */
60
- protected _fetchFile(path: string, type: 'buffer'): Promise<Uint8Array>;
61
- protected _fetchFile(path: string, type: 'json'): Promise<object>;
62
- protected _fetchFile(path: string, type: 'buffer' | 'json'): Promise<object>;
63
- /**
64
- * Only requests the HEAD content, for the file size.
65
- */
66
- protected _fetchSize(path: string): Promise<number>;
52
+ protected getData(path: string, stats: Stats): Promise<Uint8Array>;
53
+ protected getDataSync(path: string, stats: Stats): Uint8Array;
67
54
  }
68
55
  export declare const Fetch: {
69
56
  readonly name: "Fetch";
@@ -1,7 +1,5 @@
1
- import { ErrnoError, Errno } from '../error.js';
2
- import { NoSyncFile } from '../file.js';
3
- import { Stats } from '../stats.js';
4
- import { FileIndex, AsyncIndexFS } from './Index.js';
1
+ import { Errno, ErrnoError } from '../error.js';
2
+ import { IndexFS } from './index/fs.js';
5
3
  async function fetchFile(path, type) {
6
4
  const response = await fetch(path).catch(e => {
7
5
  throw new ErrnoError(Errno.EIO, e.message);
@@ -24,64 +22,44 @@ async function fetchFile(path, type) {
24
22
  }
25
23
  }
26
24
  /**
27
- * Asynchronously retrieves the size of the given file in bytes.
28
- * @hidden
29
- */
30
- async function fetchSize(path) {
31
- const response = await fetch(path, { method: 'HEAD' }).catch(e => {
32
- throw new ErrnoError(Errno.EIO, e.message);
33
- });
34
- if (!response.ok) {
35
- throw new ErrnoError(Errno.EIO, 'fetch failed: HEAD response returned code ' + response.status);
36
- }
37
- return parseInt(response.headers.get('Content-Length') || '-1', 10);
38
- }
39
- /**
40
- * A simple filesystem backed by HTTP using the fetch API.
25
+ * A simple filesystem backed by HTTP using the `fetch` API.
41
26
  *
42
27
  *
43
- * Listings objects look like the following:
28
+ * Index objects look like the following:
44
29
  *
45
30
  * ```json
46
31
  * {
47
- * "home": {
48
- * "jvilk": {
49
- * "someFile.txt": null,
50
- * "someDir": {
51
- * // Empty directory
52
- * }
53
- * }
54
- * }
32
+ * "version": 1,
33
+ * "entries": {
34
+ * "/home": { ... },
35
+ * "/home/jvilk": { ... },
36
+ * "/home/james": { ... }
37
+ * }
55
38
  * }
56
39
  * ```
57
40
  *
58
- * This example has the folder `/home/jvilk` with subfile `someFile.txt` and subfolder `someDir`.
41
+ * Each entry contains the stats associated with the file.
59
42
  */
60
- export class FetchFS extends AsyncIndexFS {
61
- async _initialize(index) {
62
- if (typeof index != 'string') {
63
- this._index = FileIndex.FromListing(index);
43
+ export class FetchFS extends IndexFS {
44
+ async ready() {
45
+ if (this._isInitialized) {
64
46
  return;
65
47
  }
66
- try {
67
- const response = await fetch(index);
68
- this._index = FileIndex.FromListing((await response.json()));
69
- }
70
- catch (e) {
71
- throw new ErrnoError(Errno.EINVAL, 'Invalid or unavailable file listing tree');
48
+ await super.ready();
49
+ /**
50
+ * Iterate over all of the files and cache their contents
51
+ */
52
+ for (const [path, stats] of this.index.files()) {
53
+ await this.getData(path, stats);
72
54
  }
73
55
  }
74
- async ready() {
75
- await this._init;
76
- }
77
56
  constructor({ index = 'index.json', baseUrl = '' }) {
78
- super({});
57
+ super(typeof index != 'string' ? index : fetchFile(index, 'json'));
79
58
  // prefix url must end in a directory separator.
80
59
  if (baseUrl.at(-1) != '/') {
81
60
  baseUrl += '/';
82
61
  }
83
- this.prefixUrl = baseUrl;
84
- this._init = this._initialize(index);
62
+ this.baseUrl = baseUrl;
85
63
  }
86
64
  metadata() {
87
65
  return {
@@ -90,63 +68,38 @@ export class FetchFS extends AsyncIndexFS {
90
68
  readonly: true,
91
69
  };
92
70
  }
93
- empty() {
94
- for (const file of this._index.files()) {
95
- delete file.data.fileData;
96
- }
97
- }
98
71
  /**
99
- * Special function: Preload the given file into the index.
72
+ * Preload the given file into the index.
100
73
  * @param path
101
74
  * @param buffer
102
75
  */
103
- preloadFile(path, buffer) {
104
- const inode = this._index.get(path);
105
- if (!inode) {
76
+ preload(path, buffer) {
77
+ const stats = this.index.get(path);
78
+ if (!stats) {
106
79
  throw ErrnoError.With('ENOENT', path, 'preloadFile');
107
80
  }
108
- if (!inode.isFile()) {
81
+ if (!stats.isFile()) {
109
82
  throw ErrnoError.With('EISDIR', path, 'preloadFile');
110
83
  }
111
- const stats = inode.data;
112
84
  stats.size = buffer.length;
113
85
  stats.fileData = buffer;
114
86
  }
115
- async statFileInode(inode, path) {
116
- const stats = inode.data;
117
- // At this point, a non-opened file will still have default stats from the listing.
118
- if (stats.size < 0) {
119
- stats.size = await this._fetchSize(path);
120
- }
121
- return stats;
122
- }
123
- async openFileInode(inode, path, flag) {
124
- const stats = inode.data;
125
- // Use existing file contents. This maintains the previously-used flag.
87
+ /**
88
+ * @todo Be lazier about actually requesting the data?
89
+ */
90
+ async getData(path, stats) {
126
91
  if (stats.fileData) {
127
- return new NoSyncFile(this, path, flag, new Stats(stats), stats.fileData);
92
+ return stats.fileData;
128
93
  }
129
- // @todo be lazier about actually requesting the file
130
- const data = await this._fetchFile(path, 'buffer');
131
- // we don't initially have file sizes
132
- stats.size = data.length;
94
+ const data = await fetchFile(this.baseUrl + (path.startsWith('/') ? path.slice(1) : path), 'buffer');
133
95
  stats.fileData = data;
134
- return new NoSyncFile(this, path, flag, new Stats(stats), data);
96
+ return data;
135
97
  }
136
- _getRemotePath(filePath) {
137
- if (filePath.charAt(0) === '/') {
138
- filePath = filePath.slice(1);
98
+ getDataSync(path, stats) {
99
+ if (stats.fileData) {
100
+ return stats.fileData;
139
101
  }
140
- return this.prefixUrl + filePath;
141
- }
142
- _fetchFile(path, type) {
143
- return fetchFile(this._getRemotePath(path), type);
144
- }
145
- /**
146
- * Only requests the HEAD content, for the file size.
147
- */
148
- _fetchSize(path) {
149
- return fetchSize(this._getRemotePath(path));
102
+ throw new ErrnoError(Errno.ENODATA, '', path, 'getData');
150
103
  }
151
104
  }
152
105
  export const Fetch = {
@@ -0,0 +1,49 @@
1
+ import type { Cred } from '../../cred.js';
2
+ import { NoSyncFile } from '../../file.js';
3
+ import { FileSystem } from '../../filesystem.js';
4
+ import type { Stats } from '../../stats.js';
5
+ import { Index, IndexData } from './index.js';
6
+ declare const IndexFS_base: (abstract new (...args: any[]) => {
7
+ metadata(): import("../../filesystem.js").FileSystemMetadata;
8
+ rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
9
+ renameSync(oldPath: string, newPath: string, cred: Cred): void;
10
+ createFile(path: string, flag: string, mode: number, cred: Cred): Promise<import("../../file.js").File>;
11
+ createFileSync(path: string, flag: string, mode: number, cred: Cred): import("../../file.js").File;
12
+ unlink(path: string, cred: Cred): Promise<void>;
13
+ unlinkSync(path: string, cred: Cred): void;
14
+ rmdir(path: string, cred: Cred): Promise<void>;
15
+ rmdirSync(path: string, cred: Cred): void;
16
+ mkdir(path: string, mode: number, cred: Cred): Promise<void>;
17
+ mkdirSync(path: string, mode: number, cred: Cred): void;
18
+ link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
19
+ linkSync(srcpath: string, dstpath: string, cred: Cred): void;
20
+ sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
21
+ syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
22
+ ready(): Promise<void>;
23
+ stat(path: string, cred: Cred): Promise<Stats>;
24
+ statSync(path: string, cred: Cred): Stats;
25
+ openFile(path: string, flag: string, cred: Cred): Promise<import("../../file.js").File>;
26
+ openFileSync(path: string, flag: string, cred: Cred): import("../../file.js").File;
27
+ readdir(path: string, cred: Cred): Promise<string[]>;
28
+ readdirSync(path: string, cred: Cred): string[];
29
+ exists(path: string, cred: Cred): Promise<boolean>;
30
+ existsSync(path: string, cred: Cred): boolean;
31
+ }) & typeof FileSystem;
32
+ export declare abstract class IndexFS extends IndexFS_base {
33
+ private indexData;
34
+ protected index: Index;
35
+ protected _isInitialized: boolean;
36
+ ready(): Promise<void>;
37
+ constructor(indexData: IndexData | Promise<IndexData>);
38
+ reloadFiles(): Promise<void>;
39
+ reloadFilesSync(): void;
40
+ stat(path: string): Promise<Stats>;
41
+ statSync(path: string): Stats;
42
+ openFile(path: string, flag: string, cred: Cred): Promise<NoSyncFile<this>>;
43
+ openFileSync(path: string, flag: string, cred: Cred): NoSyncFile<this>;
44
+ readdir(path: string): Promise<string[]>;
45
+ readdirSync(path: string): string[];
46
+ protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
47
+ protected abstract getDataSync(path: string, stats: Stats): Uint8Array;
48
+ }
49
+ export {};
@@ -0,0 +1,86 @@
1
+ import { ErrnoError, Errno } from '../../error.js';
2
+ import { NoSyncFile, isWriteable, flagToMode } from '../../file.js';
3
+ import { Readonly, FileSystem } from '../../filesystem.js';
4
+ import { decode } from '../../utils.js';
5
+ import { Index } from './index.js';
6
+ export class IndexFS extends Readonly(FileSystem) {
7
+ async ready() {
8
+ await super.ready();
9
+ if (this._isInitialized) {
10
+ return;
11
+ }
12
+ this.index.fromJSON(await this.indexData);
13
+ this._isInitialized = true;
14
+ }
15
+ constructor(indexData) {
16
+ super();
17
+ this.indexData = indexData;
18
+ this.index = new Index();
19
+ this._isInitialized = false;
20
+ }
21
+ async reloadFiles() {
22
+ for (const [path, stats] of this.index.files()) {
23
+ delete stats.fileData;
24
+ stats.fileData = await this.getData(path, stats);
25
+ }
26
+ }
27
+ reloadFilesSync() {
28
+ for (const [path, stats] of this.index.files()) {
29
+ delete stats.fileData;
30
+ stats.fileData = this.getDataSync(path, stats);
31
+ }
32
+ }
33
+ async stat(path) {
34
+ return this.statSync(path);
35
+ }
36
+ statSync(path) {
37
+ if (!this.index.has(path)) {
38
+ throw ErrnoError.With('ENOENT', path, 'stat');
39
+ }
40
+ return this.index.get(path);
41
+ }
42
+ async openFile(path, flag, cred) {
43
+ if (isWriteable(flag)) {
44
+ // You can't write to files on this file system.
45
+ throw new ErrnoError(Errno.EPERM, path);
46
+ }
47
+ // Check if the path exists, and is a file.
48
+ const stats = this.index.get(path);
49
+ if (!stats) {
50
+ throw ErrnoError.With('ENOENT', path, 'openFile');
51
+ }
52
+ if (!stats.hasAccess(flagToMode(flag), cred)) {
53
+ throw ErrnoError.With('EACCES', path, 'openFile');
54
+ }
55
+ return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : await this.getData(path, stats));
56
+ }
57
+ openFileSync(path, flag, cred) {
58
+ if (isWriteable(flag)) {
59
+ // You can't write to files on this file system.
60
+ throw new ErrnoError(Errno.EPERM, path);
61
+ }
62
+ // Check if the path exists, and is a file.
63
+ const stats = this.index.get(path);
64
+ if (!stats) {
65
+ throw ErrnoError.With('ENOENT', path, 'openFile');
66
+ }
67
+ if (!stats.hasAccess(flagToMode(flag), cred)) {
68
+ throw ErrnoError.With('EACCES', path, 'openFile');
69
+ }
70
+ return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));
71
+ }
72
+ async readdir(path) {
73
+ return this.readdirSync(path);
74
+ }
75
+ readdirSync(path) {
76
+ // Check if it exists.
77
+ const stats = this.index.get(path);
78
+ if (!stats) {
79
+ throw ErrnoError.With('ENOENT', path, 'readdir');
80
+ }
81
+ if (!stats.isDirectory()) {
82
+ throw ErrnoError.With('ENOTDIR', path, 'readdir');
83
+ }
84
+ return JSON.parse(decode(stats.fileData));
85
+ }
86
+ }
@@ -0,0 +1,42 @@
1
+ import { Stats, StatsLike } from '../../stats.js';
2
+ /**
3
+ * An Index in JSON form
4
+ * @internal
5
+ */
6
+ export interface IndexData {
7
+ version: 1;
8
+ entries: Record<string, StatsLike<number>>;
9
+ }
10
+ export declare const version = 1;
11
+ /**
12
+ * An index of files
13
+ * @internal
14
+ */
15
+ export declare class Index extends Map<string, Stats> {
16
+ constructor();
17
+ /**
18
+ * Convience method
19
+ */
20
+ files(): Map<string, Stats>;
21
+ /**
22
+ * Converts the index to JSON
23
+ */
24
+ toJSON(): IndexData;
25
+ /**
26
+ * Converts the index to a string
27
+ */
28
+ toString(): string;
29
+ /**
30
+ * Returns the files in the directory `dir`.
31
+ * This is expensive so it is only called once per directory.
32
+ */
33
+ protected dirEntries(dir: string): string[];
34
+ /**
35
+ * Loads the index from JSON data
36
+ */
37
+ fromJSON(json: IndexData): void;
38
+ /**
39
+ * Parses an index from a string
40
+ */
41
+ static parse(data: string): Index;
42
+ }
@@ -0,0 +1,83 @@
1
+ import { isJSON } from 'utilium';
2
+ import { Errno, ErrnoError } from '../../error.js';
3
+ import { Stats } from '../../stats.js';
4
+ import { encode } from '../../utils.js';
5
+ import { basename, dirname } from '../../emulation/path.js';
6
+ export const version = 1;
7
+ /**
8
+ * An index of files
9
+ * @internal
10
+ */
11
+ export class Index extends Map {
12
+ constructor() {
13
+ super();
14
+ }
15
+ /**
16
+ * Convience method
17
+ */
18
+ files() {
19
+ const files = new Map();
20
+ for (const [path, stats] of this) {
21
+ if (stats.isFile()) {
22
+ files.set(path, stats);
23
+ }
24
+ }
25
+ return files;
26
+ }
27
+ /**
28
+ * Converts the index to JSON
29
+ */
30
+ toJSON() {
31
+ return {
32
+ version,
33
+ entries: Object.fromEntries(this),
34
+ };
35
+ }
36
+ /**
37
+ * Converts the index to a string
38
+ */
39
+ toString() {
40
+ return JSON.stringify(this.toJSON());
41
+ }
42
+ /**
43
+ * Returns the files in the directory `dir`.
44
+ * This is expensive so it is only called once per directory.
45
+ */
46
+ dirEntries(dir) {
47
+ const entries = [];
48
+ for (const entry of this.keys()) {
49
+ if (dirname(entry) == dir) {
50
+ entries.push(basename(entry));
51
+ }
52
+ }
53
+ return entries;
54
+ }
55
+ /**
56
+ * Loads the index from JSON data
57
+ */
58
+ fromJSON(json) {
59
+ if (json.version != version) {
60
+ throw new ErrnoError(Errno.EINVAL, 'Index version mismatch');
61
+ }
62
+ this.clear();
63
+ for (const [path, data] of Object.entries(json.entries)) {
64
+ const stats = new Stats(data);
65
+ if (stats.isDirectory()) {
66
+ stats.fileData = encode(JSON.stringify(this.dirEntries(path)));
67
+ }
68
+ this.set(path, stats);
69
+ }
70
+ }
71
+ /**
72
+ * Parses an index from a string
73
+ */
74
+ static parse(data) {
75
+ if (!isJSON(data)) {
76
+ throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
77
+ }
78
+ const json = JSON.parse(data);
79
+ const index = new Index();
80
+ index.fromJSON(json);
81
+ return index;
82
+ }
83
+ }
@@ -40,7 +40,8 @@ export declare class PortFile extends File {
40
40
  type FSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<any> | FileSystemMetadata>;
41
41
  type FSMethod = keyof FSMethods;
42
42
  declare const PortFS_base: (abstract new (...args: any[]) => {
43
- _sync: FileSystem;
43
+ _disableSync: boolean;
44
+ _sync?: FileSystem | undefined;
44
45
  queueDone(): Promise<void>;
45
46
  metadata(): FileSystemMetadata;
46
47
  ready(): Promise<void>;