@zenfs/core 0.10.0 → 0.11.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 (61) hide show
  1. package/dist/backends/Index.d.ts +3 -3
  2. package/dist/backends/Index.js +4 -4
  3. package/dist/backends/backend.js +2 -1
  4. package/dist/backends/{Fetch.d.ts → fetch.d.ts} +4 -4
  5. package/dist/backends/{Fetch.js → fetch.js} +6 -7
  6. package/dist/backends/{Locked.d.ts → locked.d.ts} +2 -2
  7. package/dist/backends/{Locked.js → locked.js} +4 -5
  8. package/dist/backends/{InMemory.d.ts → memory.d.ts} +7 -9
  9. package/dist/backends/memory.js +38 -0
  10. package/dist/backends/{Overlay.d.ts → overlay.d.ts} +12 -13
  11. package/dist/backends/{Overlay.js → overlay.js} +98 -103
  12. package/dist/backends/port/fs.d.ts +15 -16
  13. package/dist/backends/port/fs.js +20 -22
  14. package/dist/backends/store/fs.d.ts +169 -0
  15. package/dist/backends/store/fs.js +743 -0
  16. package/dist/backends/store/simple.d.ts +64 -0
  17. package/dist/backends/store/simple.js +111 -0
  18. package/dist/backends/store/store.d.ts +111 -0
  19. package/dist/backends/store/store.js +62 -0
  20. package/dist/browser.min.js +4 -4
  21. package/dist/browser.min.js.map +4 -4
  22. package/dist/config.d.ts +1 -1
  23. package/dist/emulation/shared.js +1 -1
  24. package/dist/error.d.ts +0 -1
  25. package/dist/error.js +0 -1
  26. package/dist/file.js +1 -1
  27. package/dist/filesystem.d.ts +3 -3
  28. package/dist/filesystem.js +26 -29
  29. package/dist/index.d.ts +5 -7
  30. package/dist/index.js +5 -7
  31. package/dist/inode.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/backends/Index.ts +4 -4
  34. package/src/backends/backend.ts +2 -1
  35. package/src/backends/{Fetch.ts → fetch.ts} +13 -14
  36. package/src/backends/{Locked.ts → locked.ts} +5 -6
  37. package/src/backends/memory.ts +44 -0
  38. package/src/backends/{Overlay.ts → overlay.ts} +99 -105
  39. package/src/backends/port/fs.ts +24 -26
  40. package/src/backends/store/fs.ts +881 -0
  41. package/src/backends/store/readme.md +9 -0
  42. package/src/backends/store/simple.ts +144 -0
  43. package/src/backends/store/store.ts +164 -0
  44. package/src/config.ts +2 -2
  45. package/src/emulation/shared.ts +1 -1
  46. package/src/error.ts +0 -1
  47. package/src/file.ts +1 -1
  48. package/src/filesystem.ts +29 -32
  49. package/src/index.ts +5 -7
  50. package/src/inode.ts +1 -1
  51. package/dist/backends/AsyncStore.d.ts +0 -204
  52. package/dist/backends/AsyncStore.js +0 -509
  53. package/dist/backends/InMemory.js +0 -49
  54. package/dist/backends/SyncStore.d.ts +0 -213
  55. package/dist/backends/SyncStore.js +0 -445
  56. package/dist/backends/port/store.d.ts +0 -30
  57. package/dist/backends/port/store.js +0 -142
  58. package/src/backends/AsyncStore.ts +0 -655
  59. package/src/backends/InMemory.ts +0 -56
  60. package/src/backends/SyncStore.ts +0 -589
  61. package/src/backends/port/store.ts +0 -187
@@ -0,0 +1,9 @@
1
+ # Store
2
+
3
+ While `StoreFS`, `Store`, etc. don't provide any backends directly, they are invaluable for creating new backends with a minimal amount of code.
4
+
5
+ `StoreFS` implements the all of `FileSystem` using a `Store`.
6
+
7
+ `Store` and `Transaction` are simple interfaces which are used by `StoreFS`.
8
+
9
+ In [simple.ts](./simple.ts) you can find `SimpleSyncStore`, `SimpleAsyncStore`, and `SimpleTransaction`. These classes provide an even more simple interface. This means backends like `InMemory` can be implemented with a very small amount of code.
@@ -0,0 +1,144 @@
1
+ import type { Ino } from '../../inode.js';
2
+ import { type Store, SyncTransaction } from './store.js';
3
+
4
+ /**
5
+ * An interface for simple synchronous stores that don't have special support for transactions and such.
6
+ */
7
+ export interface SimpleSyncStore extends Store {
8
+ get(ino: Ino): Uint8Array | undefined;
9
+ set(ino: Ino, data: Uint8Array): void;
10
+ delete(ino: Ino): void;
11
+ }
12
+
13
+ /**
14
+ * An interface for simple asynchronous stores that don't have special support for transactions and such.
15
+ * This class adds caching at the store level.
16
+ */
17
+ export abstract class SimpleAsyncStore implements SimpleSyncStore {
18
+ public abstract name: string;
19
+
20
+ protected cache: Map<Ino, Uint8Array> = new Map();
21
+
22
+ protected queue: Set<Promise<unknown>> = new Set();
23
+
24
+ protected abstract entries(): Promise<Iterable<[Ino, Uint8Array]>>;
25
+
26
+ public get(ino: Ino): Uint8Array | undefined {
27
+ return this.cache.get(ino);
28
+ }
29
+
30
+ public set(ino: Ino, data: Uint8Array): void {
31
+ this.cache.set(ino, data);
32
+ this.queue.add(this._set(ino, data));
33
+ }
34
+
35
+ protected abstract _set(ino: Ino, data: Uint8Array): Promise<void>;
36
+
37
+ public delete(ino: Ino): void {
38
+ this.cache.delete(ino);
39
+ this.queue.add(this._delete(ino));
40
+ }
41
+
42
+ protected abstract _delete(ino: Ino): Promise<void>;
43
+
44
+ public clearSync(): void {
45
+ this.cache.clear();
46
+ this.queue.add(this.clear());
47
+ }
48
+
49
+ public abstract clear(): Promise<void>;
50
+
51
+ public async sync(): Promise<void> {
52
+ for (const [ino, data] of await this.entries()) {
53
+ if (!this.cache.has(ino)) {
54
+ this.cache.set(ino, data);
55
+ }
56
+ }
57
+ for (const promise of this.queue) {
58
+ await promise;
59
+ }
60
+ }
61
+
62
+ public transaction(): SimpleTransaction {
63
+ return new SimpleTransaction(this);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Transaction for simple stores.
69
+ * @see SimpleSyncStore
70
+ * @see SimpleAsyncStore
71
+ */
72
+ export class SimpleTransaction extends SyncTransaction {
73
+ /**
74
+ * Stores data in the keys we modify prior to modifying them.
75
+ * Allows us to roll back commits.
76
+ */
77
+ protected originalData: Map<Ino, Uint8Array | void> = new Map();
78
+ /**
79
+ * List of keys modified in this transaction, if any.
80
+ */
81
+ protected modifiedKeys: Set<Ino> = new Set();
82
+
83
+ constructor(protected store: SimpleSyncStore) {
84
+ super();
85
+ }
86
+
87
+ public getSync(ino: Ino): Uint8Array {
88
+ const val = this.store.get(ino);
89
+ this.stashOldValue(ino, val);
90
+ return val!;
91
+ }
92
+
93
+ public setSync(ino: Ino, data: Uint8Array): void {
94
+ this.markModified(ino);
95
+ return this.store.set(ino, data);
96
+ }
97
+
98
+ public removeSync(ino: Ino): void {
99
+ this.markModified(ino);
100
+ this.store.delete(ino);
101
+ }
102
+
103
+ public commitSync(): void {
104
+ /* NOP */
105
+ }
106
+
107
+ public abortSync(): void {
108
+ // Rollback old values.
109
+ for (const key of this.modifiedKeys) {
110
+ const value = this.originalData.get(key);
111
+ if (!value) {
112
+ // Key didn't exist.
113
+ this.store.delete(key);
114
+ } else {
115
+ // Key existed. Store old value.
116
+ this.store.set(key, value);
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Stashes given key value pair into `originalData` if it doesn't already
123
+ * exist. Allows us to stash values the program is requesting anyway to
124
+ * prevent needless `get` requests if the program modifies the data later
125
+ * on during the transaction.
126
+ */
127
+ protected stashOldValue(ino: Ino, value?: Uint8Array): void {
128
+ // Keep only the earliest value in the transaction.
129
+ if (!this.originalData.has(ino)) {
130
+ this.originalData.set(ino, value);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Marks the given key as modified, and stashes its value if it has not been
136
+ * stashed already.
137
+ */
138
+ protected markModified(ino: Ino): void {
139
+ this.modifiedKeys.add(ino);
140
+ if (!this.originalData.has(ino)) {
141
+ this.originalData.set(ino, this.store.get(ino));
142
+ }
143
+ }
144
+ }
@@ -0,0 +1,164 @@
1
+ import { ErrnoError } from '../../error.js';
2
+ import type { Ino } from '../../inode.js';
3
+
4
+ /**
5
+ * Represents a key-value store.
6
+ */
7
+ export interface Store {
8
+ /**
9
+ * The name of the store.
10
+ */
11
+ name: string;
12
+
13
+ /**
14
+ * Syncs the store
15
+ */
16
+ sync(): Promise<void>;
17
+
18
+ /**
19
+ * Empties the store completely.
20
+ */
21
+ clear(): Promise<void> | void;
22
+
23
+ /**
24
+ * Empties the store completely.
25
+ */
26
+ clearSync(): void;
27
+
28
+ /**
29
+ * Begins a new transaction.
30
+ */
31
+ transaction(): Transaction;
32
+ }
33
+
34
+ /**
35
+ * A transaction for a synchronous store.
36
+ */
37
+ export abstract class Transaction {
38
+ protected aborted: boolean = false;
39
+
40
+ /**
41
+ * Retrieves the data at the given key.
42
+ * @param ino The key to look under for data.
43
+ */
44
+ public abstract get(ino: Ino): Promise<Uint8Array>;
45
+
46
+ /**
47
+ * Retrieves the data at the given key. Throws an error if an error occurs
48
+ * or if the key does not exist.
49
+ * @param ino The key to look under for data.
50
+ * @return The data stored under the key, or undefined if not present.
51
+ */
52
+ public abstract getSync(ino: Ino): Uint8Array;
53
+
54
+ /**
55
+ * Adds the data to the store under the given key. Overwrites any existing
56
+ * data.
57
+ * @param ino The key to add the data under.
58
+ * @param data The data to add to the store.
59
+ * @param overwrite If 'true', overwrite any existing data. If 'false',
60
+ * avoids writing the data if the key exists.
61
+ */
62
+ public abstract set(ino: Ino, data: Uint8Array): Promise<void>;
63
+
64
+ /**
65
+ * Adds the data to the store under the given key.
66
+ * @param ino The key to add the data under.
67
+ * @param data The data to add to the store.
68
+ * @param overwrite If 'true', overwrite any existing data. If 'false',
69
+ * avoids storing the data if the key exists.
70
+ * @return True if storage succeeded, false otherwise.
71
+ */
72
+ public abstract setSync(ino: Ino, data: Uint8Array): void;
73
+
74
+ /**
75
+ * Deletes the data at the given key.
76
+ * @param ino The key to delete from the store.
77
+ */
78
+ public abstract remove(ino: Ino): Promise<void>;
79
+
80
+ /**
81
+ * Deletes the data at the given key.
82
+ * @param ino The key to delete from the store.
83
+ */
84
+ public abstract removeSync(ino: Ino): void;
85
+
86
+ /**
87
+ * Commits the transaction.
88
+ */
89
+ public abstract commit(): Promise<void>;
90
+
91
+ public async [Symbol.asyncDispose]() {
92
+ if (this.aborted) {
93
+ return;
94
+ }
95
+
96
+ await this.commit();
97
+ }
98
+
99
+ /**
100
+ * Commits the transaction.
101
+ */
102
+ public abstract commitSync(): void;
103
+
104
+ public [Symbol.dispose]() {
105
+ if (this.aborted) {
106
+ return;
107
+ }
108
+
109
+ this.commitSync();
110
+ }
111
+
112
+ /**
113
+ * Aborts and rolls back the transaction.
114
+ */
115
+ public abstract abort(): Promise<void>;
116
+
117
+ /**
118
+ * Aborts and rolls back the transaction.
119
+ */
120
+ public abstract abortSync(): void;
121
+ }
122
+
123
+ /**
124
+ * Transaction that implements asynchronous operations with synchronous ones
125
+ */
126
+ export abstract class SyncTransaction extends Transaction {
127
+ public async get(ino: Ino): Promise<Uint8Array> {
128
+ return this.getSync(ino);
129
+ }
130
+ public async set(ino: bigint, data: Uint8Array): Promise<void> {
131
+ return this.setSync(ino, data);
132
+ }
133
+ public async remove(ino: Ino): Promise<void> {
134
+ return this.removeSync(ino);
135
+ }
136
+ public async commit(): Promise<void> {
137
+ return this.commitSync();
138
+ }
139
+ public async abort(): Promise<void> {
140
+ return this.abortSync();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Transaction that only supports asynchronous operations
146
+ * @todo Add caching
147
+ */
148
+ export abstract class AsyncTransaction extends Transaction {
149
+ public getSync(ino: Ino): Uint8Array {
150
+ throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.getSync');
151
+ }
152
+ public setSync(ino: bigint, data: Uint8Array): void {
153
+ throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.setSync');
154
+ }
155
+ public removeSync(ino: bigint): void {
156
+ throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.removeSync');
157
+ }
158
+ public commitSync(): void {
159
+ throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.commitSync');
160
+ }
161
+ public abortSync(): void {
162
+ throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.abortSync');
163
+ }
164
+ }
package/src/config.ts CHANGED
@@ -33,7 +33,7 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
33
33
  }
34
34
 
35
35
  if (isBackend(config)) {
36
- config = <BackendConfiguration<Backend<FS, TOptions>>>{ backend: config };
36
+ config = { backend: config } as BackendConfiguration<Backend<FS, TOptions>>;
37
37
  }
38
38
 
39
39
  for (const [key, value] of Object.entries(config)) {
@@ -76,7 +76,7 @@ export interface Configuration {
76
76
  * Creates filesystems with the given configuration, and initializes ZenFS with it.
77
77
  * @see Configuration for more info on the configuration object.
78
78
  */
79
- export async function configure(config: MountConfiguration | Configuration): Promise<void> {
79
+ export async function configure<T extends MountConfiguration | Configuration>(config: T | Configuration): Promise<void> {
80
80
  const uid = 'uid' in config ? config.uid || 0 : 0;
81
81
  const gid = 'gid' in config ? config.gid || 0 : 0;
82
82
 
@@ -1,7 +1,7 @@
1
1
  // Utilities and shared data
2
2
 
3
3
  import { ErrnoError, Errno } from '../error.js';
4
- import { InMemory } from '../backends/InMemory.js';
4
+ import { InMemory } from '../backends/memory.js';
5
5
  import { Cred, rootCred } from '../cred.js';
6
6
  import type { File } from '../file.js';
7
7
  import { FileSystem } from '../filesystem.js';
package/src/error.ts CHANGED
@@ -269,7 +269,6 @@ export class ErrnoError extends Error implements NodeJS.ErrnoException {
269
269
  *
270
270
  * Error codes mirror those returned by regular Unix file operations, which is
271
271
  * what Node returns.
272
- * @constructor ApiError
273
272
  * @param type The type of the error.
274
273
  * @param message A descriptive error message.
275
274
  */
package/src/file.ts CHANGED
@@ -620,7 +620,7 @@ export class PreloadFile<FS extends FileSystem> extends File {
620
620
  // No copy/read. Return immediatly for better performance
621
621
  return bytesRead;
622
622
  }
623
- new Uint8Array(buffer.buffer, 0, length).set(this._buffer.slice(position, end), offset);
623
+ new Uint8Array(buffer.buffer, offset, length).set(this._buffer.slice(position, end));
624
624
  return bytesRead;
625
625
  }
626
626
 
package/src/filesystem.ts CHANGED
@@ -59,9 +59,7 @@ export abstract class FileSystem {
59
59
  // unused
60
60
  }
61
61
 
62
- public async ready(): Promise<this> {
63
- return this;
64
- }
62
+ public async ready(): Promise<void> {}
65
63
 
66
64
  /**
67
65
  * Asynchronous rename. No arguments other than a possible exception
@@ -201,7 +199,7 @@ export abstract class FileSystem {
201
199
  */
202
200
  declare abstract class SyncFileSystem extends FileSystem {
203
201
  metadata(): FileSystemMetadata;
204
- ready(): Promise<this>;
202
+ ready(): Promise<void>;
205
203
  exists(path: string, cred: Cred): Promise<boolean>;
206
204
  rename(oldPath: string, newPath: string, cred: Cred): Promise<void>;
207
205
  stat(path: string, cred: Cred): Promise<Stats>;
@@ -278,7 +276,7 @@ declare abstract class AsyncFileSystem extends FileSystem {
278
276
  abstract _sync: FileSystem;
279
277
  queueDone(): Promise<void>;
280
278
  metadata(): FileSystemMetadata;
281
- ready(): Promise<this>;
279
+ ready(): Promise<void>;
282
280
  renameSync(oldPath: string, newPath: string, cred: Cred): void;
283
281
  statSync(path: string, cred: Cred): Stats;
284
282
  createFileSync(path: string, flag: string, mode: number, cred: Cred): File;
@@ -335,11 +333,11 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
335
333
 
336
334
  abstract _sync: FileSystem;
337
335
 
338
- public async ready(): Promise<this> {
336
+ public async ready(): Promise<void> {
339
337
  await this._sync.ready();
340
338
  await super.ready();
341
339
  if (this._isInitialized) {
342
- return this;
340
+ return;
343
341
  }
344
342
 
345
343
  try {
@@ -349,7 +347,6 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
349
347
  this._isInitialized = false;
350
348
  throw e;
351
349
  }
352
- return this;
353
350
  }
354
351
 
355
352
  public renameSync(oldPath: string, newPath: string, cred: Cred): void {
@@ -357,8 +354,8 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
357
354
  this.queue('rename', oldPath, newPath, cred);
358
355
  }
359
356
 
360
- public statSync(p: string, cred: Cred): Stats {
361
- return this._sync.statSync(p, cred);
357
+ public statSync(path: string, cred: Cred): Stats {
358
+ return this._sync.statSync(path, cred);
362
359
  }
363
360
 
364
361
  public createFileSync(path: string, flag: string, mode: number, cred: Cred): PreloadFile<this> {
@@ -374,23 +371,23 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
374
371
  return this._sync.openFileSync(path, flag, cred);
375
372
  }
376
373
 
377
- public unlinkSync(p: string, cred: Cred): void {
378
- this._sync.unlinkSync(p, cred);
379
- this.queue('unlink', p, cred);
374
+ public unlinkSync(path: string, cred: Cred): void {
375
+ this._sync.unlinkSync(path, cred);
376
+ this.queue('unlink', path, cred);
380
377
  }
381
378
 
382
- public rmdirSync(p: string, cred: Cred): void {
383
- this._sync.rmdirSync(p, cred);
384
- this.queue('rmdir', p, cred);
379
+ public rmdirSync(path: string, cred: Cred): void {
380
+ this._sync.rmdirSync(path, cred);
381
+ this.queue('rmdir', path, cred);
385
382
  }
386
383
 
387
- public mkdirSync(p: string, mode: number, cred: Cred): void {
388
- this._sync.mkdirSync(p, mode, cred);
389
- this.queue('mkdir', p, mode, cred);
384
+ public mkdirSync(path: string, mode: number, cred: Cred): void {
385
+ this._sync.mkdirSync(path, mode, cred);
386
+ this.queue('mkdir', path, mode, cred);
390
387
  }
391
388
 
392
- public readdirSync(p: string, cred: Cred): string[] {
393
- return this._sync.readdirSync(p, cred);
389
+ public readdirSync(path: string, cred: Cred): string[] {
390
+ return this._sync.readdirSync(path, cred);
394
391
  }
395
392
 
396
393
  public linkSync(srcpath: string, dstpath: string, cred: Cred): void {
@@ -403,27 +400,27 @@ export function Async<T extends abstract new (...args: any[]) => FileSystem>(FS:
403
400
  this.queue('sync', path, data, stats);
404
401
  }
405
402
 
406
- public existsSync(p: string, cred: Cred): boolean {
407
- return this._sync.existsSync(p, cred);
403
+ public existsSync(path: string, cred: Cred): boolean {
404
+ return this._sync.existsSync(path, cred);
408
405
  }
409
406
 
410
407
  /**
411
408
  * @internal
412
409
  */
413
- protected async crossCopy(p: string): Promise<void> {
414
- const stats = await this.stat(p, rootCred);
410
+ protected async crossCopy(path: string): Promise<void> {
411
+ const stats = await this.stat(path, rootCred);
415
412
  if (stats.isDirectory()) {
416
- if (p !== '/') {
417
- const stats = await this.stat(p, rootCred);
418
- this._sync.mkdirSync(p, stats.mode, stats.cred());
413
+ if (path !== '/') {
414
+ const stats = await this.stat(path, rootCred);
415
+ this._sync.mkdirSync(path, stats.mode, stats.cred());
419
416
  }
420
- const files = await this.readdir(p, rootCred);
417
+ const files = await this.readdir(path, rootCred);
421
418
  for (const file of files) {
422
- await this.crossCopy(join(p, file));
419
+ await this.crossCopy(join(path, file));
423
420
  }
424
421
  } else {
425
- const asyncFile = await this.openFile(p, parseFlag('r'), rootCred);
426
- const syncFile = this._sync.createFileSync(p, parseFlag('w'), stats.mode, stats.cred());
422
+ const asyncFile = await this.openFile(path, parseFlag('r'), rootCred);
423
+ const syncFile = this._sync.createFileSync(path, parseFlag('w'), stats.mode, stats.cred());
427
424
  try {
428
425
  const buffer = new Uint8Array(stats.size);
429
426
  await asyncFile.read(buffer);
package/src/index.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  export * from './error.js';
2
2
  export * from './backends/port/fs.js';
3
- export * from './backends/port/store.js';
4
- export * from './backends/AsyncStore.js';
5
- export * from './backends/Fetch.js';
6
- export * from './backends/InMemory.js';
3
+ export * from './backends/fetch.js';
4
+ export * from './backends/memory.js';
7
5
  export * from './backends/Index.js';
8
- export * from './backends/Locked.js';
9
- export * from './backends/Overlay.js';
10
- export * from './backends/SyncStore.js';
6
+ export * from './backends/locked.js';
7
+ export * from './backends/overlay.js';
8
+ export * from './backends/store/fs.js';
11
9
  export * from './backends/backend.js';
12
10
  export * from './config.js';
13
11
  export * from './cred.js';
package/src/inode.ts CHANGED
@@ -16,7 +16,7 @@ export const size_max = 2 ** 32 - 1;
16
16
  * Room inode
17
17
  * @hidden
18
18
  */
19
- export const rootIno: Ino = 0n;
19
+ export const rootIno = 0n as const;
20
20
 
21
21
  /**
22
22
  * Generates a random 32 bit integer, then converts to a hex string