@zenfs/core 0.3.4 → 0.4.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 (44) hide show
  1. package/dist/FileIndex.d.ts +6 -0
  2. package/dist/FileIndex.js +3 -3
  3. package/dist/backends/AsyncMirror.d.ts +3 -5
  4. package/dist/backends/AsyncMirror.js +7 -6
  5. package/dist/backends/AsyncStore.d.ts +9 -4
  6. package/dist/backends/AsyncStore.js +7 -2
  7. package/dist/backends/Locked.d.ts +8 -8
  8. package/dist/backends/Locked.js +2 -1
  9. package/dist/backends/Overlay.d.ts +13 -1
  10. package/dist/backends/Overlay.js +16 -16
  11. package/dist/backends/SyncStore.d.ts +8 -5
  12. package/dist/backends/SyncStore.js +9 -5
  13. package/dist/backends/backend.js +8 -8
  14. package/dist/browser.min.js +4 -5
  15. package/dist/browser.min.js.map +4 -4
  16. package/dist/cred.d.ts +1 -1
  17. package/dist/cred.js +1 -1
  18. package/dist/emulation/callbacks.d.ts +1 -1
  19. package/dist/emulation/callbacks.js +1 -1
  20. package/dist/emulation/constants.d.ts +48 -42
  21. package/dist/emulation/constants.js +68 -59
  22. package/dist/emulation/dir.d.ts +2 -2
  23. package/dist/emulation/promises.d.ts +1 -1
  24. package/dist/emulation/promises.js +6 -6
  25. package/dist/emulation/sync.d.ts +1 -1
  26. package/dist/emulation/sync.js +7 -7
  27. package/dist/file.d.ts +26 -12
  28. package/dist/file.js +68 -29
  29. package/dist/filesystem.d.ts +3 -3
  30. package/dist/index.d.ts +6 -3
  31. package/dist/index.js +4 -5
  32. package/dist/inode.d.ts +21 -15
  33. package/dist/inode.js +52 -40
  34. package/dist/mutex.d.ts +1 -2
  35. package/dist/mutex.js +1 -1
  36. package/dist/stats.d.ts +70 -18
  37. package/dist/stats.js +12 -18
  38. package/dist/utils.d.ts +3 -8
  39. package/dist/utils.js +60 -39
  40. package/package.json +5 -1
  41. package/readme.md +14 -10
  42. package/scripts/make-index.js +100 -0
  43. package/dist/backends/index.d.ts +0 -10
  44. package/dist/backends/index.js +0 -12
@@ -211,10 +211,15 @@ declare const SyncFileIndexFS_base: (abstract new (...args: any[]) => {
211
211
  syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
212
212
  }) & (abstract new (index: ListingTree) => FileIndexFS<unknown>);
213
213
  export declare abstract class SyncFileIndexFS<TIndex> extends SyncFileIndexFS_base {
214
+ _index: FileIndex<TIndex>;
214
215
  protected statFileInode(inode: IndexFileInode<TIndex>, path: string): Promise<Stats>;
215
216
  protected openFileInode(inode: IndexFileInode<TIndex>, path: string, flag: FileFlag): Promise<NoSyncFile<this>>;
216
217
  }
217
218
  declare const AsyncFileIndexFS_base: (abstract new (...args: any[]) => {
219
+ /**
220
+ * Returns the inode for the indicated item, or null if it does not exist.
221
+ * @param path Name of item in this directory.
222
+ */
218
223
  metadata(): import("./filesystem.js").FileSystemMetadata;
219
224
  renameSync(oldPath: string, newPath: string, cred: Cred): void;
220
225
  statSync(path: string, cred: Cred): Stats;
@@ -228,6 +233,7 @@ declare const AsyncFileIndexFS_base: (abstract new (...args: any[]) => {
228
233
  syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
229
234
  }) & (abstract new (index: ListingTree) => FileIndexFS<unknown>);
230
235
  export declare abstract class AsyncFileIndexFS<TIndex> extends AsyncFileIndexFS_base {
236
+ _index: FileIndex<TIndex>;
231
237
  protected statFileInodeSync(): Stats;
232
238
  protected openFileInodeSync(): NoSyncFile<this>;
233
239
  }
package/dist/FileIndex.js CHANGED
@@ -40,7 +40,7 @@ export class FileIndex {
40
40
  }
41
41
  else {
42
42
  // This inode doesn't have correct size information, noted with -1.
43
- inode = new IndexFileInode(new Stats(FileType.FILE, -1, 0o555));
43
+ inode = new IndexFileInode(new Stats({ mode: FileType.FILE | 0o555 }));
44
44
  }
45
45
  if (!parent) {
46
46
  continue;
@@ -219,7 +219,7 @@ export class IndexFileInode extends IndexInode {
219
219
  return false;
220
220
  }
221
221
  toStats() {
222
- return new Stats(FileType.FILE, 4096, 0o666);
222
+ return new Stats({ mode: FileType.FILE | 0o666, size: 4096 });
223
223
  }
224
224
  }
225
225
  /**
@@ -244,7 +244,7 @@ export class IndexDirInode extends IndexInode {
244
244
  * @todo Should probably remove this at some point. This isn't the responsibility of the FileIndex.
245
245
  */
246
246
  get stats() {
247
- return new Stats(FileType.DIRECTORY, 4096, 0o555);
247
+ return new Stats({ mode: FileType.DIRECTORY | 0o555, size: 4096 });
248
248
  }
249
249
  /**
250
250
  * Alias of getStats()
@@ -1,12 +1,13 @@
1
1
  import { FileSystem, FileSystemMetadata } from '../filesystem.js';
2
2
  import { File, FileFlag, PreloadFile } from '../file.js';
3
- import { Stats } from '../stats.js';
3
+ import type { Stats } from '../stats.js';
4
4
  import { Cred } from '../cred.js';
5
5
  import type { Backend } from './backend.js';
6
6
  /**
7
7
  * We define our own file to interpose on syncSync() for mirroring purposes.
8
+ * @internal
8
9
  */
9
- declare class MirrorFile extends PreloadFile<AsyncMirrorFS> {
10
+ export declare class MirrorFile extends PreloadFile<AsyncMirrorFS> {
10
11
  constructor(fs: AsyncMirrorFS, path: string, flag: FileFlag, stat: Stats, data: Uint8Array);
11
12
  sync(): Promise<void>;
12
13
  syncSync(): void;
@@ -41,9 +42,6 @@ declare const AsyncMirrorFS_base: (abstract new (...args: any[]) => {
41
42
  link(srcpath: string, dstpath: string, cred: Cred): Promise<void>;
42
43
  sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void>;
43
44
  renameSync(oldPath: string, newPath: string, cred: Cred): void;
44
- /**
45
- * Queue of pending asynchronous operations.
46
- */
47
45
  statSync(path: string, cred: Cred): Stats;
48
46
  openFileSync(path: string, flag: FileFlag, cred: Cred): File;
49
47
  createFileSync(path: string, flag: FileFlag, mode: number, cred: Cred): File;
@@ -5,8 +5,9 @@ import { join } from '../emulation/path.js';
5
5
  import { Cred } from '../cred.js';
6
6
  /**
7
7
  * We define our own file to interpose on syncSync() for mirroring purposes.
8
+ * @internal
8
9
  */
9
- class MirrorFile extends PreloadFile {
10
+ export class MirrorFile extends PreloadFile {
10
11
  constructor(fs, path, flag, stat, data) {
11
12
  super(fs, path, flag, stat, data);
12
13
  }
@@ -153,8 +154,8 @@ export class AsyncMirrorFS extends Sync(FileSystem) {
153
154
  * @internal
154
155
  */
155
156
  async crossCopyFile(p, mode) {
156
- const asyncFile = await this._async.openFile(p, FileFlag.FromString('r'), Cred.Root);
157
- const syncFile = this._sync.createFileSync(p, FileFlag.FromString('w'), mode, Cred.Root);
157
+ const asyncFile = await this._async.openFile(p, FileFlag.Get('r'), Cred.Root);
158
+ const syncFile = this._sync.createFileSync(p, FileFlag.Get('w'), mode, Cred.Root);
158
159
  try {
159
160
  const { size } = await asyncFile.stat();
160
161
  const buffer = new Uint8Array(size);
@@ -230,9 +231,9 @@ export const AsyncMirror = {
230
231
  sync: {
231
232
  type: 'object',
232
233
  description: 'The synchronous file system to mirror the asynchronous file system to.',
233
- validator: async (v) => {
234
- if (!v?.metadata().synchronous) {
235
- throw new ApiError(ErrorCode.EINVAL, `'sync' option must be a file system that supports synchronous operations`);
234
+ validator: async (backend) => {
235
+ if ('metadata' in backend && !backend.metadata().synchronous) {
236
+ throw new ApiError(ErrorCode.EINVAL, '"sync" option must be a file system that supports synchronous operations');
236
237
  }
237
238
  },
238
239
  },
@@ -1,8 +1,8 @@
1
- import { Cred } from '../cred.js';
1
+ import type { Cred } from '../cred.js';
2
2
  import { PreloadFile, File, FileFlag } from '../file.js';
3
3
  import { FileSystem, type FileSystemMetadata } from '../filesystem.js';
4
4
  import { type Ino } from '../inode.js';
5
- import { Stats } from '../stats.js';
5
+ import { type Stats } from '../stats.js';
6
6
  /**
7
7
  * Represents an *asynchronous* key-value store.
8
8
  */
@@ -62,6 +62,10 @@ export interface AsyncRWTransaction extends AsyncROTransaction {
62
62
  */
63
63
  abort(): Promise<void>;
64
64
  }
65
+ /**
66
+ * Async preload file for usage with AsyncStore
67
+ * @internal
68
+ */
65
69
  export declare class AsyncFile extends PreloadFile<AsyncStoreFS> {
66
70
  constructor(_fs: AsyncStoreFS, _path: string, _flag: FileFlag, _stat: Stats, contents?: Uint8Array);
67
71
  sync(): Promise<void>;
@@ -93,8 +97,9 @@ declare const AsyncStoreFS_base: (abstract new (...args: any[]) => {
93
97
  syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
94
98
  }) & typeof FileSystem;
95
99
  /**
96
- * An "Asynchronous key-value file system". Stores data to/retrieves data from
97
- * an underlying asynchronous key-value store.
100
+ * An asynchronous file system which uses an async store to store its data.
101
+ * @see AsyncStore
102
+ * @internal
98
103
  */
99
104
  export declare class AsyncStoreFS extends AsyncStoreFS_base {
100
105
  protected store: AsyncStore;
@@ -44,6 +44,10 @@ class LRUCache {
44
44
  this.cache = [];
45
45
  }
46
46
  }
47
+ /**
48
+ * Async preload file for usage with AsyncStore
49
+ * @internal
50
+ */
47
51
  export class AsyncFile extends PreloadFile {
48
52
  constructor(_fs, _path, _flag, _stat, contents) {
49
53
  super(_fs, _path, _flag, _stat, contents);
@@ -66,8 +70,9 @@ export class AsyncFile extends PreloadFile {
66
70
  }
67
71
  }
68
72
  /**
69
- * An "Asynchronous key-value file system". Stores data to/retrieves data from
70
- * an underlying asynchronous key-value store.
73
+ * An asynchronous file system which uses an async store to store its data.
74
+ * @see AsyncStore
75
+ * @internal
71
76
  */
72
77
  export class AsyncStoreFS extends Async(FileSystem) {
73
78
  ready() {
@@ -1,8 +1,7 @@
1
- import { FileSystem, FileSystemMetadata } from '../filesystem.js';
2
- import { FileFlag } from '../file.js';
3
- import { Stats } from '../stats.js';
4
- import { File } from '../file.js';
5
- import { Cred } from '../cred.js';
1
+ import type { Cred } from '../cred.js';
2
+ import type { File, FileFlag } from '../file.js';
3
+ import type { FileSystem, FileSystemMetadata } from '../filesystem.js';
4
+ import type { Stats } from '../stats.js';
6
5
  /**
7
6
  * This class serializes access to an underlying async filesystem.
8
7
  * For example, on an OverlayFS instance with an async lower
@@ -11,11 +10,12 @@ import { Cred } from '../cred.js';
11
10
  * are not executed in a single atomic step. OverlayFS uses this
12
11
  * LockedFS to avoid having to reason about the correctness of
13
12
  * multiple requests interleaving.
13
+ * @internal
14
14
  */
15
- export declare class LockedFS<T extends FileSystem> implements FileSystem {
16
- readonly fs: T;
15
+ export declare class LockedFS<FS extends FileSystem> implements FileSystem {
16
+ readonly fs: FS;
17
17
  private _mu;
18
- constructor(fs: T);
18
+ constructor(fs: FS);
19
19
  ready(): Promise<this>;
20
20
  metadata(): FileSystemMetadata;
21
21
  rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
@@ -1,4 +1,4 @@
1
- import Mutex from '../mutex.js';
1
+ import { Mutex } from '../mutex.js';
2
2
  /**
3
3
  * This class serializes access to an underlying async filesystem.
4
4
  * For example, on an OverlayFS instance with an async lower
@@ -7,6 +7,7 @@ import Mutex from '../mutex.js';
7
7
  * are not executed in a single atomic step. OverlayFS uses this
8
8
  * LockedFS to avoid having to reason about the correctness of
9
9
  * multiple requests interleaving.
10
+ * @internal
10
11
  */
11
12
  export class LockedFS {
12
13
  constructor(fs) {
@@ -1,9 +1,20 @@
1
1
  import { FileSystem, FileSystemMetadata } from '../filesystem.js';
2
- import { File, FileFlag } from '../file.js';
2
+ import { File, FileFlag, PreloadFile } from '../file.js';
3
3
  import { Stats } from '../stats.js';
4
4
  import { LockedFS } from './Locked.js';
5
5
  import { Cred } from '../cred.js';
6
6
  import type { Backend } from './backend.js';
7
+ /**
8
+ * Overlays a RO file to make it writable.
9
+ * @internal
10
+ */
11
+ export declare class OverlayFile extends PreloadFile<UnlockedOverlayFS> implements File {
12
+ constructor(fs: UnlockedOverlayFS, path: string, flag: FileFlag, stats: Stats, data: Uint8Array);
13
+ sync(): Promise<void>;
14
+ syncSync(): void;
15
+ close(): Promise<void>;
16
+ closeSync(): void;
17
+ }
7
18
  /**
8
19
  * Configuration options for OverlayFS instances.
9
20
  */
@@ -95,6 +106,7 @@ export declare class UnlockedOverlayFS extends FileSystem {
95
106
  * OverlayFS makes a read-only filesystem writable by storing writes on a second,
96
107
  * writable file system. Deletes are persisted via metadata stored on the writable
97
108
  * file system.
109
+ * @internal
98
110
  */
99
111
  export declare class OverlayFS extends LockedFS<UnlockedOverlayFS> {
100
112
  ready(): Promise<this>;
@@ -12,8 +12,9 @@ import { decode, encode } from '../utils.js';
12
12
  const deletionLogPath = '/.deleted';
13
13
  /**
14
14
  * Overlays a RO file to make it writable.
15
+ * @internal
15
16
  */
16
- class OverlayFile extends PreloadFile {
17
+ export class OverlayFile extends PreloadFile {
17
18
  constructor(fs, path, flag, stats, data) {
18
19
  super(fs, path, flag, stats, data);
19
20
  }
@@ -103,7 +104,7 @@ export class UnlockedOverlayFS extends FileSystem {
103
104
  }
104
105
  // Read deletion log, process into metadata.
105
106
  try {
106
- const file = await this._writable.openFile(deletionLogPath, FileFlag.FromString('r'), Cred.Root);
107
+ const file = await this._writable.openFile(deletionLogPath, FileFlag.Get('r'), Cred.Root);
107
108
  const { size } = await file.stat();
108
109
  const { buffer } = await file.read(new Uint8Array(size));
109
110
  this._deleteLog = decode(buffer);
@@ -159,9 +160,8 @@ export class UnlockedOverlayFS extends FileSystem {
159
160
  if (this._deletedFiles.has(p)) {
160
161
  throw ApiError.ENOENT(p);
161
162
  }
162
- const oldStat = Stats.clone(await this._readable.stat(p, cred));
163
- // Make the oldStat's mode writable. Preserve the topmost part of the
164
- // mode, which specifies if it is a file or a directory.
163
+ const oldStat = new Stats(await this._readable.stat(p, cred));
164
+ // Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type
165
165
  oldStat.mode |= 0o222;
166
166
  return oldStat;
167
167
  }
@@ -175,9 +175,8 @@ export class UnlockedOverlayFS extends FileSystem {
175
175
  if (this._deletedFiles.has(p)) {
176
176
  throw ApiError.ENOENT(p);
177
177
  }
178
- const oldStat = Stats.clone(this._readable.statSync(p, cred));
179
- // Make the oldStat's mode writable. Preserve the topmost part of the
180
- // mode, which specifies if it is a file or a directory.
178
+ const oldStat = new Stats(this._readable.statSync(p, cred));
179
+ // Make the oldStat's mode writable. Preserve the topmost part of the mode, which specifies the type.
181
180
  oldStat.mode |= 0o222;
182
181
  return oldStat;
183
182
  }
@@ -187,8 +186,8 @@ export class UnlockedOverlayFS extends FileSystem {
187
186
  return this._writable.openFile(path, flag, cred);
188
187
  }
189
188
  // Create an OverlayFile.
190
- const file = await this._readable.openFile(path, FileFlag.FromString('r'), cred);
191
- const stats = Stats.clone(await file.stat());
189
+ const file = await this._readable.openFile(path, FileFlag.Get('r'), cred);
190
+ const stats = new Stats(await file.stat());
192
191
  const { buffer } = await file.read(new Uint8Array(stats.size));
193
192
  return new OverlayFile(this, path, flag, stats, buffer);
194
193
  }
@@ -197,7 +196,7 @@ export class UnlockedOverlayFS extends FileSystem {
197
196
  return this._writable.openFileSync(path, flag, cred);
198
197
  }
199
198
  // Create an OverlayFile.
200
- const file = this._readable.openFileSync(path, FileFlag.FromString('r'), cred);
199
+ const file = this._readable.openFileSync(path, FileFlag.Get('r'), cred);
201
200
  const stats = Stats.clone(file.statSync());
202
201
  const data = new Uint8Array(stats.size);
203
202
  file.readSync(data);
@@ -368,7 +367,7 @@ export class UnlockedOverlayFS extends FileSystem {
368
367
  return;
369
368
  }
370
369
  this._deleteLogUpdatePending = true;
371
- const log = await this._writable.openFile(deletionLogPath, FileFlag.FromString('w'), cred);
370
+ const log = await this._writable.openFile(deletionLogPath, FileFlag.Get('w'), cred);
372
371
  try {
373
372
  await log.write(encode(this._deleteLog));
374
373
  if (this._deleteLogUpdateNeeded) {
@@ -470,10 +469,10 @@ export class UnlockedOverlayFS extends FileSystem {
470
469
  return;
471
470
  }
472
471
  const data = new Uint8Array(stats.size);
473
- const readable = this._readable.openFileSync(p, FileFlag.FromString('r'), cred);
472
+ const readable = this._readable.openFileSync(p, FileFlag.Get('r'), cred);
474
473
  readable.readSync(data);
475
474
  readable.closeSync();
476
- const writable = this._writable.openFileSync(p, FileFlag.FromString('w'), cred);
475
+ const writable = this._writable.openFileSync(p, FileFlag.Get('w'), cred);
477
476
  writable.writeSync(data);
478
477
  writable.closeSync();
479
478
  }
@@ -484,10 +483,10 @@ export class UnlockedOverlayFS extends FileSystem {
484
483
  return;
485
484
  }
486
485
  const data = new Uint8Array(stats.size);
487
- const readable = await this._readable.openFile(p, FileFlag.FromString('r'), cred);
486
+ const readable = await this._readable.openFile(p, FileFlag.Get('r'), cred);
488
487
  await readable.read(data);
489
488
  await readable.close();
490
- const writable = await this._writable.openFile(p, FileFlag.FromString('w'), cred);
489
+ const writable = await this._writable.openFile(p, FileFlag.Get('w'), cred);
491
490
  await writable.write(data);
492
491
  await writable.close();
493
492
  }
@@ -496,6 +495,7 @@ export class UnlockedOverlayFS extends FileSystem {
496
495
  * OverlayFS makes a read-only filesystem writable by storing writes on a second,
497
496
  * writable file system. Deletes are persisted via metadata stored on the writable
498
497
  * file system.
498
+ * @internal
499
499
  */
500
500
  export class OverlayFS extends LockedFS {
501
501
  async ready() {
@@ -2,7 +2,7 @@ import { Cred } from '../cred.js';
2
2
  import { FileFlag, PreloadFile } from '../file.js';
3
3
  import { type FileSystemMetadata, FileSystem } from '../filesystem.js';
4
4
  import { type Ino, Inode } from '../inode.js';
5
- import { Stats, FileType } from '../stats.js';
5
+ import { type Stats, FileType } from '../stats.js';
6
6
  /**
7
7
  * Represents a *synchronous* key-value store.
8
8
  */
@@ -112,6 +112,10 @@ export interface SyncStoreOptions {
112
112
  */
113
113
  store: SyncStore;
114
114
  }
115
+ /**
116
+ * File backend by a SyncStoreFS
117
+ * @internal
118
+ */
115
119
  export declare class SyncStoreFile extends PreloadFile<SyncStoreFS> {
116
120
  constructor(_fs: SyncStoreFS, _path: string, _flag: FileFlag, _stat: Stats, contents?: Uint8Array);
117
121
  sync(): Promise<void>;
@@ -146,13 +150,12 @@ declare const SyncStoreFS_base: (abstract new (...args: any[]) => {
146
150
  syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void;
147
151
  }) & typeof FileSystem;
148
152
  /**
149
- * A "Synchronous key-value file system". Stores data to/retrieves data from an
150
- * underlying key-value store.
153
+ * A synchronous key-value file system. Uses a SyncStore to store the data.
151
154
  *
152
- * We use a unique ID for each node in the file system. The root node has a
153
- * fixed ID.
155
+ * We use a unique ID for each node in the file system. The root node has a fixed ID.
154
156
  * @todo Introduce Node ID caching.
155
157
  * @todo Check modes.
158
+ * @internal
156
159
  */
157
160
  export declare class SyncStoreFS extends SyncStoreFS_base {
158
161
  protected store: SyncStore;
@@ -76,6 +76,10 @@ export class SimpleSyncRWTransaction {
76
76
  }
77
77
  }
78
78
  }
79
+ /**
80
+ * File backend by a SyncStoreFS
81
+ * @internal
82
+ */
79
83
  export class SyncStoreFile extends PreloadFile {
80
84
  constructor(_fs, _path, _flag, _stat, contents) {
81
85
  super(_fs, _path, _flag, _stat, contents);
@@ -97,13 +101,12 @@ export class SyncStoreFile extends PreloadFile {
97
101
  }
98
102
  }
99
103
  /**
100
- * A "Synchronous key-value file system". Stores data to/retrieves data from an
101
- * underlying key-value store.
104
+ * A synchronous key-value file system. Uses a SyncStore to store the data.
102
105
  *
103
- * We use a unique ID for each node in the file system. The root node has a
104
- * fixed ID.
106
+ * We use a unique ID for each node in the file system. The root node has a fixed ID.
105
107
  * @todo Introduce Node ID caching.
106
108
  * @todo Check modes.
109
+ * @internal
107
110
  */
108
111
  export class SyncStoreFS extends Sync(FileSystem) {
109
112
  constructor(options) {
@@ -347,7 +350,8 @@ export class SyncStoreFS extends Sync(FileSystem) {
347
350
  if (!data) {
348
351
  throw ApiError.ENOENT(p);
349
352
  }
350
- return new Inode(data.buffer);
353
+ const inode = new Inode(data.buffer);
354
+ return inode;
351
355
  }
352
356
  /**
353
357
  * Given the Inode of a directory, retrieves the corresponding directory listing.
@@ -16,7 +16,7 @@ export async function checkOptions(backend, opts) {
16
16
  }
17
17
  // Check for required options.
18
18
  for (const [optName, opt] of Object.entries(backend.options)) {
19
- const providedValue = opts && opt;
19
+ const providedValue = opts?.[optName];
20
20
  if (providedValue === undefined || providedValue === null) {
21
21
  if (!opt.required) {
22
22
  continue;
@@ -36,7 +36,7 @@ export async function checkOptions(backend, opts) {
36
36
  // Option provided, check type.
37
37
  const typeMatches = Array.isArray(opt.type) ? opt.type.indexOf(typeof providedValue) != -1 : typeof providedValue == opt.type;
38
38
  if (!typeMatches) {
39
- throw new ApiError(ErrorCode.EINVAL, `${backend.name}: 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}`);
39
+ throw new ApiError(ErrorCode.EINVAL, `${backend.name}: 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}`);
40
40
  }
41
41
  if (opt.validator) {
42
42
  await opt.validator(providedValue);
@@ -60,13 +60,13 @@ export function isBackendConfig(arg) {
60
60
  * @param config A BackendConfig object.
61
61
  */
62
62
  export async function resolveBackendConfig(options) {
63
- const { backend } = options;
64
- if (!backend) {
65
- throw new ApiError(ErrorCode.EPERM, 'Missing backend');
66
- }
67
63
  if (typeof options !== 'object' || options == null) {
68
64
  throw new ApiError(ErrorCode.EINVAL, 'Invalid options on configuration object.');
69
65
  }
66
+ const { backend } = options;
67
+ if (!isBackend(backend)) {
68
+ throw new ApiError(ErrorCode.EINVAL, 'Missing or invalid backend');
69
+ }
70
70
  const props = Object.keys(options).filter(k => k != 'backend');
71
71
  for (const prop of props) {
72
72
  let option = options[prop];
@@ -77,8 +77,8 @@ export async function resolveBackendConfig(options) {
77
77
  options[prop] = await resolveBackendConfig(option);
78
78
  }
79
79
  }
80
- if (!backend) {
81
- throw new ApiError(ErrorCode.EPERM, `Backend "${backend}" is not available`);
80
+ if (!backend.isAvailable()) {
81
+ throw new ApiError(ErrorCode.EPERM, 'Backend not available: ' + backend);
82
82
  }
83
83
  checkOptions(backend, options);
84
84
  const fs = backend.create(options);