@zenfs/core 1.3.6 → 1.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 (88) hide show
  1. package/dist/backends/memory.d.ts +4 -4
  2. package/dist/backends/memory.js +4 -4
  3. package/dist/backends/overlay.d.ts +5 -2
  4. package/dist/backends/overlay.js +7 -10
  5. package/dist/backends/port/fs.js +1 -4
  6. package/dist/config.js +4 -8
  7. package/dist/context.d.ts +32 -0
  8. package/dist/context.js +23 -0
  9. package/dist/credentials.d.ts +5 -5
  10. package/dist/credentials.js +10 -6
  11. package/dist/emulation/async.d.ts +90 -89
  12. package/dist/emulation/async.js +76 -75
  13. package/dist/emulation/dir.d.ts +3 -1
  14. package/dist/emulation/dir.js +6 -7
  15. package/dist/emulation/index.d.ts +1 -1
  16. package/dist/emulation/index.js +1 -1
  17. package/dist/emulation/promises.d.ts +50 -48
  18. package/dist/emulation/promises.js +78 -77
  19. package/dist/emulation/shared.d.ts +35 -8
  20. package/dist/emulation/shared.js +37 -11
  21. package/dist/emulation/sync.d.ts +63 -62
  22. package/dist/emulation/sync.js +72 -73
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +1 -0
  25. package/dist/stats.d.ts +2 -1
  26. package/dist/stats.js +5 -4
  27. package/package.json +3 -5
  28. package/scripts/test.js +78 -17
  29. package/tests/assignment.ts +1 -1
  30. package/tests/common/context.test.ts +19 -0
  31. package/tests/{devices.test.ts → common/devices.test.ts} +3 -3
  32. package/tests/{handle.test.ts → common/handle.test.ts} +1 -1
  33. package/tests/common/mounts.test.ts +36 -0
  34. package/tests/{mutex.test.ts → common/mutex.test.ts} +3 -3
  35. package/tests/common/path.test.ts +34 -0
  36. package/tests/common.ts +4 -3
  37. package/tests/fs/dir.test.ts +11 -11
  38. package/tests/fs/directory.test.ts +17 -17
  39. package/tests/fs/errors.test.ts +29 -39
  40. package/tests/fs/watch.test.ts +2 -2
  41. package/tests/setup/context.ts +9 -0
  42. package/tests/setup/cow+fetch.ts +1 -1
  43. package/tests/setup/memory.ts +1 -1
  44. package/tests/{setup/common.ts → setup.ts} +6 -5
  45. package/src/backends/backend.ts +0 -161
  46. package/src/backends/fetch.ts +0 -180
  47. package/src/backends/file_index.ts +0 -206
  48. package/src/backends/memory.ts +0 -45
  49. package/src/backends/overlay.ts +0 -560
  50. package/src/backends/port/fs.ts +0 -329
  51. package/src/backends/port/readme.md +0 -54
  52. package/src/backends/port/rpc.ts +0 -167
  53. package/src/backends/readme.md +0 -3
  54. package/src/backends/store/fs.ts +0 -667
  55. package/src/backends/store/readme.md +0 -9
  56. package/src/backends/store/simple.ts +0 -154
  57. package/src/backends/store/store.ts +0 -189
  58. package/src/config.ts +0 -227
  59. package/src/credentials.ts +0 -49
  60. package/src/devices.ts +0 -521
  61. package/src/emulation/async.ts +0 -834
  62. package/src/emulation/cache.ts +0 -86
  63. package/src/emulation/config.ts +0 -21
  64. package/src/emulation/constants.ts +0 -182
  65. package/src/emulation/dir.ts +0 -138
  66. package/src/emulation/index.ts +0 -8
  67. package/src/emulation/path.ts +0 -440
  68. package/src/emulation/promises.ts +0 -1140
  69. package/src/emulation/shared.ts +0 -172
  70. package/src/emulation/streams.ts +0 -34
  71. package/src/emulation/sync.ts +0 -863
  72. package/src/emulation/watchers.ts +0 -194
  73. package/src/error.ts +0 -307
  74. package/src/file.ts +0 -631
  75. package/src/filesystem.ts +0 -174
  76. package/src/index.ts +0 -35
  77. package/src/inode.ts +0 -128
  78. package/src/mixins/async.ts +0 -230
  79. package/src/mixins/index.ts +0 -5
  80. package/src/mixins/mutexed.ts +0 -257
  81. package/src/mixins/readonly.ts +0 -96
  82. package/src/mixins/shared.ts +0 -25
  83. package/src/mixins/sync.ts +0 -58
  84. package/src/polyfills.ts +0 -21
  85. package/src/stats.ts +0 -405
  86. package/src/utils.ts +0 -276
  87. package/tests/mounts.test.ts +0 -18
  88. package/tests/path.test.ts +0 -34
@@ -1,154 +0,0 @@
1
- import { SyncTransaction, type Store } from './store.js';
2
-
3
- /**
4
- * An interface for simple synchronous stores that don't have special support for transactions and such.
5
- */
6
- export interface SimpleSyncStore extends Store {
7
- keys(): Iterable<bigint>;
8
- get(id: bigint): Uint8Array | undefined;
9
- set(id: bigint, data: Uint8Array): void;
10
- delete(id: bigint): 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<bigint, Uint8Array> = new Map();
21
-
22
- protected queue: Set<Promise<unknown>> = new Set();
23
-
24
- protected abstract entries(): Promise<Iterable<[bigint, Uint8Array]>>;
25
-
26
- public keys(): Iterable<bigint> {
27
- return this.cache.keys();
28
- }
29
-
30
- public get(id: bigint): Uint8Array | undefined {
31
- return this.cache.get(id);
32
- }
33
-
34
- public set(id: bigint, data: Uint8Array): void {
35
- this.cache.set(id, data);
36
- this.queue.add(this._set(id, data));
37
- }
38
-
39
- protected abstract _set(ino: bigint, data: Uint8Array): Promise<void>;
40
-
41
- public delete(id: bigint): void {
42
- this.cache.delete(id);
43
- this.queue.add(this._delete(id));
44
- }
45
-
46
- protected abstract _delete(ino: bigint): Promise<void>;
47
-
48
- public clearSync(): void {
49
- this.cache.clear();
50
- this.queue.add(this.clear());
51
- }
52
-
53
- public abstract clear(): Promise<void>;
54
-
55
- public async sync(): Promise<void> {
56
- for (const [ino, data] of await this.entries()) {
57
- if (!this.cache.has(ino)) {
58
- this.cache.set(ino, data);
59
- }
60
- }
61
- for (const promise of this.queue) {
62
- await promise;
63
- }
64
- }
65
-
66
- public transaction(): SimpleTransaction {
67
- return new SimpleTransaction(this);
68
- }
69
- }
70
-
71
- /**
72
- * Transaction for simple stores.
73
- * @see SimpleSyncStore
74
- * @see SimpleAsyncStore
75
- */
76
- export class SimpleTransaction extends SyncTransaction<SimpleSyncStore> {
77
- /**
78
- * Stores data in the keys we modify prior to modifying them.
79
- * Allows us to roll back commits.
80
- */
81
- protected originalData: Map<bigint, Uint8Array | void> = new Map();
82
- /**
83
- * List of keys modified in this transaction, if any.
84
- */
85
- protected modifiedKeys: Set<bigint> = new Set();
86
-
87
- protected declare store: SimpleSyncStore;
88
-
89
- public keysSync(): Iterable<bigint> {
90
- return this.store.keys();
91
- }
92
-
93
- public getSync(id: bigint): Uint8Array {
94
- const val = this.store.get(id);
95
- this.stashOldValue(id, val);
96
- return val!;
97
- }
98
-
99
- public setSync(id: bigint, data: Uint8Array): void {
100
- this.markModified(id);
101
- return this.store.set(id, data);
102
- }
103
-
104
- public removeSync(id: bigint): void {
105
- this.markModified(id);
106
- this.store.delete(id);
107
- }
108
-
109
- public commitSync(): void {
110
- this.done = true;
111
- }
112
-
113
- public abortSync(): void {
114
- if (!this.done) {
115
- return;
116
- }
117
- // Rollback old values.
118
- for (const key of this.modifiedKeys) {
119
- const value = this.originalData.get(key);
120
- if (!value) {
121
- // Key didn't exist.
122
- this.store.delete(key);
123
- } else {
124
- // Key existed. Store old value.
125
- this.store.set(key, value);
126
- }
127
- }
128
- this.done = true;
129
- }
130
-
131
- /**
132
- * Stashes given key value pair into `originalData` if it doesn't already
133
- * exist. Allows us to stash values the program is requesting anyway to
134
- * prevent needless `get` requests if the program modifies the data later
135
- * on during the transaction.
136
- */
137
- protected stashOldValue(id: bigint, value?: Uint8Array): void {
138
- // Keep only the earliest value in the transaction.
139
- if (!this.originalData.has(id)) {
140
- this.originalData.set(id, value);
141
- }
142
- }
143
-
144
- /**
145
- * Marks `ino` as modified, and stashes its value if it has not been
146
- * stashed already.
147
- */
148
- protected markModified(id: bigint): void {
149
- this.modifiedKeys.add(id);
150
- if (!this.originalData.has(id)) {
151
- this.originalData.set(id, this.store.get(id));
152
- }
153
- }
154
- }
@@ -1,189 +0,0 @@
1
- import { ErrnoError } from '../../error.js';
2
- import '../../polyfills.js';
3
-
4
- /**
5
- * Represents a key-value store.
6
- */
7
- export interface Store {
8
- /**
9
- * The name of the store.
10
- */
11
- readonly 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 store.
36
- */
37
- export abstract class Transaction<T extends Store = Store> {
38
- public constructor(protected store: T) {}
39
-
40
- /**
41
- * Whether the transaction was committed or aborted
42
- */
43
- protected done: boolean = false;
44
-
45
- /**
46
- * Gets all of the keys
47
- */
48
- public abstract keys(): Promise<Iterable<bigint>>;
49
-
50
- /**
51
- * Gets all of the keys
52
- */
53
- public abstract keysSync(): Iterable<bigint>;
54
-
55
- /**
56
- * Retrieves data.
57
- * @param id The key to look under for data.
58
- */
59
- public abstract get(id: bigint): Promise<Uint8Array>;
60
-
61
- /**
62
- * Retrieves data.
63
- * Throws an error if an error occurs or if the key does not exist.
64
- * @param id The key to look under for data.
65
- * @return The data stored under the key, or undefined if not present.
66
- */
67
- public abstract getSync(id: bigint): Uint8Array;
68
-
69
- /**
70
- * Adds the data to the store under an id. Overwrites any existing data.
71
- * @param id The key to add the data under.
72
- * @param data The data to add to the store.
73
- */
74
- public abstract set(id: bigint, data: Uint8Array): Promise<void>;
75
-
76
- /**
77
- * Adds the data to the store under and id.
78
- * @param id The key to add the data under.
79
- * @param data The data to add to the store.
80
- */
81
- public abstract setSync(id: bigint, data: Uint8Array): void;
82
-
83
- /**
84
- * Deletes the data at `ino`.
85
- * @param id The key to delete from the store.
86
- */
87
- public abstract remove(id: bigint): Promise<void>;
88
-
89
- /**
90
- * Deletes the data at `ino`.
91
- * @param id The key to delete from the store.
92
- */
93
- public abstract removeSync(id: bigint): void;
94
-
95
- /**
96
- * Commits the transaction.
97
- */
98
- public abstract commit(): Promise<void>;
99
-
100
- public async [Symbol.asyncDispose]() {
101
- if (this.done) {
102
- return;
103
- }
104
-
105
- await this.abort();
106
- }
107
-
108
- /**
109
- * Commits the transaction.
110
- */
111
- public abstract commitSync(): void;
112
-
113
- public [Symbol.dispose]() {
114
- if (this.done) {
115
- return;
116
- }
117
-
118
- this.abortSync();
119
- }
120
-
121
- /**
122
- * Aborts and rolls back the transaction.
123
- */
124
- public abstract abort(): Promise<void>;
125
-
126
- /**
127
- * Aborts and rolls back the transaction.
128
- */
129
- public abstract abortSync(): void;
130
- }
131
-
132
- /**
133
- * Transaction that implements asynchronous operations with synchronous ones
134
- */
135
- export abstract class SyncTransaction<T extends Store = Store> extends Transaction<T> {
136
- /* eslint-disable @typescript-eslint/require-await */
137
- public async keys(): Promise<Iterable<bigint>> {
138
- return this.keysSync();
139
- }
140
- public async get(id: bigint): Promise<Uint8Array> {
141
- return this.getSync(id);
142
- }
143
-
144
- public async set(id: bigint, data: Uint8Array): Promise<void> {
145
- return this.setSync(id, data);
146
- }
147
-
148
- public async remove(id: bigint): Promise<void> {
149
- return this.removeSync(id);
150
- }
151
-
152
- public async commit(): Promise<void> {
153
- return this.commitSync();
154
- }
155
-
156
- public async abort(): Promise<void> {
157
- return this.abortSync();
158
- }
159
- /* eslint-enable @typescript-eslint/require-await */
160
- }
161
-
162
- /**
163
- * Transaction that only supports asynchronous operations
164
- */
165
- export abstract class AsyncTransaction<T extends Store = Store> extends Transaction<T> {
166
- public keysSync(): never {
167
- throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.keysSync');
168
- }
169
-
170
- public getSync(): never {
171
- throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.getSync');
172
- }
173
-
174
- public setSync(): never {
175
- throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.setSync');
176
- }
177
-
178
- public removeSync(): never {
179
- throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.removeSync');
180
- }
181
-
182
- public commitSync(): never {
183
- throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.commitSync');
184
- }
185
-
186
- public abortSync(): never {
187
- throw ErrnoError.With('ENOSYS', undefined, 'AsyncTransaction.abortSync');
188
- }
189
- }
package/src/config.ts DELETED
@@ -1,227 +0,0 @@
1
- import type { Backend, BackendConfiguration, FilesystemOf, SharedConfig } from './backends/backend.js';
2
- import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
3
- import { useCredentials } from './credentials.js';
4
- import { DeviceFS } from './devices.js';
5
- import * as cache from './emulation/cache.js';
6
- import { config } from './emulation/config.js';
7
- import * as fs from './emulation/index.js';
8
- import { Errno, ErrnoError } from './error.js';
9
- import { FileSystem } from './filesystem.js';
10
-
11
- /**
12
- * Configuration for a specific mount point
13
- */
14
- export type MountConfiguration<T extends Backend> = FilesystemOf<T> | BackendConfiguration<T> | T;
15
-
16
- function isMountConfig<T extends Backend>(arg: unknown): arg is MountConfiguration<T> {
17
- return isBackendConfig(arg) || isBackend(arg) || arg instanceof FileSystem;
18
- }
19
-
20
- /**
21
- * Retrieve a file system with `configuration`.
22
- * @see MountConfiguration
23
- */
24
- export async function resolveMountConfig<T extends Backend>(configuration: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
25
- if (typeof configuration !== 'object' || configuration == null) {
26
- throw new ErrnoError(Errno.EINVAL, 'Invalid options on mount configuration');
27
- }
28
-
29
- if (!isMountConfig(configuration)) {
30
- throw new ErrnoError(Errno.EINVAL, 'Invalid mount configuration');
31
- }
32
-
33
- if (configuration instanceof FileSystem) {
34
- await configuration.ready();
35
- return configuration;
36
- }
37
-
38
- if (isBackend(configuration)) {
39
- configuration = { backend: configuration } as BackendConfiguration<T>;
40
- }
41
-
42
- for (const [key, value] of Object.entries(configuration)) {
43
- if (key == 'backend') {
44
- continue;
45
- }
46
-
47
- if (!isMountConfig(value)) {
48
- continue;
49
- }
50
-
51
- if (_depth > 10) {
52
- throw new ErrnoError(Errno.EINVAL, 'Invalid configuration, too deep and possibly infinite');
53
- }
54
-
55
- (configuration as Record<string, FileSystem>)[key] = await resolveMountConfig(value, ++_depth);
56
- }
57
-
58
- const { backend } = configuration;
59
-
60
- if (!(await backend.isAvailable())) {
61
- throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend.name);
62
- }
63
- await checkOptions(backend, configuration);
64
- const mount = (await backend.create(configuration)) as FilesystemOf<T>;
65
- mount._disableSync = configuration.disableAsyncCache || false;
66
- await mount.ready();
67
- return mount;
68
- }
69
-
70
- /**
71
- * An object mapping mount points to backends
72
- */
73
- export interface ConfigMounts {
74
- [K: string]: Backend;
75
- }
76
-
77
- /**
78
- * Configuration
79
- */
80
- export interface Configuration<T extends ConfigMounts> extends SharedConfig {
81
- /**
82
- * An object mapping mount points to mount configuration
83
- */
84
- mounts: { [K in keyof T]: MountConfiguration<T[K]> };
85
-
86
- /**
87
- * The uid to use
88
- * @default 0
89
- */
90
- uid: number;
91
-
92
- /**
93
- * The gid to use
94
- * @default 0
95
- */
96
- gid: number;
97
-
98
- /**
99
- * Whether to automatically add normal Linux devices
100
- * @experimental
101
- * @default false
102
- */
103
- addDevices: boolean;
104
-
105
- /**
106
- * If true, enables caching stats for certain operations.
107
- * This should reduce the number of stat calls performed.
108
- * @experimental
109
- * @default false
110
- */
111
- cacheStats: boolean;
112
-
113
- /**
114
- * If true, enables caching realpath output
115
- *
116
- * This can increase performance.
117
- * @experimental
118
- * @default false
119
- */
120
- cachePaths: boolean;
121
-
122
- /**
123
- * If true, disables *all* permissions checking.
124
- *
125
- * This can increase performance.
126
- * @experimental
127
- * @default false
128
- */
129
- disableAccessChecks: boolean;
130
-
131
- /**
132
- * If true, disables `read` and `readSync` from updating the atime.
133
- *
134
- * This can increase performance.
135
- * @experimental
136
- * @default false
137
- */
138
- disableUpdateOnRead: boolean;
139
-
140
- /**
141
- * If true, files will only sync to the file system when closed.
142
- *
143
- * This can increase performance.
144
- * @experimental
145
- * @overrides `disableUpdateOnRead`
146
- * @default false
147
- */
148
- onlySyncOnClose: boolean;
149
- }
150
-
151
- /**
152
- * Configures ZenFS with single mount point /
153
- */
154
- export async function configureSingle<T extends Backend>(configuration: MountConfiguration<T>): Promise<void> {
155
- if (!isBackendConfig(configuration)) {
156
- throw new TypeError('Invalid single mount point configuration');
157
- }
158
-
159
- const resolved = await resolveMountConfig(configuration);
160
- fs.umount('/');
161
- fs.mount('/', resolved);
162
- }
163
-
164
- /**
165
- * Like `fs.mount`, but it also creates missing directories.
166
- * @privateRemarks
167
- * This is implemented as a separate function to avoid a circular dependency between emulation/shared.ts and other emulation layer files.
168
- * @internal
169
- */
170
- async function mount(path: string, mount: FileSystem): Promise<void> {
171
- if (path == '/') {
172
- fs.mount(path, mount);
173
- return;
174
- }
175
-
176
- const stats = await fs.promises.stat(path).catch(() => null);
177
- if (!stats) {
178
- await fs.promises.mkdir(path, { recursive: true });
179
- } else if (!stats.isDirectory()) {
180
- throw ErrnoError.With('ENOTDIR', path, 'configure');
181
- }
182
- fs.mount(path, mount);
183
- }
184
-
185
- /**
186
- * Configures ZenFS with `configuration`
187
- * @see Configuration
188
- */
189
- export async function configure<T extends ConfigMounts>(configuration: Partial<Configuration<T>>): Promise<void> {
190
- const uid = 'uid' in configuration ? configuration.uid || 0 : 0;
191
- const gid = 'gid' in configuration ? configuration.gid || 0 : 0;
192
-
193
- useCredentials({ uid, gid });
194
-
195
- cache.stats.isEnabled = configuration.cacheStats ?? false;
196
- cache.paths.isEnabled = configuration.cachePaths ?? false;
197
- config.checkAccess = !configuration.disableAccessChecks;
198
- config.updateOnRead = !configuration.disableUpdateOnRead;
199
- config.syncImmediately = !configuration.onlySyncOnClose;
200
-
201
- if (configuration.mounts) {
202
- const toMount: [string, FileSystem][] = [];
203
- let unmountRoot = false;
204
-
205
- for (const [_point, mountConfig] of Object.entries(configuration.mounts)) {
206
- const point = _point.startsWith('/') ? _point : '/' + _point;
207
-
208
- if (isBackendConfig(mountConfig)) {
209
- mountConfig.disableAsyncCache ??= configuration.disableAsyncCache || false;
210
- }
211
-
212
- if (point == '/') unmountRoot = true;
213
- toMount.push([point, await resolveMountConfig(mountConfig)]);
214
- }
215
-
216
- if (unmountRoot) fs.umount('/');
217
-
218
- await Promise.all(toMount.map(([point, fs]) => mount(point, fs)));
219
- }
220
-
221
- if (configuration.addDevices) {
222
- const devfs = new DeviceFS();
223
- devfs.addDefaults();
224
- await devfs.ready();
225
- await mount('/dev', devfs);
226
- }
227
- }
@@ -1,49 +0,0 @@
1
- /**
2
- * Credentials used for various operations.
3
- * Similar to Linux's cred struct.
4
- * @see https://github.com/torvalds/linux/blob/master/include/linux/cred.h
5
- */
6
- export interface Credentials {
7
- uid: number;
8
- gid: number;
9
- suid: number;
10
- sgid: number;
11
- euid: number;
12
- egid: number;
13
- /**
14
- * List of group IDs.
15
- */
16
- groups: number[];
17
- }
18
-
19
- export const credentials: Credentials = {
20
- uid: 0,
21
- gid: 0,
22
- suid: 0,
23
- sgid: 0,
24
- euid: 0,
25
- egid: 0,
26
- groups: [],
27
- };
28
-
29
- export interface CredentialInit {
30
- uid: number;
31
- gid: number;
32
- suid?: number;
33
- sgid?: number;
34
- euid?: number;
35
- egid?: number;
36
- }
37
-
38
- /**
39
- * Uses credentials from the provided uid and gid.
40
- */
41
- export function useCredentials(source: CredentialInit): void {
42
- Object.assign(credentials, {
43
- suid: source.uid,
44
- sgid: source.gid,
45
- euid: source.uid,
46
- egid: source.gid,
47
- ...source,
48
- });
49
- }