@zenfs/core 2.6.1 → 2.6.3
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/cow.d.ts +1 -0
- package/dist/backends/cow.js +49 -10
- package/dist/backends/single_buffer.js +34 -11
- package/dist/context.js +1 -0
- package/dist/internal/contexts.d.ts +3 -1
- package/dist/internal/contexts.js +11 -1
- package/dist/internal/inode.d.ts +2 -0
- package/dist/internal/inode.js +2 -0
- package/dist/internal/rpc.d.ts +4 -4
- package/dist/node/async.d.ts +11 -1
- package/dist/node/async.js +14 -35
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/promises.d.ts +17 -3
- package/dist/node/promises.js +41 -17
- package/dist/node/stats.d.ts +12 -0
- package/dist/node/stats.js +35 -6
- package/dist/node/sync.d.ts +39 -11
- package/dist/node/sync.js +52 -22
- package/dist/node/utf8stream.d.ts +99 -0
- package/dist/node/utf8stream.js +690 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +17 -0
- package/dist/vfs/async.js +29 -21
- package/dist/vfs/shared.js +9 -7
- package/dist/vfs/sync.js +35 -21
- package/package.json +4 -4
- package/tests/backend/single-buffer-rotate.worker.js +19 -0
- package/tests/backend/single-buffer.test.ts +106 -0
- package/tests/common/context.test.ts +46 -0
- package/tests/common/glob.test.ts +85 -0
- package/tests/common/utf8stream.test.ts +164 -0
- package/tests/fs/concurrency.test.ts +23 -0
- package/tests/fs/directory.test.ts +21 -0
- package/tests/fs/links.test.ts +146 -0
- package/tests/fs/stat.test.ts +30 -1
- package/tests/native/setup.ts +1 -0
package/dist/backends/cow.d.ts
CHANGED
|
@@ -46,6 +46,7 @@ export declare class Journal extends EventEmitter<{
|
|
|
46
46
|
fromString(value: string): this;
|
|
47
47
|
add(op: JournalOperation, path: string): void;
|
|
48
48
|
has(op: JournalOperation, path: string): boolean;
|
|
49
|
+
delete(op: JournalOperation, path: string): void;
|
|
49
50
|
isDeleted(path: string): boolean;
|
|
50
51
|
}
|
|
51
52
|
/**
|
package/dist/backends/cow.js
CHANGED
|
@@ -53,6 +53,9 @@ export class Journal extends EventEmitter {
|
|
|
53
53
|
return true;
|
|
54
54
|
return false;
|
|
55
55
|
}
|
|
56
|
+
delete(op, path) {
|
|
57
|
+
this.entries = this.entries.filter(entry => entry.op != op || entry.path != path);
|
|
58
|
+
}
|
|
56
59
|
isDeleted(path) {
|
|
57
60
|
let deleted = false;
|
|
58
61
|
for (const entry of this.entries) {
|
|
@@ -168,6 +171,9 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
168
171
|
if (this.isDeleted(oldPath))
|
|
169
172
|
throw withErrno('ENOENT');
|
|
170
173
|
}
|
|
174
|
+
if (await this.readable.exists(oldPath))
|
|
175
|
+
this.journal.add('delete', oldPath);
|
|
176
|
+
this.journal.delete('delete', newPath);
|
|
171
177
|
this.renameInos(oldPath, newPath);
|
|
172
178
|
}
|
|
173
179
|
renameSync(oldPath, newPath) {
|
|
@@ -179,6 +185,9 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
179
185
|
if (this.isDeleted(oldPath))
|
|
180
186
|
throw withErrno('ENOENT');
|
|
181
187
|
}
|
|
188
|
+
if (this.readable.existsSync(oldPath))
|
|
189
|
+
this.journal.add('delete', oldPath);
|
|
190
|
+
this.journal.delete('delete', newPath);
|
|
182
191
|
this.renameInos(oldPath, newPath);
|
|
183
192
|
}
|
|
184
193
|
async stat(path) {
|
|
@@ -211,19 +220,25 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
211
220
|
}
|
|
212
221
|
async createFile(path, options) {
|
|
213
222
|
await this.createParentDirectories(path);
|
|
214
|
-
|
|
223
|
+
const inode = await this.writable.createFile(path, options);
|
|
224
|
+
this.journal.delete('delete', path);
|
|
225
|
+
return this.remapIno(path, inode);
|
|
215
226
|
}
|
|
216
227
|
createFileSync(path, options) {
|
|
217
228
|
this.createParentDirectoriesSync(path);
|
|
218
|
-
|
|
229
|
+
const inode = this.writable.createFileSync(path, options);
|
|
230
|
+
this.journal.delete('delete', path);
|
|
231
|
+
return this.remapIno(path, inode);
|
|
219
232
|
}
|
|
220
233
|
async link(srcpath, dstpath) {
|
|
221
234
|
await this.copyForWrite(srcpath);
|
|
222
235
|
await this.writable.link(srcpath, dstpath);
|
|
236
|
+
this.journal.delete('delete', dstpath);
|
|
223
237
|
}
|
|
224
238
|
linkSync(srcpath, dstpath) {
|
|
225
239
|
this.copyForWriteSync(srcpath);
|
|
226
240
|
this.writable.linkSync(srcpath, dstpath);
|
|
241
|
+
this.journal.delete('delete', dstpath);
|
|
227
242
|
}
|
|
228
243
|
async unlink(path) {
|
|
229
244
|
if (!(await this.exists(path)))
|
|
@@ -285,19 +300,27 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
285
300
|
if (await this.exists(path))
|
|
286
301
|
throw withErrno('EEXIST');
|
|
287
302
|
await this.createParentDirectories(path);
|
|
288
|
-
|
|
303
|
+
const inode = await this.writable.mkdir(path, options);
|
|
304
|
+
this.journal.delete('delete', path);
|
|
305
|
+
return this.remapIno(path, inode);
|
|
289
306
|
}
|
|
290
307
|
mkdirSync(path, options) {
|
|
291
308
|
if (this.existsSync(path))
|
|
292
309
|
throw withErrno('EEXIST');
|
|
293
310
|
this.createParentDirectoriesSync(path);
|
|
294
|
-
|
|
311
|
+
const inode = this.writable.mkdirSync(path, options);
|
|
312
|
+
this.journal.delete('delete', path);
|
|
313
|
+
return this.remapIno(path, inode);
|
|
295
314
|
}
|
|
296
315
|
async readdir(path) {
|
|
297
|
-
if (this.isDeleted(path)
|
|
316
|
+
if (this.isDeleted(path))
|
|
317
|
+
throw withErrno('ENOENT');
|
|
318
|
+
const readable = await this.readable.stat(path).catch(() => null);
|
|
319
|
+
const writable = await this.writable.stat(path).catch(() => null);
|
|
320
|
+
if (!readable && !writable)
|
|
298
321
|
throw withErrno('ENOENT');
|
|
299
|
-
const entries = (
|
|
300
|
-
if (
|
|
322
|
+
const entries = readable && isDirectory(readable) ? await this.readable.readdir(path) : [];
|
|
323
|
+
if (writable && isDirectory(writable))
|
|
301
324
|
for (const entry of await this.writable.readdir(path)) {
|
|
302
325
|
if (!entries.includes(entry))
|
|
303
326
|
entries.push(entry);
|
|
@@ -305,10 +328,26 @@ export class CopyOnWriteFS extends FileSystem {
|
|
|
305
328
|
return entries.filter(entry => !this.isDeleted(join(path, entry)));
|
|
306
329
|
}
|
|
307
330
|
readdirSync(path) {
|
|
308
|
-
if (this.isDeleted(path)
|
|
331
|
+
if (this.isDeleted(path))
|
|
332
|
+
throw withErrno('ENOENT');
|
|
333
|
+
let readable = null;
|
|
334
|
+
let writable = null;
|
|
335
|
+
try {
|
|
336
|
+
readable = this.readable.statSync(path);
|
|
337
|
+
}
|
|
338
|
+
catch {
|
|
339
|
+
// Not on the readable layer
|
|
340
|
+
}
|
|
341
|
+
try {
|
|
342
|
+
writable = this.writable.statSync(path);
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
// Not on the writable layer
|
|
346
|
+
}
|
|
347
|
+
if (!readable && !writable)
|
|
309
348
|
throw withErrno('ENOENT');
|
|
310
|
-
const entries =
|
|
311
|
-
if (
|
|
349
|
+
const entries = readable && isDirectory(readable) ? this.readable.readdirSync(path) : [];
|
|
350
|
+
if (writable && isDirectory(writable))
|
|
312
351
|
for (const entry of this.writable.readdirSync(path)) {
|
|
313
352
|
if (!entries.includes(entry))
|
|
314
353
|
entries.push(entry);
|
|
@@ -320,7 +320,7 @@ const sb_magic = 0x62732e7a; // 'z.sb'
|
|
|
320
320
|
* Shortcut for minor perf. bump
|
|
321
321
|
* @internal
|
|
322
322
|
*/
|
|
323
|
-
const
|
|
323
|
+
const kUsedBytes = 2;
|
|
324
324
|
/**
|
|
325
325
|
* The super block structure for a single-buffer file system
|
|
326
326
|
*/
|
|
@@ -502,16 +502,34 @@ let SuperBlock = (() => {
|
|
|
502
502
|
* @returns the new metadata block
|
|
503
503
|
*/
|
|
504
504
|
rotateMetadata() {
|
|
505
|
-
const
|
|
506
|
-
Atomics.
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
505
|
+
const alignment = BigInt(Int32Array.BYTES_PER_ELEMENT), blockSize = BigInt(sizeof(MetadataBlock));
|
|
506
|
+
let used = Atomics.load(this, kUsedBytes), offset;
|
|
507
|
+
// Padding and block space must be reserved together because other writers can advance used_bytes at any time.
|
|
508
|
+
for (;;) {
|
|
509
|
+
const padding = (alignment - (used % alignment)) % alignment;
|
|
510
|
+
offset = used + padding;
|
|
511
|
+
const next = offset + blockSize;
|
|
512
|
+
if (next > this.total_bytes)
|
|
513
|
+
throw err(withErrno('ENOSPC', 'sbfs: not enough space for a new metadata block'));
|
|
514
|
+
const observed = Atomics.compareExchange(this, kUsedBytes, used, next);
|
|
515
|
+
if (observed === used)
|
|
516
|
+
break;
|
|
517
|
+
used = observed;
|
|
518
|
+
}
|
|
519
|
+
const metadata = new MetadataBlock(this.buffer, Number(offset));
|
|
520
|
+
const offsets = new Uint32Array(this.buffer, this.byteOffset, sizeof(SuperBlock) / Uint32Array.BYTES_PER_ELEMENT);
|
|
521
|
+
let previous = Atomics.load(offsets, kMetadataOffset);
|
|
522
|
+
for (;;) {
|
|
523
|
+
metadata.previous_offset = previous;
|
|
524
|
+
_update(metadata);
|
|
525
|
+
const observed = Atomics.compareExchange(offsets, kMetadataOffset, previous, metadata.byteOffset);
|
|
526
|
+
if (observed === previous)
|
|
527
|
+
break;
|
|
528
|
+
previous = observed;
|
|
529
|
+
}
|
|
510
530
|
this.metadata = metadata;
|
|
511
|
-
this.metadata_offset = metadata.byteOffset;
|
|
512
|
-
_update(metadata);
|
|
513
531
|
_update(this);
|
|
514
|
-
debug(`sbfs: rotated metadata block at ${hex(metadata.previous_offset)} with new block at ${hex(
|
|
532
|
+
debug(`sbfs: rotated metadata block at ${hex(metadata.previous_offset)} with new block at ${hex(metadata.byteOffset)}`);
|
|
515
533
|
return metadata;
|
|
516
534
|
}
|
|
517
535
|
/**
|
|
@@ -545,6 +563,11 @@ let SuperBlock = (() => {
|
|
|
545
563
|
return SuperBlock = _classThis;
|
|
546
564
|
})();
|
|
547
565
|
export { SuperBlock };
|
|
566
|
+
/**
|
|
567
|
+
* Index of `SuperBlock.metadata_offset` in a `Uint32Array` view of the super block.
|
|
568
|
+
* @internal
|
|
569
|
+
*/
|
|
570
|
+
const kMetadataOffset = offsetof(SuperBlock, 'metadata_offset') / Uint32Array.BYTES_PER_ELEMENT;
|
|
548
571
|
/**
|
|
549
572
|
* Compute the checksum for a super block or metadata block.
|
|
550
573
|
* Note we don't include the checksum when computing a new one.
|
|
@@ -635,7 +658,7 @@ export class SingleBufferStore extends BufferView {
|
|
|
635
658
|
_update(block);
|
|
636
659
|
return;
|
|
637
660
|
}
|
|
638
|
-
entry.offset = Number(Atomics.add(this.superblock,
|
|
661
|
+
entry.offset = Number(Atomics.add(this.superblock, kUsedBytes, BigInt(data.length)));
|
|
639
662
|
entry.size = data.length;
|
|
640
663
|
this._u8.set(data, entry.offset);
|
|
641
664
|
_update(block);
|
|
@@ -657,7 +680,7 @@ export class SingleBufferStore extends BufferView {
|
|
|
657
680
|
entry = this.superblock.metadata.items[0];
|
|
658
681
|
}
|
|
659
682
|
const lock = __addDisposableResource(env_1, this.superblock.metadata.lock(), false);
|
|
660
|
-
const offset = Number(Atomics.add(this.superblock,
|
|
683
|
+
const offset = Number(Atomics.add(this.superblock, kUsedBytes, BigInt(data.length)));
|
|
661
684
|
entry.id = id;
|
|
662
685
|
entry.offset = offset;
|
|
663
686
|
entry.size = data.length;
|
package/dist/context.js
CHANGED
|
@@ -14,7 +14,7 @@ declare const kIsContext: unique symbol;
|
|
|
14
14
|
* @category Contexts
|
|
15
15
|
*/
|
|
16
16
|
export interface FSContext {
|
|
17
|
-
/**
|
|
17
|
+
/** Brand */
|
|
18
18
|
readonly [kIsContext]: boolean;
|
|
19
19
|
/** The unique ID of the context */
|
|
20
20
|
readonly id: number;
|
|
@@ -35,6 +35,8 @@ export interface FSContext {
|
|
|
35
35
|
/** The child contexts */
|
|
36
36
|
readonly children: FSContext[];
|
|
37
37
|
}
|
|
38
|
+
export declare function isContext(obj: unknown): obj is FSContext;
|
|
39
|
+
export declare function assertContext(ctx: unknown): asserts ctx is FSContext;
|
|
38
40
|
/**
|
|
39
41
|
* maybe an FS context
|
|
40
42
|
*/
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { createCredentials } from './credentials.js';
|
|
2
|
+
import { warn } from 'kerium/log';
|
|
3
|
+
import { withErrno } from 'kerium';
|
|
2
4
|
/**
|
|
3
5
|
* Symbol used for context branding
|
|
4
6
|
* @internal @hidden
|
|
5
7
|
*/
|
|
6
8
|
const kIsContext = Symbol('ZenFSContext');
|
|
9
|
+
export function isContext(obj) {
|
|
10
|
+
return typeof obj === 'object' && obj !== null && kIsContext in obj;
|
|
11
|
+
}
|
|
12
|
+
export function assertContext(ctx) {
|
|
13
|
+
if (!isContext(ctx))
|
|
14
|
+
throw warn(withErrno('EINVAL', 'Invalid context provided'));
|
|
15
|
+
}
|
|
7
16
|
/**
|
|
8
17
|
* The default/global context.
|
|
9
18
|
* @internal @hidden
|
|
@@ -20,7 +29,7 @@ export const defaultContext = {
|
|
|
20
29
|
children: [],
|
|
21
30
|
};
|
|
22
31
|
export function contextOf($) {
|
|
23
|
-
return
|
|
32
|
+
return isContext($) ? $ : defaultContext;
|
|
24
33
|
}
|
|
25
34
|
// 0 is reserved for the global/default context
|
|
26
35
|
let _nextId = 1;
|
|
@@ -33,6 +42,7 @@ let _nextId = 1;
|
|
|
33
42
|
* This exists so that `kIsContext` is not exported and to make sure the context is "secure".
|
|
34
43
|
*/
|
|
35
44
|
export function createChildContext(parent, init = {}) {
|
|
45
|
+
assertContext(parent);
|
|
36
46
|
const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials) } = init;
|
|
37
47
|
const ctx = {
|
|
38
48
|
[kIsContext]: true,
|
package/dist/internal/inode.d.ts
CHANGED
|
@@ -243,6 +243,8 @@ export declare function isFIFO(metadata: {
|
|
|
243
243
|
export declare function hasAccess($: V_Context, inode: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, access: number): boolean;
|
|
244
244
|
/**
|
|
245
245
|
* @hidden @internal
|
|
246
|
+
*
|
|
247
|
+
* @todo https://github.com/nodejs/node/pull/58836 support -1 for `*chown`
|
|
246
248
|
*/
|
|
247
249
|
export declare function _chown(stats: Partial<InodeLike>, uid: number, gid: number): boolean;
|
|
248
250
|
export {};
|
package/dist/internal/inode.js
CHANGED
package/dist/internal/rpc.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ExceptionJSON } from 'kerium';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Transferable } from 'node:worker_threads';
|
|
3
3
|
import type { WithOptional } from 'utilium';
|
|
4
4
|
import type { Backend, FilesystemOf } from '../backends/backend.js';
|
|
5
5
|
import type { PortFS } from '../backends/port.js';
|
|
6
6
|
import type { CreationOptions, FileSystem, UsageInfo } from '../internal/filesystem.js';
|
|
7
7
|
import '../polyfills.js';
|
|
8
8
|
export interface WebMessagePort {
|
|
9
|
-
postMessage(value: unknown, transfer?:
|
|
9
|
+
postMessage(value: unknown, transfer?: Transferable[]): void;
|
|
10
10
|
addEventListener(type: 'message', listener: (ev: {
|
|
11
11
|
data: any;
|
|
12
12
|
}) => void): void;
|
|
@@ -15,7 +15,7 @@ export interface WebMessagePort {
|
|
|
15
15
|
}) => void): void;
|
|
16
16
|
}
|
|
17
17
|
export interface NodeMessagePort {
|
|
18
|
-
postMessage(value: unknown, transfer?:
|
|
18
|
+
postMessage(value: unknown, transfer?: Transferable[]): void;
|
|
19
19
|
on(type: 'message', listener: (ev: any) => void): void;
|
|
20
20
|
off(type: 'message', listener: (ev: any) => void): void;
|
|
21
21
|
}
|
|
@@ -24,7 +24,7 @@ export type Channel = NodeMessagePort | WebMessagePort | WebSocket;
|
|
|
24
24
|
export interface Port<T extends Channel = Channel> {
|
|
25
25
|
readonly channel: T;
|
|
26
26
|
/** Send a request */
|
|
27
|
-
send<M extends Message>(message: M, transfer?:
|
|
27
|
+
send<M extends Message>(message: M, transfer?: Transferable[]): void;
|
|
28
28
|
/** Add a response handler */
|
|
29
29
|
addHandler<M extends Message>(handler: (message: M) => void): void;
|
|
30
30
|
/** Remove a response handler */
|
package/dist/node/async.d.ts
CHANGED
|
@@ -22,11 +22,21 @@ export declare function exists(this: V_Context, path: fs.PathLike, cb?: (exists:
|
|
|
22
22
|
export declare function stat(this: V_Context, path: fs.PathLike, callback: Callback<[Stats]>): void;
|
|
23
23
|
export declare function stat(this: V_Context, path: fs.PathLike, options: {
|
|
24
24
|
bigint?: false;
|
|
25
|
+
throwIfNoEntry?: true;
|
|
25
26
|
}, callback: Callback<[Stats]>): void;
|
|
26
27
|
export declare function stat(this: V_Context, path: fs.PathLike, options: {
|
|
27
28
|
bigint: true;
|
|
29
|
+
throwIfNoEntry?: true;
|
|
28
30
|
}, callback: Callback<[BigIntStats]>): void;
|
|
29
|
-
export declare function stat(this: V_Context, path: fs.PathLike, options:
|
|
31
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options: {
|
|
32
|
+
bigint?: false;
|
|
33
|
+
throwIfNoEntry: false;
|
|
34
|
+
}, callback: Callback<[Stats | undefined]>): void;
|
|
35
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options: {
|
|
36
|
+
bigint: true;
|
|
37
|
+
throwIfNoEntry: false;
|
|
38
|
+
}, callback: Callback<[BigIntStats | undefined]>): void;
|
|
39
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions, callback: Callback<[Stats | BigIntStats | undefined]>): void;
|
|
30
40
|
/**
|
|
31
41
|
* Asynchronous `lstat`.
|
|
32
42
|
* `lstat()` is identical to `stat()`, except that if path is a symbolic link,
|
package/dist/node/async.js
CHANGED
|
@@ -148,47 +148,26 @@ fdatasync;
|
|
|
148
148
|
export function write(fd, data, cbPosOff, cbLenEnc, cbPosEnc, cb = nop) {
|
|
149
149
|
let buffer, offset, length, position, encoding;
|
|
150
150
|
const handle = new promises.FileHandle(this, fd);
|
|
151
|
+
const _cb = ([cbPosOff, cbLenEnc, cbPosEnc].find(arg => typeof arg === 'function') ?? cb);
|
|
151
152
|
if (typeof data === 'string') {
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// (fd, string, cb)
|
|
157
|
-
cb = cbPosOff;
|
|
158
|
-
break;
|
|
159
|
-
case 'number':
|
|
160
|
-
// (fd, string, position, encoding?, cb?)
|
|
161
|
-
position = cbPosOff;
|
|
162
|
-
encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
|
|
163
|
-
cb = typeof cbPosEnc === 'function' ? cbPosEnc : cb;
|
|
164
|
-
break;
|
|
165
|
-
default:
|
|
166
|
-
// ...try to find the callback and get out of here!
|
|
167
|
-
cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb);
|
|
168
|
-
cb(withErrno('EINVAL'));
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
buffer = Buffer.from(data);
|
|
153
|
+
// (fd, string, [position?, [encoding?]], cb?)
|
|
154
|
+
position = typeof cbPosOff === 'number' ? cbPosOff : null;
|
|
155
|
+
encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
|
|
156
|
+
buffer = Buffer.from(data, encoding);
|
|
172
157
|
offset = 0;
|
|
173
|
-
length = buffer.
|
|
174
|
-
const _cb = cb;
|
|
175
|
-
handle
|
|
176
|
-
.write(buffer, offset, length, position)
|
|
177
|
-
.then(({ bytesWritten }) => _cb(null, bytesWritten, buffer.toString(encoding)))
|
|
178
|
-
.catch(_cb);
|
|
158
|
+
length = buffer.byteLength;
|
|
179
159
|
}
|
|
180
160
|
else {
|
|
181
|
-
//
|
|
182
|
-
buffer = Buffer.from(data.buffer);
|
|
183
|
-
offset = cbPosOff;
|
|
184
|
-
length = cbLenEnc;
|
|
161
|
+
// (fd, buffer, [offset?, [length?, [position?]]], cb?)
|
|
162
|
+
buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
163
|
+
offset = typeof cbPosOff === 'number' ? cbPosOff : 0;
|
|
164
|
+
length = typeof cbLenEnc === 'number' ? cbLenEnc : buffer.byteLength - offset;
|
|
185
165
|
position = typeof cbPosEnc === 'number' ? cbPosEnc : null;
|
|
186
|
-
const _cb = (typeof cbPosEnc === 'function' ? cbPosEnc : cb);
|
|
187
|
-
void handle
|
|
188
|
-
.write(buffer, offset, length, position)
|
|
189
|
-
.then(({ bytesWritten }) => _cb(null, bytesWritten, buffer))
|
|
190
|
-
.catch(_cb);
|
|
191
166
|
}
|
|
167
|
+
handle
|
|
168
|
+
.write(buffer, offset, length, position)
|
|
169
|
+
.then(({ bytesWritten }) => _cb(null, bytesWritten, typeof data === 'string' ? data : buffer))
|
|
170
|
+
.catch(_cb);
|
|
192
171
|
}
|
|
193
172
|
write;
|
|
194
173
|
/**
|
package/dist/node/index.d.ts
CHANGED
package/dist/node/index.js
CHANGED
package/dist/node/promises.d.ts
CHANGED
|
@@ -180,11 +180,25 @@ export declare function rename(this: V_Context, oldPath: fs.PathLike, newPath: f
|
|
|
180
180
|
* Test whether or not `path` exists by checking with the file system.
|
|
181
181
|
*/
|
|
182
182
|
export declare function exists(this: V_Context, path: fs.PathLike): Promise<boolean>;
|
|
183
|
-
export declare function stat(this: V_Context, path: fs.PathLike, options: fs.BigIntOptions
|
|
184
|
-
|
|
183
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options: fs.BigIntOptions & {
|
|
184
|
+
throwIfNoEntry?: true;
|
|
185
|
+
}): Promise<BigIntStats>;
|
|
186
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options?: fs.StatOptions & {
|
|
185
187
|
bigint?: false;
|
|
188
|
+
throwIfNoEntry?: true;
|
|
186
189
|
}): Promise<Stats>;
|
|
187
|
-
export declare function stat(this: V_Context, path: fs.PathLike, options
|
|
190
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
|
|
191
|
+
bigint?: false;
|
|
192
|
+
throwIfNoEntry: false;
|
|
193
|
+
}): Promise<Stats | undefined>;
|
|
194
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
|
|
195
|
+
bigint: true;
|
|
196
|
+
throwIfNoEntry: false;
|
|
197
|
+
}): Promise<BigIntStats | undefined>;
|
|
198
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options: fs.StatOptions & {
|
|
199
|
+
throwIfNoEntry?: true;
|
|
200
|
+
}): Promise<Stats | BigIntStats>;
|
|
201
|
+
export declare function stat(this: V_Context, path: fs.PathLike, options?: fs.StatOptions): Promise<Stats | BigIntStats | undefined>;
|
|
188
202
|
/**
|
|
189
203
|
* `lstat`.
|
|
190
204
|
* `lstat()` is identical to `stat()`, except that if path is a symbolic link,
|
package/dist/node/promises.js
CHANGED
|
@@ -54,10 +54,11 @@ import { Buffer } from 'buffer';
|
|
|
54
54
|
import { Exception, rethrow, UV } from 'kerium';
|
|
55
55
|
import { encodeUTF8 } from 'utilium';
|
|
56
56
|
import * as constants from '../constants.js';
|
|
57
|
+
import { contextOf } from '../internal/contexts.js';
|
|
57
58
|
import { hasAccess, InodeFlags, isDirectory } from '../internal/inode.js';
|
|
58
|
-
import { dirname, join, matchesGlob } from '../path.js';
|
|
59
|
+
import { dirname, join, matchesGlob, relative, resolve } from '../path.js';
|
|
59
60
|
import '../polyfills.js';
|
|
60
|
-
import { _tempDirName, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime, validateFD } from '../utils.js';
|
|
61
|
+
import { _isNoEntry, _tempDirName, globToRegex, normalizeMode, normalizeOptions, normalizePath, normalizeTime, validateFD } from '../utils.js';
|
|
61
62
|
import * as _async from '../vfs/async.js';
|
|
62
63
|
import { checkAccess } from '../vfs/config.js';
|
|
63
64
|
import { deleteFD, fromFD, toFD } from '../vfs/file.js';
|
|
@@ -541,7 +542,15 @@ export async function exists(path) {
|
|
|
541
542
|
}
|
|
542
543
|
}
|
|
543
544
|
export async function stat(path, options) {
|
|
544
|
-
|
|
545
|
+
let stats;
|
|
546
|
+
try {
|
|
547
|
+
stats = await _async.stat.call(this, path, false);
|
|
548
|
+
}
|
|
549
|
+
catch (e) {
|
|
550
|
+
if (options?.throwIfNoEntry === false && _isNoEntry(e))
|
|
551
|
+
return;
|
|
552
|
+
throw e;
|
|
553
|
+
}
|
|
545
554
|
return options?.bigint ? new BigIntStats(stats) : new Stats(stats);
|
|
546
555
|
}
|
|
547
556
|
stat;
|
|
@@ -1081,8 +1090,16 @@ export async function cp(source, destination, opts) {
|
|
|
1081
1090
|
await Promise.all(entries.map(_cp));
|
|
1082
1091
|
break;
|
|
1083
1092
|
}
|
|
1084
|
-
case constants.S_IFREG:
|
|
1085
1093
|
case constants.S_IFLNK:
|
|
1094
|
+
if (opts?.dereference) {
|
|
1095
|
+
await copyFile.call(this, source, destination);
|
|
1096
|
+
}
|
|
1097
|
+
else {
|
|
1098
|
+
const link = await readlink.call(this, source);
|
|
1099
|
+
await symlink.call(this, opts?.verbatimSymlinks ? link : resolve.call(this, dirname(source), link), destination);
|
|
1100
|
+
}
|
|
1101
|
+
break;
|
|
1102
|
+
case constants.S_IFREG:
|
|
1086
1103
|
await copyFile.call(this, source, destination);
|
|
1087
1104
|
break;
|
|
1088
1105
|
case constants.S_IFBLK:
|
|
@@ -1105,8 +1122,10 @@ export async function statfs(path, opts) {
|
|
|
1105
1122
|
}
|
|
1106
1123
|
export function glob(pattern, opt) {
|
|
1107
1124
|
pattern = Array.isArray(pattern) ? pattern : [pattern];
|
|
1108
|
-
const
|
|
1109
|
-
const
|
|
1125
|
+
const $ = contextOf(this);
|
|
1126
|
+
const { cwd = $.pwd, withFileTypes = false, exclude = () => false, followSymlinks = false } = opt || {};
|
|
1127
|
+
const base = resolve.call($, cwd instanceof URL ? cwd.pathname : cwd);
|
|
1128
|
+
const normalizedPatterns = pattern.map(p => (p.startsWith('/') ? relative.call($, base, p) : p));
|
|
1110
1129
|
const hasGlobStar = normalizedPatterns.some(p => p.includes('**'));
|
|
1111
1130
|
const patternBases = normalizedPatterns.map(p => {
|
|
1112
1131
|
const firstGlob = p.search(/[*?[\]{]/);
|
|
@@ -1116,23 +1135,28 @@ export function glob(pattern, opt) {
|
|
|
1116
1135
|
return lastSlash === -1 ? '' : p.slice(0, lastSlash);
|
|
1117
1136
|
});
|
|
1118
1137
|
const regexPatterns = normalizedPatterns.map(globToRegex);
|
|
1138
|
+
async function shouldDescend(path, relativePath) {
|
|
1139
|
+
const isNamed = patternBases.some(base => relativePath === base || base.startsWith(relativePath + '/'));
|
|
1140
|
+
const inScope = hasGlobStar || isNamed;
|
|
1141
|
+
const stats = await lstat.call($, path);
|
|
1142
|
+
if (!stats.isSymbolicLink())
|
|
1143
|
+
return inScope && stats.isDirectory();
|
|
1144
|
+
return inScope && (followSymlinks || isNamed) && !!(await stat.call($, path, { throwIfNoEntry: false }))?.isDirectory();
|
|
1145
|
+
}
|
|
1119
1146
|
async function* recursiveList(dir) {
|
|
1120
|
-
const entries = await readdir(dir, { withFileTypes, encoding: 'utf8' });
|
|
1147
|
+
const entries = await readdir.call($, dir, { withFileTypes, encoding: 'utf8' });
|
|
1121
1148
|
for (const entry of entries) {
|
|
1122
1149
|
const fullPath = join(dir, withFileTypes ? entry.name : entry);
|
|
1123
|
-
|
|
1150
|
+
const relPath = relative.call($, base, fullPath);
|
|
1151
|
+
if (typeof exclude != 'function' ? exclude.some(p => matchesGlob(p, relPath)) : exclude((withFileTypes ? entry : relPath)))
|
|
1124
1152
|
continue;
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
if (regexPatterns.some(rx => rx.test(relativePath))) {
|
|
1132
|
-
yield withFileTypes ? entry : relativePath;
|
|
1153
|
+
if (await shouldDescend(fullPath, relPath))
|
|
1154
|
+
yield* recursiveList(fullPath);
|
|
1155
|
+
if (regexPatterns.some(rx => rx.test(relPath))) {
|
|
1156
|
+
yield withFileTypes ? entry : relPath;
|
|
1133
1157
|
}
|
|
1134
1158
|
}
|
|
1135
1159
|
}
|
|
1136
|
-
return recursiveList(
|
|
1160
|
+
return recursiveList(base);
|
|
1137
1161
|
}
|
|
1138
1162
|
glob;
|
package/dist/node/stats.d.ts
CHANGED
|
@@ -91,24 +91,32 @@ export declare abstract class StatsCommon<T extends number | bigint> implements
|
|
|
91
91
|
atimeMs: T;
|
|
92
92
|
get atime(): Date;
|
|
93
93
|
set atime(value: Date);
|
|
94
|
+
get atimeInstant(): Temporal.Instant;
|
|
95
|
+
set atimeInstant(value: Temporal.Instant);
|
|
94
96
|
/**
|
|
95
97
|
* Time of last modification, since epoch
|
|
96
98
|
*/
|
|
97
99
|
mtimeMs: T;
|
|
98
100
|
get mtime(): Date;
|
|
99
101
|
set mtime(value: Date);
|
|
102
|
+
get mtimeInstant(): Temporal.Instant;
|
|
103
|
+
set mtimeInstant(value: Temporal.Instant);
|
|
100
104
|
/**
|
|
101
105
|
* Time of last time file status was changed, since epoch
|
|
102
106
|
*/
|
|
103
107
|
ctimeMs: T;
|
|
104
108
|
get ctime(): Date;
|
|
105
109
|
set ctime(value: Date);
|
|
110
|
+
get ctimeInstant(): Temporal.Instant;
|
|
111
|
+
set ctimeInstant(value: Temporal.Instant);
|
|
106
112
|
/**
|
|
107
113
|
* Time of file creation, since epoch
|
|
108
114
|
*/
|
|
109
115
|
birthtimeMs: T;
|
|
110
116
|
get birthtime(): Date;
|
|
111
117
|
set birthtime(value: Date);
|
|
118
|
+
get birthtimeInstant(): Temporal.Instant;
|
|
119
|
+
set birthtimeInstant(value: Temporal.Instant);
|
|
112
120
|
/**
|
|
113
121
|
* Size of the item in bytes.
|
|
114
122
|
* For directories/symlinks, this is normally the size of the struct that represents the item.
|
|
@@ -193,6 +201,8 @@ export declare class StatsFs implements Node.StatsFsBase<number> {
|
|
|
193
201
|
bavail: number;
|
|
194
202
|
/** Total file nodes in file system. */
|
|
195
203
|
files: number;
|
|
204
|
+
/** Fundamental file system block size. */
|
|
205
|
+
frsize: number;
|
|
196
206
|
/** Free file nodes in file system. */
|
|
197
207
|
ffree: number;
|
|
198
208
|
}
|
|
@@ -212,6 +222,8 @@ export declare class BigIntStatsFs implements Node.StatsFsBase<bigint> {
|
|
|
212
222
|
bavail: bigint;
|
|
213
223
|
/** Total file nodes in file system. */
|
|
214
224
|
files: bigint;
|
|
225
|
+
/** Fundamental file system block size. */
|
|
226
|
+
frsize: bigint;
|
|
215
227
|
/** Free file nodes in file system. */
|
|
216
228
|
ffree: bigint;
|
|
217
229
|
}
|