@zenfs/core 1.7.2 → 1.8.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.
- package/dist/backends/backend.js +3 -4
- package/dist/backends/fetch.d.ts +17 -18
- package/dist/backends/fetch.js +95 -58
- package/dist/backends/index.d.ts +2 -1
- package/dist/backends/index.js +2 -1
- package/dist/backends/memory.d.ts +1 -1
- package/dist/backends/overlay.d.ts +8 -14
- package/dist/backends/overlay.js +38 -31
- package/dist/backends/passthrough.d.ts +8 -3
- package/dist/backends/passthrough.js +148 -4
- package/dist/backends/port/fs.d.ts +15 -49
- package/dist/backends/port/fs.js +28 -116
- package/dist/backends/port/rpc.d.ts +13 -6
- package/dist/backends/port/rpc.js +9 -7
- package/dist/backends/store/file_index.d.ts +38 -0
- package/dist/backends/store/file_index.js +76 -0
- package/dist/backends/store/fs.d.ts +39 -34
- package/dist/backends/store/fs.js +407 -238
- package/dist/backends/store/index_fs.d.ts +34 -0
- package/dist/backends/store/index_fs.js +67 -0
- package/dist/backends/store/inode.d.ts +26 -8
- package/dist/backends/store/inode.js +92 -91
- package/dist/backends/store/simple.d.ts +20 -20
- package/dist/backends/store/simple.js +3 -4
- package/dist/backends/store/store.d.ts +12 -12
- package/dist/backends/store/store.js +4 -6
- package/dist/devices.d.ts +44 -21
- package/dist/devices.js +110 -55
- package/dist/file.d.ts +111 -7
- package/dist/file.js +324 -92
- package/dist/filesystem.d.ts +44 -4
- package/dist/mixins/async.js +12 -6
- package/dist/mixins/mutexed.d.ts +8 -3
- package/dist/mixins/mutexed.js +57 -1
- package/dist/mixins/readonly.d.ts +17 -16
- package/dist/mixins/readonly.js +6 -0
- package/dist/mixins/sync.d.ts +1 -1
- package/dist/stats.d.ts +12 -6
- package/dist/stats.js +14 -6
- package/dist/utils.d.ts +23 -3
- package/dist/utils.js +58 -10
- package/dist/vfs/async.js +1 -1
- package/dist/vfs/constants.d.ts +2 -2
- package/dist/vfs/constants.js +2 -2
- package/dist/vfs/dir.js +3 -1
- package/dist/vfs/index.js +4 -1
- package/dist/vfs/promises.js +33 -13
- package/dist/vfs/shared.js +2 -0
- package/dist/vfs/sync.js +25 -13
- package/dist/vfs/types.d.ts +15 -0
- package/eslint.shared.js +1 -0
- package/package.json +2 -3
- package/readme.md +2 -2
- package/scripts/test.js +73 -11
- package/tests/common/mutex.test.ts +1 -1
- package/tests/fetch/run.sh +16 -0
- package/tests/fetch/server.ts +49 -0
- package/tests/fetch/setup.ts +13 -0
- package/tests/fs/read.test.ts +10 -10
- package/tests/fs/times.test.ts +2 -2
- package/tests/fs/write.test.ts +6 -11
- package/tests/setup/index.ts +38 -0
- package/tests/setup/port.ts +15 -0
- package/dist/backends/file_index.d.ts +0 -63
- package/dist/backends/file_index.js +0 -163
- package/tests/common/async.test.ts +0 -31
- package/tests/setup/cow+fetch.ts +0 -45
- /package/tests/fs/{appendFile.test.ts → append.test.ts} +0 -0
package/dist/backends/backend.js
CHANGED
|
@@ -20,8 +20,8 @@ export async function checkOptions(backend, options) {
|
|
|
20
20
|
}
|
|
21
21
|
throw new ErrnoError(Errno.EINVAL, 'Missing required option: ' + optName);
|
|
22
22
|
}
|
|
23
|
-
const isType = (value) => (typeof
|
|
24
|
-
if (Array.isArray(opt.type) ? !opt.type.some(isType) : !isType(
|
|
23
|
+
const isType = (type, _ = value) => (typeof type == 'function' ? value instanceof type : typeof value === type);
|
|
24
|
+
if (Array.isArray(opt.type) ? !opt.type.some(v => isType(v)) : !isType(opt.type)) {
|
|
25
25
|
// The type of the value as a string
|
|
26
26
|
const type = typeof value == 'object' && 'constructor' in value ? value.constructor.name : typeof value;
|
|
27
27
|
// The expected type (as a string)
|
|
@@ -29,9 +29,8 @@ export async function checkOptions(backend, options) {
|
|
|
29
29
|
const expected = Array.isArray(opt.type) ? `one of ${opt.type.map(name).join(', ')}` : name(opt.type);
|
|
30
30
|
throw new ErrnoError(Errno.EINVAL, `Incorrect type for "${optName}": ${type} (expected ${expected})`);
|
|
31
31
|
}
|
|
32
|
-
if (opt.validator)
|
|
32
|
+
if (opt.validator)
|
|
33
33
|
await opt.validator(value);
|
|
34
|
-
}
|
|
35
34
|
// Otherwise: All good!
|
|
36
35
|
}
|
|
37
36
|
}
|
package/dist/backends/fetch.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
3
|
-
import type {
|
|
4
|
-
import { IndexFS } from './file_index.js';
|
|
1
|
+
import { StoreFS } from './store/fs.js';
|
|
2
|
+
import { type IndexData } from './store/file_index.js';
|
|
3
|
+
import type { Store } from './store/store.js';
|
|
5
4
|
/**
|
|
6
5
|
* Configuration options for FetchFS.
|
|
7
6
|
*/
|
|
@@ -19,6 +18,11 @@ export interface FetchOptions {
|
|
|
19
18
|
* Default: Fetch files relative to the index.
|
|
20
19
|
*/
|
|
21
20
|
baseUrl?: string;
|
|
21
|
+
/**
|
|
22
|
+
* A store to use for caching content.
|
|
23
|
+
* Defaults to an in-memory store
|
|
24
|
+
*/
|
|
25
|
+
cache?: Store;
|
|
22
26
|
}
|
|
23
27
|
/**
|
|
24
28
|
* A simple filesystem backed by HTTP using the `fetch` API.
|
|
@@ -31,7 +35,7 @@ export interface FetchOptions {
|
|
|
31
35
|
* "version": 1,
|
|
32
36
|
* "entries": {
|
|
33
37
|
* "/home": { ... },
|
|
34
|
-
* "/home/
|
|
38
|
+
* "/home/john": { ... },
|
|
35
39
|
* "/home/james": { ... }
|
|
36
40
|
* }
|
|
37
41
|
* }
|
|
@@ -39,21 +43,12 @@ export interface FetchOptions {
|
|
|
39
43
|
*
|
|
40
44
|
* Each entry contains the stats associated with the file.
|
|
41
45
|
*/
|
|
42
|
-
export declare class FetchFS extends
|
|
46
|
+
export declare class FetchFS extends StoreFS {
|
|
43
47
|
readonly baseUrl: string;
|
|
44
|
-
readonly requestInit?: RequestInit;
|
|
48
|
+
readonly requestInit?: RequestInit | undefined;
|
|
49
|
+
private indexData;
|
|
45
50
|
ready(): Promise<void>;
|
|
46
|
-
constructor(
|
|
47
|
-
metadata(): FileSystemMetadata;
|
|
48
|
-
/**
|
|
49
|
-
* Preload the `path` into the index.
|
|
50
|
-
*/
|
|
51
|
-
preload(path: string, buffer: Uint8Array): void;
|
|
52
|
-
/**
|
|
53
|
-
* @todo Be lazier about actually requesting the data?
|
|
54
|
-
*/
|
|
55
|
-
protected getData(path: string, stats: Stats): Promise<Uint8Array>;
|
|
56
|
-
protected getDataSync(path: string, stats: Stats): Uint8Array;
|
|
51
|
+
constructor(index?: IndexData | string, cache?: Store, baseUrl?: string, requestInit?: RequestInit | undefined);
|
|
57
52
|
}
|
|
58
53
|
declare const _Fetch: {
|
|
59
54
|
readonly name: "Fetch";
|
|
@@ -70,6 +65,10 @@ declare const _Fetch: {
|
|
|
70
65
|
readonly type: "object";
|
|
71
66
|
readonly required: false;
|
|
72
67
|
};
|
|
68
|
+
readonly cache: {
|
|
69
|
+
readonly type: "object";
|
|
70
|
+
readonly required: false;
|
|
71
|
+
};
|
|
73
72
|
};
|
|
74
73
|
readonly isAvailable: () => boolean;
|
|
75
74
|
readonly create: (options: FetchOptions) => FetchFS;
|
package/dist/backends/fetch.js
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
1
53
|
import { Errno, ErrnoError } from '../error.js';
|
|
2
|
-
import {
|
|
54
|
+
import { S_IFREG } from '../vfs/constants.js';
|
|
55
|
+
import { InMemoryStore } from './memory.js';
|
|
56
|
+
import { StoreFS } from './store/fs.js';
|
|
57
|
+
import { Index } from './store/file_index.js';
|
|
58
|
+
import { normalizePath } from '../utils.js';
|
|
3
59
|
async function fetchFile(path, type, init) {
|
|
4
60
|
const response = await fetch(path, init).catch((e) => {
|
|
5
61
|
throw new ErrnoError(Errno.EIO, e.message, path);
|
|
@@ -33,7 +89,7 @@ async function fetchFile(path, type, init) {
|
|
|
33
89
|
* "version": 1,
|
|
34
90
|
* "entries": {
|
|
35
91
|
* "/home": { ... },
|
|
36
|
-
* "/home/
|
|
92
|
+
* "/home/john": { ... },
|
|
37
93
|
* "/home/james": { ... }
|
|
38
94
|
* }
|
|
39
95
|
* }
|
|
@@ -41,68 +97,46 @@ async function fetchFile(path, type, init) {
|
|
|
41
97
|
*
|
|
42
98
|
* Each entry contains the stats associated with the file.
|
|
43
99
|
*/
|
|
44
|
-
export class FetchFS extends
|
|
100
|
+
export class FetchFS extends StoreFS {
|
|
45
101
|
async ready() {
|
|
46
|
-
|
|
47
|
-
|
|
102
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
103
|
+
try {
|
|
104
|
+
if (this._initialized)
|
|
105
|
+
return;
|
|
106
|
+
await super.ready();
|
|
107
|
+
const index = new Index();
|
|
108
|
+
index.fromJSON(await this.indexData);
|
|
109
|
+
await this.loadIndex(index);
|
|
110
|
+
if (this._disableSync)
|
|
111
|
+
return;
|
|
112
|
+
const tx = __addDisposableResource(env_1, this.store.transaction(), true);
|
|
113
|
+
// Iterate over all of the files and cache their contents
|
|
114
|
+
for (const [path, node] of index) {
|
|
115
|
+
if (!(node.mode & S_IFREG))
|
|
116
|
+
continue;
|
|
117
|
+
const content = await fetchFile(this.baseUrl + path, 'buffer', this.requestInit);
|
|
118
|
+
await tx.set(node.data, content);
|
|
119
|
+
}
|
|
120
|
+
await tx.commit();
|
|
48
121
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
122
|
+
catch (e_1) {
|
|
123
|
+
env_1.error = e_1;
|
|
124
|
+
env_1.hasError = true;
|
|
52
125
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
await this.getData(path, stats);
|
|
126
|
+
finally {
|
|
127
|
+
const result_1 = __disposeResources(env_1);
|
|
128
|
+
if (result_1)
|
|
129
|
+
await result_1;
|
|
58
130
|
}
|
|
59
131
|
}
|
|
60
|
-
constructor(
|
|
61
|
-
|
|
62
|
-
if (baseUrl.at(-1) != '/') {
|
|
63
|
-
baseUrl += '/';
|
|
64
|
-
}
|
|
65
|
-
super(typeof index != 'string' ? index : fetchFile(index, 'json', requestInit));
|
|
132
|
+
constructor(index = 'index.json', cache = new InMemoryStore('fetch'), baseUrl = '', requestInit) {
|
|
133
|
+
super(cache);
|
|
66
134
|
this.baseUrl = baseUrl;
|
|
67
135
|
this.requestInit = requestInit;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
name: FetchFS.name,
|
|
73
|
-
readonly: true,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Preload the `path` into the index.
|
|
78
|
-
*/
|
|
79
|
-
preload(path, buffer) {
|
|
80
|
-
const stats = this.index.get(path);
|
|
81
|
-
if (!stats) {
|
|
82
|
-
throw ErrnoError.With('ENOENT', path, 'preload');
|
|
83
|
-
}
|
|
84
|
-
if (!stats.isFile()) {
|
|
85
|
-
throw ErrnoError.With('EISDIR', path, 'preload');
|
|
86
|
-
}
|
|
87
|
-
stats.size = buffer.length;
|
|
88
|
-
stats.fileData = buffer;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* @todo Be lazier about actually requesting the data?
|
|
92
|
-
*/
|
|
93
|
-
async getData(path, stats) {
|
|
94
|
-
if (stats.fileData) {
|
|
95
|
-
return stats.fileData;
|
|
96
|
-
}
|
|
97
|
-
const data = await fetchFile(this.baseUrl + (path.startsWith('/') ? path.slice(1) : path), 'buffer', this.requestInit);
|
|
98
|
-
stats.fileData = data;
|
|
99
|
-
return data;
|
|
100
|
-
}
|
|
101
|
-
getDataSync(path, stats) {
|
|
102
|
-
if (stats.fileData) {
|
|
103
|
-
return stats.fileData;
|
|
104
|
-
}
|
|
105
|
-
throw new ErrnoError(Errno.ENODATA, '', path, 'getData');
|
|
136
|
+
// prefix url must end in a directory separator.
|
|
137
|
+
if (baseUrl.at(-1) == '/')
|
|
138
|
+
this.baseUrl = baseUrl.slice(0, -1);
|
|
139
|
+
this.indexData = typeof index != 'string' ? index : fetchFile(index, 'json', requestInit);
|
|
106
140
|
}
|
|
107
141
|
}
|
|
108
142
|
const _Fetch = {
|
|
@@ -111,12 +145,15 @@ const _Fetch = {
|
|
|
111
145
|
index: { type: ['string', 'object'], required: false },
|
|
112
146
|
baseUrl: { type: 'string', required: false },
|
|
113
147
|
requestInit: { type: 'object', required: false },
|
|
148
|
+
cache: { type: 'object', required: false },
|
|
114
149
|
},
|
|
115
150
|
isAvailable() {
|
|
116
151
|
return typeof globalThis.fetch == 'function';
|
|
117
152
|
},
|
|
118
153
|
create(options) {
|
|
119
|
-
|
|
154
|
+
const url = new URL(options.baseUrl || '');
|
|
155
|
+
url.pathname = normalizePath(url.pathname);
|
|
156
|
+
return new FetchFS(options.index, options.cache, url.toString(), options.requestInit);
|
|
120
157
|
},
|
|
121
158
|
};
|
|
122
159
|
export const Fetch = _Fetch;
|
package/dist/backends/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export * from './backend.js';
|
|
2
2
|
export * from './fetch.js';
|
|
3
|
-
export * from './file_index.js';
|
|
4
3
|
export * from './memory.js';
|
|
5
4
|
export * from './overlay.js';
|
|
6
5
|
export * from './passthrough.js';
|
|
7
6
|
export * from './port/fs.js';
|
|
8
7
|
export * from './store/fs.js';
|
|
8
|
+
export * from './store/file_index.js';
|
|
9
|
+
export * from './store/index_fs.js';
|
|
9
10
|
export * from './store/inode.js';
|
|
10
11
|
export * from './store/simple.js';
|
|
11
12
|
export * from './store/store.js';
|
package/dist/backends/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
export * from './backend.js';
|
|
2
2
|
export * from './fetch.js';
|
|
3
|
-
export * from './file_index.js';
|
|
4
3
|
export * from './memory.js';
|
|
5
4
|
export * from './overlay.js';
|
|
6
5
|
export * from './passthrough.js';
|
|
7
6
|
export * from './port/fs.js';
|
|
8
7
|
export * from './store/fs.js';
|
|
8
|
+
export * from './store/file_index.js';
|
|
9
|
+
export * from './store/index_fs.js';
|
|
9
10
|
export * from './store/inode.js';
|
|
10
11
|
export * from './store/simple.js';
|
|
11
12
|
export * from './store/store.js';
|
|
@@ -3,7 +3,7 @@ import { SimpleTransaction, type SimpleSyncStore } from './store/simple.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* A simple in-memory store
|
|
5
5
|
*/
|
|
6
|
-
export declare class InMemoryStore extends Map<
|
|
6
|
+
export declare class InMemoryStore extends Map<number, Uint8Array> implements SimpleSyncStore {
|
|
7
7
|
name: string;
|
|
8
8
|
constructor(name?: string);
|
|
9
9
|
sync(): Promise<void>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { File } from '../file.js';
|
|
2
2
|
import type { CreationOptions, FileSystemMetadata } from '../filesystem.js';
|
|
3
3
|
import type { Stats } from '../stats.js';
|
|
4
|
+
import type { InodeLike } from './store/inode.js';
|
|
4
5
|
import { FileSystem } from '../filesystem.js';
|
|
5
6
|
/**
|
|
6
7
|
* Configuration options for OverlayFS instances.
|
|
@@ -23,7 +24,7 @@ export interface OverlayOptions {
|
|
|
23
24
|
*
|
|
24
25
|
* @internal
|
|
25
26
|
*/
|
|
26
|
-
export declare class
|
|
27
|
+
export declare class OverlayFS extends FileSystem {
|
|
27
28
|
ready(): Promise<void>;
|
|
28
29
|
readonly writable: FileSystem;
|
|
29
30
|
readonly readable: FileSystem;
|
|
@@ -36,8 +37,12 @@ export declare class UnmutexedOverlayFS extends FileSystem {
|
|
|
36
37
|
private _ready;
|
|
37
38
|
constructor({ writable, readable }: OverlayOptions);
|
|
38
39
|
metadata(): FileSystemMetadata;
|
|
39
|
-
sync(path: string, data: Uint8Array, stats: Readonly<
|
|
40
|
-
syncSync(path: string, data: Uint8Array, stats: Readonly<
|
|
40
|
+
sync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): Promise<void>;
|
|
41
|
+
syncSync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): void;
|
|
42
|
+
read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
|
|
43
|
+
readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
|
|
44
|
+
write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
|
|
45
|
+
writeSync(path: string, buffer: Uint8Array, offset: number): void;
|
|
41
46
|
/**
|
|
42
47
|
* Called once to load up metadata stored on the writable file system.
|
|
43
48
|
* @internal
|
|
@@ -92,17 +97,6 @@ export declare class UnmutexedOverlayFS extends FileSystem {
|
|
|
92
97
|
private copyToWritableSync;
|
|
93
98
|
private copyToWritable;
|
|
94
99
|
}
|
|
95
|
-
declare const OverlayFS_base: {
|
|
96
|
-
new (): import("../mixins/mutexed.js")._MutexedFS<UnmutexedOverlayFS>;
|
|
97
|
-
} & (new (args_0: OverlayOptions) => import("../mixins/mutexed.js")._MutexedFS<UnmutexedOverlayFS>);
|
|
98
|
-
/**
|
|
99
|
-
* OverlayFS makes a read-only filesystem writable by storing writes on a second,
|
|
100
|
-
* writable file system. Deletes are persisted via metadata stored on the writable
|
|
101
|
-
* file system.
|
|
102
|
-
* @internal
|
|
103
|
-
*/
|
|
104
|
-
export declare class OverlayFS extends OverlayFS_base {
|
|
105
|
-
}
|
|
106
100
|
declare const _Overlay: {
|
|
107
101
|
readonly name: "Overlay";
|
|
108
102
|
readonly options: {
|
package/dist/backends/overlay.js
CHANGED
|
@@ -51,11 +51,10 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
53
|
import { Errno, ErrnoError } from '../error.js';
|
|
54
|
-
import {
|
|
54
|
+
import { LazyFile, parseFlag } from '../file.js';
|
|
55
55
|
import { FileSystem } from '../filesystem.js';
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
58
|
-
import { dirname } from '../vfs/path.js';
|
|
56
|
+
import { canary, decodeUTF8, encodeUTF8 } from '../utils.js';
|
|
57
|
+
import { dirname, join } from '../vfs/path.js';
|
|
59
58
|
/** @internal */
|
|
60
59
|
const deletionLogPath = '/.deleted';
|
|
61
60
|
/**
|
|
@@ -66,7 +65,7 @@ const deletionLogPath = '/.deleted';
|
|
|
66
65
|
*
|
|
67
66
|
* @internal
|
|
68
67
|
*/
|
|
69
|
-
export class
|
|
68
|
+
export class OverlayFS extends FileSystem {
|
|
70
69
|
async ready() {
|
|
71
70
|
await this.readable.ready();
|
|
72
71
|
await this.writable.ready();
|
|
@@ -97,15 +96,26 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
97
96
|
}
|
|
98
97
|
async sync(path, data, stats) {
|
|
99
98
|
await this.copyForWrite(path);
|
|
100
|
-
if (!(await this.writable.exists(path))) {
|
|
101
|
-
await this.writable.createFile(path, 'w', 0o644, stats);
|
|
102
|
-
}
|
|
103
99
|
await this.writable.sync(path, data, stats);
|
|
104
100
|
}
|
|
105
101
|
syncSync(path, data, stats) {
|
|
106
102
|
this.copyForWriteSync(path);
|
|
107
103
|
this.writable.syncSync(path, data, stats);
|
|
108
104
|
}
|
|
105
|
+
async read(path, buffer, offset, end) {
|
|
106
|
+
return (await this.writable.exists(path)) ? await this.writable.read(path, buffer, offset, end) : await this.readable.read(path, buffer, offset, end);
|
|
107
|
+
}
|
|
108
|
+
readSync(path, buffer, offset, end) {
|
|
109
|
+
return this.writable.existsSync(path) ? this.writable.readSync(path, buffer, offset, end) : this.readable.readSync(path, buffer, offset, end);
|
|
110
|
+
}
|
|
111
|
+
async write(path, buffer, offset) {
|
|
112
|
+
await this.copyForWrite(path);
|
|
113
|
+
return await this.writable.write(path, buffer, offset);
|
|
114
|
+
}
|
|
115
|
+
writeSync(path, buffer, offset) {
|
|
116
|
+
this.copyForWriteSync(path);
|
|
117
|
+
return this.writable.writeSync(path, buffer, offset);
|
|
118
|
+
}
|
|
109
119
|
/**
|
|
110
120
|
* Called once to load up metadata stored on the writable file system.
|
|
111
121
|
* @internal
|
|
@@ -199,22 +209,15 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
199
209
|
if (await this.writable.exists(path)) {
|
|
200
210
|
return this.writable.openFile(path, flag);
|
|
201
211
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const stats = await file.stat();
|
|
205
|
-
const { buffer } = await file.read(new Uint8Array(stats.size));
|
|
206
|
-
return new PreloadFile(this, path, flag, stats, buffer);
|
|
212
|
+
const stats = await this.readable.stat(path);
|
|
213
|
+
return new LazyFile(this, path, flag, stats);
|
|
207
214
|
}
|
|
208
215
|
openFileSync(path, flag) {
|
|
209
216
|
if (this.writable.existsSync(path)) {
|
|
210
217
|
return this.writable.openFileSync(path, flag);
|
|
211
218
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const stats = file.statSync();
|
|
215
|
-
const data = new Uint8Array(stats.size);
|
|
216
|
-
file.readSync(data);
|
|
217
|
-
return new PreloadFile(this, path, flag, stats, data);
|
|
219
|
+
const stats = this.readable.statSync(path);
|
|
220
|
+
return new LazyFile(this, path, flag, stats);
|
|
218
221
|
}
|
|
219
222
|
async createFile(path, flag, mode, options) {
|
|
220
223
|
this.checkInitialized();
|
|
@@ -421,10 +424,12 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
421
424
|
createParentDirectoriesSync(path) {
|
|
422
425
|
let parent = dirname(path);
|
|
423
426
|
const toCreate = [];
|
|
427
|
+
const silence = canary(path);
|
|
424
428
|
while (!this.writable.existsSync(parent)) {
|
|
425
429
|
toCreate.push(parent);
|
|
426
430
|
parent = dirname(parent);
|
|
427
431
|
}
|
|
432
|
+
silence();
|
|
428
433
|
for (const path of toCreate.reverse()) {
|
|
429
434
|
const { uid, gid, mode } = this.statSync(path);
|
|
430
435
|
this.writable.mkdirSync(path, mode, { uid, gid });
|
|
@@ -437,10 +442,12 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
437
442
|
async createParentDirectories(path) {
|
|
438
443
|
let parent = dirname(path);
|
|
439
444
|
const toCreate = [];
|
|
445
|
+
const silence = canary(path);
|
|
440
446
|
while (!(await this.writable.exists(parent))) {
|
|
441
447
|
toCreate.push(parent);
|
|
442
448
|
parent = dirname(parent);
|
|
443
449
|
}
|
|
450
|
+
silence();
|
|
444
451
|
for (const path of toCreate.reverse()) {
|
|
445
452
|
const { uid, gid, mode } = await this.stat(path);
|
|
446
453
|
await this.writable.mkdir(path, mode, { uid, gid });
|
|
@@ -453,7 +460,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
453
460
|
*/
|
|
454
461
|
copyForWriteSync(path) {
|
|
455
462
|
if (!this.existsSync(path)) {
|
|
456
|
-
throw ErrnoError.With('ENOENT', path, 'copyForWrite');
|
|
463
|
+
throw ErrnoError.With('ENOENT', path, '[copyForWrite]');
|
|
457
464
|
}
|
|
458
465
|
if (!this.writable.existsSync(dirname(path))) {
|
|
459
466
|
this.createParentDirectoriesSync(path);
|
|
@@ -464,7 +471,7 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
464
471
|
}
|
|
465
472
|
async copyForWrite(path) {
|
|
466
473
|
if (!(await this.exists(path))) {
|
|
467
|
-
throw ErrnoError.With('ENOENT', path, 'copyForWrite');
|
|
474
|
+
throw ErrnoError.With('ENOENT', path, '[copyForWrite]');
|
|
468
475
|
}
|
|
469
476
|
if (!(await this.writable.exists(dirname(path)))) {
|
|
470
477
|
await this.createParentDirectories(path);
|
|
@@ -481,14 +488,18 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
481
488
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
482
489
|
try {
|
|
483
490
|
const stats = this.statSync(path);
|
|
491
|
+
stats.mode |= 0o222;
|
|
484
492
|
if (stats.isDirectory()) {
|
|
485
493
|
this.writable.mkdirSync(path, stats.mode, stats);
|
|
494
|
+
for (const k of this.readable.readdirSync(path)) {
|
|
495
|
+
this.copyToWritableSync(join(path, k));
|
|
496
|
+
}
|
|
486
497
|
return;
|
|
487
498
|
}
|
|
488
499
|
const data = new Uint8Array(stats.size);
|
|
489
500
|
const readable = __addDisposableResource(env_1, this.readable.openFileSync(path, 'r'), false);
|
|
490
501
|
readable.readSync(data);
|
|
491
|
-
const writable = __addDisposableResource(env_1, this.writable.createFileSync(path, 'w', stats.mode
|
|
502
|
+
const writable = __addDisposableResource(env_1, this.writable.createFileSync(path, 'w', stats.mode, stats), false);
|
|
492
503
|
writable.writeSync(data);
|
|
493
504
|
}
|
|
494
505
|
catch (e_1) {
|
|
@@ -503,14 +514,18 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
503
514
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
504
515
|
try {
|
|
505
516
|
const stats = await this.stat(path);
|
|
517
|
+
stats.mode |= 0o222;
|
|
506
518
|
if (stats.isDirectory()) {
|
|
507
519
|
await this.writable.mkdir(path, stats.mode, stats);
|
|
520
|
+
for (const k of await this.readable.readdir(path)) {
|
|
521
|
+
await this.copyToWritable(join(path, k));
|
|
522
|
+
}
|
|
508
523
|
return;
|
|
509
524
|
}
|
|
510
525
|
const data = new Uint8Array(stats.size);
|
|
511
526
|
const readable = __addDisposableResource(env_2, await this.readable.openFile(path, 'r'), true);
|
|
512
527
|
await readable.read(data);
|
|
513
|
-
const writable = __addDisposableResource(env_2, await this.writable.createFile(path, 'w', stats.mode
|
|
528
|
+
const writable = __addDisposableResource(env_2, await this.writable.createFile(path, 'w', stats.mode, stats), true);
|
|
514
529
|
await writable.write(data);
|
|
515
530
|
}
|
|
516
531
|
catch (e_2) {
|
|
@@ -524,14 +539,6 @@ export class UnmutexedOverlayFS extends FileSystem {
|
|
|
524
539
|
}
|
|
525
540
|
}
|
|
526
541
|
}
|
|
527
|
-
/**
|
|
528
|
-
* OverlayFS makes a read-only filesystem writable by storing writes on a second,
|
|
529
|
-
* writable file system. Deletes are persisted via metadata stored on the writable
|
|
530
|
-
* file system.
|
|
531
|
-
* @internal
|
|
532
|
-
*/
|
|
533
|
-
export class OverlayFS extends Mutexed(UnmutexedOverlayFS) {
|
|
534
|
-
}
|
|
535
542
|
const _Overlay = {
|
|
536
543
|
name: 'Overlay',
|
|
537
544
|
options: {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type * as fs from 'node:fs';
|
|
2
|
+
import { File } from '../file.js';
|
|
2
3
|
import { FileSystem } from '../filesystem.js';
|
|
3
4
|
import { Stats } from '../stats.js';
|
|
4
|
-
import {
|
|
5
|
+
import type { InodeLike } from './store/inode.js';
|
|
5
6
|
export type NodeFS = typeof fs;
|
|
6
7
|
export interface PassthroughOptions {
|
|
7
8
|
fs: NodeFS;
|
|
@@ -80,11 +81,11 @@ export declare class PassthroughFS extends FileSystem {
|
|
|
80
81
|
/**
|
|
81
82
|
* Synchronize data to the file system.
|
|
82
83
|
*/
|
|
83
|
-
sync(path: string, data: Uint8Array, stats:
|
|
84
|
+
sync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): Promise<void>;
|
|
84
85
|
/**
|
|
85
86
|
* Synchronize data to the file system synchronously.
|
|
86
87
|
*/
|
|
87
|
-
syncSync(path: string, data: Uint8Array, stats:
|
|
88
|
+
syncSync(path: string, data: Uint8Array, stats: Readonly<InodeLike>): void;
|
|
88
89
|
/**
|
|
89
90
|
* Create a hard link.
|
|
90
91
|
*/
|
|
@@ -93,6 +94,10 @@ export declare class PassthroughFS extends FileSystem {
|
|
|
93
94
|
* Create a hard link synchronously.
|
|
94
95
|
*/
|
|
95
96
|
linkSync(target: string, link: string): void;
|
|
97
|
+
read(path: string, buffer: Uint8Array, offset: number, end: number): Promise<void>;
|
|
98
|
+
readSync(path: string, buffer: Uint8Array, offset: number, end: number): void;
|
|
99
|
+
write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
|
|
100
|
+
writeSync(path: string, buffer: Uint8Array, offset: number): void;
|
|
96
101
|
}
|
|
97
102
|
declare const _Passthrough: {
|
|
98
103
|
readonly name: "Passthrough";
|