@zenfs/core 1.10.0 → 1.10.2

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.
@@ -1,7 +1,7 @@
1
1
  export * from './backend.js';
2
+ export * from './cow.js';
2
3
  export * from './fetch.js';
3
4
  export * from './memory.js';
4
- export * from './overlay.js';
5
5
  export * from './passthrough.js';
6
6
  export * from './port/fs.js';
7
7
  export * from './single_buffer.js';
@@ -1,7 +1,7 @@
1
1
  export * from './backend.js';
2
+ export * from './cow.js';
2
3
  export * from './fetch.js';
3
4
  export * from './memory.js';
4
- export * from './overlay.js';
5
5
  export * from './passthrough.js';
6
6
  export * from './port/fs.js';
7
7
  export * from './single_buffer.js';
@@ -124,7 +124,7 @@ const _Port = {
124
124
  validator(port) {
125
125
  // Check for a `postMessage` function.
126
126
  if (typeof (port === null || port === void 0 ? void 0 : port.postMessage) != 'function') {
127
- throw err(new ErrnoError(Errno.EINVAL, 'option must be a port.'));
127
+ throw err(new ErrnoError(Errno.EINVAL, 'option must be a port'));
128
128
  }
129
129
  },
130
130
  },
@@ -265,7 +265,7 @@ let SuperBlock = (() => {
265
265
  __runInitializers(this, __padding_extraInitializers);
266
266
  this.store = store;
267
267
  if (store._view.getUint32(offsetof(SuperBlock, 'magic'), true) != sb_magic) {
268
- warn('SingleBuffer: Invalid magic value. Assuming this is a fresh super block.');
268
+ warn('SingleBuffer: Invalid magic value, assuming this is a fresh super block');
269
269
  this.metadata = new MetadataBlock(this);
270
270
  this.used_bytes = BigInt(sizeof(SuperBlock) + sizeof(MetadataBlock));
271
271
  this.total_bytes = BigInt(store._buffer.byteLength);
@@ -371,7 +371,7 @@ export class SingleBufferStore {
371
371
  this.name = 'sbfs';
372
372
  this.id = 0x73626673; // 'sbfs'
373
373
  if (buffer.byteLength < sizeof(SuperBlock) + sizeof(MetadataBlock))
374
- throw crit(new ErrnoError(Errno.EINVAL, 'SingleBuffer: Buffer is too small for a file system.'));
374
+ throw crit(new ErrnoError(Errno.EINVAL, 'SingleBuffer: Buffer is too small for a file system'));
375
375
  this._view = !ArrayBuffer.isView(buffer) ? new DataView(buffer) : new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
376
376
  this._buffer = !ArrayBuffer.isView(buffer) ? new Uint8Array(buffer) : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
377
377
  this.superblock = new SuperBlock(this);
@@ -467,8 +467,8 @@ export class SingleBufferStore {
467
467
  }
468
468
  }
469
469
  }
470
- async sync() {
471
- return;
470
+ sync() {
471
+ return Promise.resolve();
472
472
  }
473
473
  usage() {
474
474
  return {
@@ -792,11 +792,11 @@ export class StoreFS extends FileSystem {
792
792
  warn('Attempted to populate tables after initialization');
793
793
  return;
794
794
  }
795
- debug('Populating tables with existing store metadata.');
795
+ debug('Populating tables with existing store metadata');
796
796
  const tx = __addDisposableResource(env_23, this.transaction(), true);
797
797
  const rootData = await tx.get(rootIno);
798
798
  if (!rootData) {
799
- notice('Store does not have a root inode.');
799
+ notice('Store does not have a root inode');
800
800
  const inode = new Inode({ ino: rootIno, data: 1, mode: 0o777 | S_IFDIR });
801
801
  await tx.set(inode.data, encodeUTF8('{}'));
802
802
  this._add(rootIno, '/');
@@ -805,7 +805,7 @@ export class StoreFS extends FileSystem {
805
805
  return;
806
806
  }
807
807
  if (rootData.length != __inode_sz) {
808
- crit('Store contains an invalid root inode. Refusing to populate tables.');
808
+ crit('Store contains an invalid root inode. Refusing to populate tables');
809
809
  return;
810
810
  }
811
811
  // Keep track of directories we have already traversed to avoid loops
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /// <reference path="../types/readable-stream.d.ts" preserve="true" />
1
2
  export * from './backends/index.js';
2
3
  export * from './config.js';
3
4
  export * from './context.js';
@@ -9,10 +10,3 @@ export * from './vfs/index.js';
9
10
  export { fs };
10
11
  import * as fs from './vfs/index.js';
11
12
  export default fs;
12
- declare global {
13
- /**
14
- * Global VFS. Do not use unless absolutely needed.
15
- * @hidden
16
- */
17
- var __zenfs__: typeof fs;
18
- }
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
2
+ /// <reference path="../types/readable-stream.d.ts" preserve="true" />
1
3
  export * from './backends/index.js';
2
4
  export * from './config.js';
3
5
  export * from './context.js';
@@ -241,7 +241,7 @@ export class DeviceFS extends StoreFS {
241
241
  this._createDevice(fullDevice);
242
242
  this._createDevice(randomDevice);
243
243
  this._createDevice(consoleDevice);
244
- debug('Added default devices.');
244
+ debug('Added default devices');
245
245
  }
246
246
  constructor() {
247
247
  // Please don't store your temporary files in /dev.
@@ -280,7 +280,7 @@ export class PreloadFile extends File {
280
280
  throw ErrnoError.With('EBADF', this.path, 'truncate');
281
281
  this.dirty = true;
282
282
  if (!isWriteable(this.flag)) {
283
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
283
+ throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
284
284
  }
285
285
  this.stats.mtimeMs = Date.now();
286
286
  if (length > this._buffer.length) {
@@ -307,7 +307,7 @@ export class PreloadFile extends File {
307
307
  if (this.closed)
308
308
  throw ErrnoError.With('EBADF', this.path, 'write');
309
309
  if (!isWriteable(this.flag)) {
310
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
310
+ throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
311
311
  }
312
312
  this.dirty = true;
313
313
  const end = position + length;
@@ -353,7 +353,7 @@ export class PreloadFile extends File {
353
353
  if (this.closed)
354
354
  throw ErrnoError.With('EBADF', this.path, 'read');
355
355
  if (!isReadable(this.flag)) {
356
- throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
356
+ throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode');
357
357
  }
358
358
  if (config.updateOnRead) {
359
359
  this.dirty = true;
@@ -572,7 +572,7 @@ export class LazyFile extends File {
572
572
  throw ErrnoError.With('EBADF', this.path, 'truncate');
573
573
  this.dirty = true;
574
574
  if (!isWriteable(this.flag)) {
575
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
575
+ throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
576
576
  }
577
577
  this.stats.mtimeMs = Date.now();
578
578
  this.stats.size = length;
@@ -584,7 +584,7 @@ export class LazyFile extends File {
584
584
  throw ErrnoError.With('EBADF', this.path, 'truncate');
585
585
  this.dirty = true;
586
586
  if (!isWriteable(this.flag)) {
587
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
587
+ throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
588
588
  }
589
589
  this.stats.mtimeMs = Date.now();
590
590
  this.stats.size = length;
@@ -595,7 +595,7 @@ export class LazyFile extends File {
595
595
  if (this.closed)
596
596
  throw ErrnoError.With('EBADF', this.path, 'write');
597
597
  if (!isWriteable(this.flag)) {
598
- throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode.');
598
+ throw new ErrnoError(Errno.EPERM, 'File not opened with a writeable mode');
599
599
  }
600
600
  this.dirty = true;
601
601
  const end = position + length;
@@ -644,7 +644,7 @@ export class LazyFile extends File {
644
644
  if (this.closed)
645
645
  throw ErrnoError.With('EBADF', this.path, 'read');
646
646
  if (!isReadable(this.flag))
647
- throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode.');
647
+ throw new ErrnoError(Errno.EPERM, 'File not opened with a readable mode');
648
648
  if (config.updateOnRead)
649
649
  this.dirty = true;
650
650
  this.stats.atimeMs = Date.now();
@@ -88,16 +88,17 @@ export declare function log_deprecated(symbol: string): void;
88
88
  */
89
89
  export declare const formats: {
90
90
  /** Format with a timestamp and the level, colorized with ANSI escape codes */
91
- readonly ansi_level: (this: void, entry: Entry) => string;
91
+ readonly ansi_level: (this: void, entry: Entry) => string[];
92
92
  /**
93
93
  * Format with a timestamp and colorize the message with ANSI escape codes.
94
94
  * For EMERG and ALERT, the levels are included
95
95
  */
96
96
  readonly ansi_message: (this: void, entry: Entry) => string;
97
- /** Uncolored format with a timestamp */
97
+ readonly css_level: (this: void, entry: Entry) => string[];
98
+ readonly css_message: (this: void, entry: Entry) => string[];
98
99
  readonly default: (this: void, entry: Entry) => string;
99
100
  };
100
- export declare function format(entry: Entry): string;
101
+ export declare function format(entry: Entry): string[];
101
102
  /** Whether log entries are being recorded */
102
103
  export declare let isEnabled: boolean;
103
104
  export interface LogConfiguration {
@@ -115,12 +116,12 @@ export interface LogConfiguration {
115
116
  * Formats a log entry into a string
116
117
  * @default `[${ms / 1000}] ${message}`
117
118
  */
118
- format?(this: void, entry: Entry): string;
119
+ format?(this: void, entry: Entry): string | string[];
119
120
  /**
120
121
  * Outputs a log message
121
122
  * @default console.error()
122
123
  */
123
- output?(this: void, message: string): unknown;
124
+ output?(this: void, ...message: string[]): unknown;
124
125
  /**
125
126
  * If set, output() all current entries after `configure` is done
126
127
  * @default false
@@ -97,9 +97,16 @@ export function log_deprecated(symbol) {
97
97
  function ansi(text, format) {
98
98
  return `\x1b[${format}m${text}\x1b[0m`;
99
99
  }
100
- function _prettyMs(entry, useANSI = false) {
100
+ function _prettyMs(entry, style) {
101
101
  const text = '[' + (entry.elapsedMs / 1000).toFixed(3).padStart(10) + '] ';
102
- return useANSI ? ansi(text, '2;37') : text;
102
+ switch (style) {
103
+ case 'ansi':
104
+ return ansi(text, '2;37');
105
+ case 'css':
106
+ return ['%c' + text, 'opacity: 0.8; color: white;'];
107
+ default:
108
+ return text;
109
+ }
103
110
  }
104
111
  const _ansiLevelColor = {
105
112
  [Level.EMERG]: '1;4;37;41',
@@ -121,6 +128,26 @@ const _ansiMessageColor = {
121
128
  [Level.INFO]: '37',
122
129
  [Level.DEBUG]: '2;37',
123
130
  };
131
+ const _cssLevelColor = {
132
+ [Level.EMERG]: 'font-weight: bold; text-decoration: underline; color: white; background-color: red;',
133
+ [Level.ALERT]: 'font-weight: bold; color: white; background-color: red;',
134
+ [Level.CRIT]: 'font-weight: bold; color: magenta;',
135
+ [Level.ERR]: 'font-weight: bold; color: red;',
136
+ [Level.WARN]: 'font-weight: bold; color: yellow;',
137
+ [Level.NOTICE]: 'font-weight: bold; color: cyan;',
138
+ [Level.INFO]: 'font-weight: bold; color: white;',
139
+ [Level.DEBUG]: 'opacity: 0.8; color: white;',
140
+ };
141
+ const _cssMessageColor = {
142
+ [Level.EMERG]: 'font-weight: bold; color: red;',
143
+ [Level.ALERT]: 'font-weight: bold; color: red;',
144
+ [Level.CRIT]: 'font-weight: bold; color: red;',
145
+ [Level.ERR]: 'color: red;',
146
+ [Level.WARN]: 'color: yellow;',
147
+ [Level.NOTICE]: 'font-weight: bold; color: white;',
148
+ [Level.INFO]: 'color: white;',
149
+ [Level.DEBUG]: 'opacity: 0.8; color: white;',
150
+ };
124
151
  /**
125
152
  * Various format functions included to make using the logger easier.
126
153
  * These are not the only formats you can use.
@@ -129,34 +156,48 @@ export const formats = {
129
156
  /** Format with a timestamp and the level, colorized with ANSI escape codes */
130
157
  ansi_level(entry) {
131
158
  const levelText = ansi(levels[entry.level].toUpperCase(), _ansiLevelColor[entry.level]);
132
- return [_prettyMs(entry, true), levelText, entry.message].join(' ');
159
+ return [_prettyMs(entry, 'ansi'), levelText, entry.message];
133
160
  },
134
161
  /**
135
162
  * Format with a timestamp and colorize the message with ANSI escape codes.
136
163
  * For EMERG and ALERT, the levels are included
137
164
  */
138
165
  ansi_message(entry) {
139
- let msg = _prettyMs(entry, true);
166
+ let msg = _prettyMs(entry, 'ansi');
140
167
  const isImportant = entry.level < Level.CRIT;
141
168
  if (isImportant)
142
169
  msg += ansi(levels[entry.level].toUpperCase(), _ansiLevelColor[entry.level]) + ': ';
143
170
  msg += ansi(entry.message, _ansiMessageColor[entry.level]);
144
171
  return msg;
145
172
  },
146
- /** Uncolored format with a timestamp */
173
+ css_level(entry) {
174
+ const levelLabel = levels[entry.level].toUpperCase();
175
+ return [..._prettyMs(entry, 'css'), `%c${levelLabel}%c ${entry.message}`, _cssLevelColor[entry.level], ''];
176
+ },
177
+ css_message(entry) {
178
+ const text = _prettyMs(entry, 'css');
179
+ const isImportant = entry.level < Level.CRIT;
180
+ if (isImportant) {
181
+ const levelLabel = levels[entry.level].toUpperCase();
182
+ text.push(`%c${levelLabel}%c:`, _cssLevelColor[entry.level], '');
183
+ }
184
+ text.push('%c' + entry.message, _cssMessageColor[entry.level]);
185
+ return text;
186
+ },
147
187
  default(entry) {
148
188
  return `[${_prettyMs(entry)}] ${entry.message}`;
149
189
  },
150
190
  };
151
191
  let _format = formats.default;
152
192
  export function format(entry) {
153
- return _format(entry);
193
+ const formatted = _format(entry);
194
+ return Array.isArray(formatted) ? formatted : [formatted];
154
195
  }
155
196
  let _output = console.error;
156
197
  function output(entry) {
157
198
  if (entry.level > minLevel)
158
199
  return;
159
- _output(format(entry));
200
+ _output(...format(entry));
160
201
  }
161
202
  let minLevel = Level.ALERT;
162
203
  // Configuration
@@ -50,10 +50,11 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
+ import { getAllPrototypes } from 'utilium';
53
54
  import { StoreFS } from '../backends/store/fs.js';
54
55
  import { Errno, ErrnoError } from '../internal/error.js';
55
56
  import { LazyFile, parseFlag } from '../internal/file.js';
56
- import { crit, err, notice } from '../internal/log.js';
57
+ import { crit, debug, err } from '../internal/log.js';
57
58
  import { join } from '../vfs/path.js';
58
59
  /**
59
60
  * Async() implements synchronous methods on an asynchronous file system
@@ -80,6 +81,8 @@ export function Async(FS) {
80
81
  super(...args);
81
82
  this._promise = Promise.resolve();
82
83
  this._isInitialized = false;
84
+ /** Tracks how many updates to the sync. cache we skipped during initialization */
85
+ this._skippedCacheUpdates = 0;
83
86
  this._patchAsync();
84
87
  }
85
88
  async ready() {
@@ -103,6 +106,7 @@ export function Async(FS) {
103
106
  }
104
107
  try {
105
108
  await this.crossCopy('/');
109
+ debug(`Skipped ${this._skippedCacheUpdates} updates to the sync cache during initialization`);
106
110
  this._isInitialized = true;
107
111
  }
108
112
  catch (e) {
@@ -131,9 +135,9 @@ export function Async(FS) {
131
135
  }
132
136
  createFileSync(path, flag, mode, options) {
133
137
  this.checkSync(path, 'createFile');
134
- this._sync.createFileSync(path, flag, mode, options);
138
+ const file = this._sync.createFileSync(path, flag, mode, options);
135
139
  this._async(this.createFile(path, flag, mode, options));
136
- return this.openFileSync(path, flag);
140
+ return new LazyFile(this, path, flag, file.statSync());
137
141
  }
138
142
  openFileSync(path, flag) {
139
143
  this.checkSync(path, 'openFile');
@@ -220,38 +224,30 @@ export function Async(FS) {
220
224
  }
221
225
  /**
222
226
  * @internal
223
- * Patch all async methods to also call their synchronous counterparts unless called from the queue
227
+ * Patch all async methods to also call their synchronous counterparts unless called from themselves (either sync or async)
224
228
  */
225
229
  _patchAsync() {
226
- const asyncFSMethodKeys = [
227
- 'rename',
228
- 'stat',
229
- 'createFile',
230
- 'openFile',
231
- 'unlink',
232
- 'rmdir',
233
- 'mkdir',
234
- 'readdir',
235
- 'link',
236
- 'sync',
237
- 'exists',
238
- ];
239
- for (const key of asyncFSMethodKeys) {
240
- if (typeof this[key] !== 'function')
241
- continue;
230
+ const methods = Array.from(getAllPrototypes(this))
231
+ .flatMap(Object.getOwnPropertyNames)
232
+ .filter(key => typeof this[key] == 'function' && `${key}Sync` in this);
233
+ debug('Async: patching methods: ' + methods.join(', '));
234
+ for (const key of methods) {
235
+ // TS does not narrow the union based on the key
242
236
  const originalMethod = this[key];
243
237
  this[key] = async (...args) => {
244
- var _a, _b;
238
+ var _a, _b, _c;
245
239
  const result = await originalMethod.apply(this, args);
246
- if (new Error().stack.includes(`at <computed> [as ${key}]`))
240
+ const stack = (_a = new Error().stack) === null || _a === void 0 ? void 0 : _a.split('\n').slice(2).join('\n');
241
+ // !stack == From the async queue
242
+ if ((stack === null || stack === void 0 ? void 0 : stack.includes(`at <computed> [as ${key}]`)) || (stack === null || stack === void 0 ? void 0 : stack.includes(`${key}Sync `)) || !stack)
247
243
  return result;
248
244
  if (!this._isInitialized) {
249
- notice('Skipping sync cache update for Async#' + key);
245
+ this._skippedCacheUpdates++;
250
246
  return result;
251
247
  }
252
248
  try {
253
- // @ts-expect-error 2556
254
- (_b = (_a = this._sync) === null || _a === void 0 ? void 0 : _a[`${key}Sync`]) === null || _b === void 0 ? void 0 : _b.call(_a, ...args);
249
+ // @ts-expect-error 2556 - The type of `args` is not narrowed
250
+ (_c = (_b = this._sync) === null || _b === void 0 ? void 0 : _b[`${key}Sync`]) === null || _c === void 0 ? void 0 : _c.call(_b, ...args);
255
251
  }
256
252
  catch (e) {
257
253
  throw err(new ErrnoError(e.errno, e.message + ' (Out of sync!)', e.path, key), { fs: this });
@@ -8,12 +8,12 @@ import type { FileSystem } from '../internal/filesystem.js';
8
8
  export type Mixin<TBase extends typeof FileSystem, TMixin> = (abstract new (...args: any[]) => TMixin) & TBase;
9
9
  /**
10
10
  * @internal @hidden
11
- * Note this include `existsSync`, even though it is a concrete method.
11
+ * Note this includes `existsSync`, even though it is a concrete method.
12
12
  */
13
13
  export type _SyncFSKeys = Exclude<Extract<keyof FileSystem, `${string}Sync`>, '_disableSync'>;
14
14
  /**
15
15
  * @internal @hidden
16
- * Note this include `exists`, even though it is a concrete method.
16
+ * Note this includes `exists`, even though it is a concrete method.
17
17
  */
18
18
  export type _AsyncFSKeys = {
19
19
  [K in _SyncFSKeys]: K extends `${infer T}Sync` ? T : never;
@@ -1,5 +1 @@
1
- declare global {
2
- interface AsyncIterator<T, TReturn, TNext> extends AsyncDisposable {
3
- }
4
- }
5
1
  export {};
package/dist/polyfills.js CHANGED
@@ -1,17 +1,20 @@
1
+ /* node:coverage disable */
1
2
  var _a, _b, _c;
3
+ import { warn } from './internal/log.js';
2
4
  // eslint-disable-next-line @typescript-eslint/unbound-method
3
- (_a = Promise.withResolvers) !== null && _a !== void 0 ? _a : (Promise.withResolvers = function () {
4
- let _resolve,
5
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
6
- _reject;
7
- const promise = new Promise((resolve, reject) => {
8
- _resolve = resolve;
9
- _reject = reject;
10
- });
11
- return { promise, resolve: _resolve, reject: _reject };
12
- });
5
+ (_a = Promise.withResolvers) !== null && _a !== void 0 ? _a : (Promise.withResolvers = (warn('Using a polyfill of Promise.withResolvers'),
6
+ function () {
7
+ let _resolve,
8
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
+ _reject;
10
+ const promise = new Promise((resolve, reject) => {
11
+ _resolve = resolve;
12
+ _reject = reject;
13
+ });
14
+ return { promise, resolve: _resolve, reject: _reject };
15
+ }));
13
16
  // @ts-expect-error 2540
14
- (_b = Symbol['dispose']) !== null && _b !== void 0 ? _b : (Symbol['dispose'] = Symbol('Symbol.dispose'));
17
+ (_b = Symbol['dispose']) !== null && _b !== void 0 ? _b : (Symbol['dispose'] = (warn('Using a polyfill of Symbol.dispose'), Symbol('Symbol.dispose')));
15
18
  // @ts-expect-error 2540
16
- (_c = Symbol['asyncDispose']) !== null && _c !== void 0 ? _c : (Symbol['asyncDispose'] = Symbol('Symbol.asyncDispose'));
17
- export {};
19
+ (_c = Symbol['asyncDispose']) !== null && _c !== void 0 ? _c : (Symbol['asyncDispose'] = (warn('Using a polyfill of Symbol.asyncDispose'), Symbol('Symbol.asyncDispose')));
20
+ /* node:coverage enable */
package/dist/vfs/async.js CHANGED
@@ -166,7 +166,7 @@ export function write(fd, data, cbPosOff, cbLenEnc, cbPosEnc, cb = nop) {
166
166
  default:
167
167
  // ...try to find the callback and get out of here!
168
168
  cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb);
169
- cb(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
169
+ cb(new ErrnoError(Errno.EINVAL, 'Invalid arguments'));
170
170
  return;
171
171
  }
172
172
  buffer = Buffer.from(data);
@@ -421,29 +421,43 @@ watch;
421
421
  * @returns A ReadStream object for interacting with the file's contents.
422
422
  */
423
423
  export function createReadStream(path, options) {
424
- const context = this;
424
+ var _a;
425
425
  options = typeof options == 'object' ? options : { encoding: options };
426
+ const _handle = promises.open.call(this, path, 'r', options === null || options === void 0 ? void 0 : options.mode);
427
+ void _handle.then(handle => {
428
+ if (typeof options.start == 'number')
429
+ handle.file.position = options.start;
430
+ });
426
431
  let handle;
427
432
  const stream = new ReadStream({
428
- highWaterMark: options.highWaterMark || 64 * 1024,
429
- encoding: options.encoding || 'utf8',
433
+ highWaterMark: options.highWaterMark || 0x1000,
434
+ encoding: (_a = options.encoding) !== null && _a !== void 0 ? _a : undefined,
430
435
  async read(size) {
436
+ var _a;
431
437
  try {
432
- handle || (handle = await promises.open.call(context, path, 'r', options === null || options === void 0 ? void 0 : options.mode));
433
- const result = await handle.read(new Uint8Array(size), 0, size, handle.file.position);
434
- stream.push(!result.bytesRead ? null : result.buffer.subarray(0, result.bytesRead));
435
- handle.file.position += result.bytesRead;
436
- if (!result.bytesRead) {
438
+ handle || (handle = await _handle);
439
+ const start = (_a = options.start) !== null && _a !== void 0 ? _a : handle.file.position;
440
+ if (typeof options.end === 'number' && start >= options.end) {
441
+ stream.push(null);
437
442
  await handle.close();
443
+ return;
438
444
  }
445
+ if (typeof options.end === 'number') {
446
+ size = Math.min(size, options.end - start);
447
+ }
448
+ const result = await handle.read(new Uint8Array(size), 0, size);
449
+ stream.push(!result.bytesRead ? null : result.buffer.subarray(0, result.bytesRead));
450
+ if (!result.bytesRead)
451
+ await handle.close();
439
452
  }
440
453
  catch (error) {
441
454
  await (handle === null || handle === void 0 ? void 0 : handle.close());
442
455
  stream.destroy(error);
443
456
  }
444
457
  },
445
- destroy(error, callback) {
446
- handle === null || handle === void 0 ? void 0 : handle.close().then(() => callback(error)).catch(nop);
458
+ async destroy(error, callback) {
459
+ await (handle === null || handle === void 0 ? void 0 : handle.close().catch(nop));
460
+ callback(error);
447
461
  },
448
462
  });
449
463
  stream.path = path.toString();
@@ -458,28 +472,37 @@ createReadStream;
458
472
  * @returns A WriteStream object for writing to the file.
459
473
  */
460
474
  export function createWriteStream(path, options) {
461
- const context = this;
462
475
  options = typeof options == 'object' ? options : { encoding: options };
476
+ const _handle = promises.open.call(this, path, 'w', options === null || options === void 0 ? void 0 : options.mode);
477
+ void _handle.then(handle => {
478
+ if (typeof options.start == 'number')
479
+ handle.file.position = options.start;
480
+ });
463
481
  let handle;
482
+ const { stack } = new Error();
464
483
  const stream = new WriteStream({
465
484
  highWaterMark: options === null || options === void 0 ? void 0 : options.highWaterMark,
466
485
  async write(chunk, encoding, callback) {
467
486
  try {
468
- handle || (handle = await promises.open.call(context, path, 'w', (options === null || options === void 0 ? void 0 : options.mode) || 0o666));
469
- await handle.write(chunk, 0, encoding);
470
- callback(undefined);
487
+ handle || (handle = await _handle);
488
+ const { bytesWritten } = await handle.write(chunk, null, encoding);
489
+ if (bytesWritten == chunk.length)
490
+ return callback();
491
+ throw new ErrnoError(Errno.EIO, `Failed to write full chunk of write stream (wrote ${bytesWritten}/${chunk.length} bytes)`, handle.file.path);
471
492
  }
472
493
  catch (error) {
473
494
  await (handle === null || handle === void 0 ? void 0 : handle.close());
495
+ error.stack += stack === null || stack === void 0 ? void 0 : stack.slice(5);
474
496
  callback(error);
475
497
  }
476
498
  },
477
- destroy(error, callback) {
499
+ async destroy(error, callback) {
500
+ await (handle === null || handle === void 0 ? void 0 : handle.close().catch(callback));
478
501
  callback(error);
479
- void (handle === null || handle === void 0 ? void 0 : handle.close().then(() => callback(error)).catch(callback));
480
502
  },
481
- final(callback) {
482
- void (handle === null || handle === void 0 ? void 0 : handle.close().then(() => callback()).catch(callback));
503
+ async final(callback) {
504
+ await (handle === null || handle === void 0 ? void 0 : handle.close().catch(nop));
505
+ callback();
483
506
  },
484
507
  });
485
508
  stream.path = path.toString();
@@ -1,7 +1,6 @@
1
1
  import type * as fs from 'node:fs';
2
2
  import type * as promises from 'node:fs/promises';
3
3
  import type { Stream } from 'node:stream';
4
- import type { ReadableStream as TReadableStream } from 'node:stream/web';
5
4
  import type { Interface as ReadlineInterface } from 'readline';
6
5
  import type { V_Context } from '../context.js';
7
6
  import type { File } from '../internal/file.js';
@@ -86,15 +85,10 @@ export declare class FileHandle implements promises.FileHandle {
86
85
  }): Promise<Buffer>;
87
86
  readFile(_options: (fs.ObjectEncodingOptions & promises.FlagAndOpenMode) | BufferEncoding): Promise<string>;
88
87
  /**
89
- * Returns a `ReadableStream` that may be used to read the files data.
90
- *
91
- * An error will be thrown if this method is called more than once or is called after the `FileHandle` is closed or closing.
92
- *
93
- * While the `ReadableStream` will read the file to completion,
94
- * it will not close the `FileHandle` automatically.
95
- * User code must still call the `fileHandle.close()` method.
88
+ * Read file data using a `ReadableStream`.
89
+ * The handle will not be closed automatically.
96
90
  */
97
- readableWebStream(options?: promises.ReadableWebStreamOptions): TReadableStream<Uint8Array>;
91
+ readableWebStream(options?: promises.ReadableWebStreamOptions): ReadableStream<Uint8Array>;
98
92
  /**
99
93
  * @todo Implement
100
94
  */
@@ -111,7 +105,7 @@ export declare class FileHandle implements promises.FileHandle {
111
105
  * Asynchronously writes `string` to the file.
112
106
  * The `FileHandle` must have been opened for writing.
113
107
  * It is unsafe to call `write()` multiple times on the same file without waiting for the `Promise`
114
- * to be resolved (or rejected). For this scenario, `fs.createWriteStream` is strongly recommended.
108
+ * to be resolved (or rejected). For this scenario, `createWriteStream` is strongly recommended.
115
109
  */
116
110
  write<T extends FileContents>(data: T, options?: number | null | {
117
111
  offset?: number;
@@ -296,9 +290,9 @@ export declare function lutimes(this: V_Context, path: fs.PathLike, atime: fs.Ti
296
290
  */
297
291
  export declare function realpath(this: V_Context, path: fs.PathLike, options: fs.BufferEncodingOption): Promise<Buffer>;
298
292
  export declare function realpath(this: V_Context, path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding): Promise<string>;
299
- export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIterableIterator<promises.FileChangeInfo<string>>;
300
- export declare function watch(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIterableIterator<promises.FileChangeInfo<Buffer>>;
301
- export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIterableIterator<promises.FileChangeInfo<string>> | AsyncIterableIterator<promises.FileChangeInfo<Buffer>>;
293
+ export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | BufferEncoding): AsyncIteratorObject<promises.FileChangeInfo<string>>;
294
+ export declare function watch(this: V_Context, filename: fs.PathLike, options: fs.WatchOptions | fs.BufferEncodingOption): AsyncIteratorObject<promises.FileChangeInfo<Buffer>>;
295
+ export declare function watch(this: V_Context, filename: fs.PathLike, options?: fs.WatchOptions | string): AsyncIteratorObject<promises.FileChangeInfo<string>> | AsyncIteratorObject<promises.FileChangeInfo<Buffer>>;
302
296
  export declare function access(this: V_Context, path: fs.PathLike, mode?: number): Promise<void>;
303
297
  /**
304
298
  * Asynchronous `rm`. Removes files or directories (recursively).