@zenfs/core 2.4.1 → 2.4.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.
Files changed (99) hide show
  1. package/COPYING.md +18 -0
  2. package/{readme.md → README.md} +16 -74
  3. package/dist/backends/fetch.js +1 -1
  4. package/dist/backends/memory.js +1 -1
  5. package/dist/backends/passthrough.d.ts +1 -2
  6. package/dist/backends/single_buffer.js +1 -1
  7. package/dist/backends/store/fs.js +4 -4
  8. package/dist/config.js +15 -15
  9. package/dist/context.js +3 -2
  10. package/dist/index.d.ts +9 -3
  11. package/dist/index.js +9 -4
  12. package/dist/internal/contexts.d.ts +5 -4
  13. package/dist/internal/devices.js +1 -1
  14. package/dist/internal/error.d.ts +11 -2
  15. package/dist/internal/error.js +38 -2
  16. package/dist/internal/file_index.js +1 -1
  17. package/dist/internal/index.d.ts +1 -0
  18. package/dist/internal/index.js +2 -1
  19. package/dist/internal/index_fs.js +1 -1
  20. package/dist/internal/inode.d.ts +51 -2
  21. package/dist/internal/inode.js +18 -2
  22. package/dist/mixins/shared.js +1 -1
  23. package/dist/node/async.d.ts +278 -0
  24. package/dist/node/async.js +518 -0
  25. package/dist/node/compat.d.ts +4 -0
  26. package/dist/node/compat.js +6 -0
  27. package/dist/node/dir.d.ts +78 -0
  28. package/dist/node/dir.js +150 -0
  29. package/dist/node/index.d.ts +8 -0
  30. package/dist/node/index.js +8 -0
  31. package/dist/{vfs → node}/promises.d.ts +10 -66
  32. package/dist/{vfs → node}/promises.js +141 -478
  33. package/dist/{vfs → node}/stats.d.ts +0 -4
  34. package/dist/{vfs → node}/stats.js +1 -16
  35. package/dist/{vfs → node}/streams.js +2 -2
  36. package/dist/node/sync.d.ts +252 -0
  37. package/dist/node/sync.js +682 -0
  38. package/dist/node/types.d.ts +21 -0
  39. package/dist/utils.d.ts +1 -7
  40. package/dist/utils.js +0 -6
  41. package/dist/vfs/acl.js +1 -1
  42. package/dist/vfs/async.d.ts +22 -278
  43. package/dist/vfs/async.js +212 -501
  44. package/dist/vfs/dir.d.ts +5 -82
  45. package/dist/vfs/dir.js +5 -233
  46. package/dist/vfs/file.d.ts +52 -13
  47. package/dist/vfs/file.js +167 -25
  48. package/dist/vfs/flags.js +1 -1
  49. package/dist/vfs/index.d.ts +2 -5
  50. package/dist/vfs/index.js +2 -5
  51. package/dist/vfs/shared.d.ts +25 -1
  52. package/dist/vfs/shared.js +6 -4
  53. package/dist/vfs/sync.d.ts +17 -245
  54. package/dist/vfs/sync.js +129 -773
  55. package/dist/vfs/watchers.d.ts +1 -1
  56. package/dist/vfs/watchers.js +2 -2
  57. package/dist/vfs/xattr.js +1 -1
  58. package/eslint.shared.js +1 -0
  59. package/package.json +7 -5
  60. package/scripts/make-index.js +5 -29
  61. package/scripts/test.js +59 -51
  62. package/tests/backend/fetch.test.ts +2 -2
  63. package/tests/backend/port.test.ts +2 -3
  64. package/tests/backend/single-buffer.test.ts +1 -1
  65. package/tests/common/casefold.test.ts +1 -1
  66. package/tests/common/context.test.ts +11 -4
  67. package/tests/common/devices.test.ts +3 -3
  68. package/tests/common/handle.test.ts +4 -3
  69. package/tests/common/inode.test.ts +2 -2
  70. package/tests/common/mounts.test.ts +1 -3
  71. package/tests/common/mutex.test.ts +1 -3
  72. package/tests/common/path.test.ts +2 -2
  73. package/tests/common/readline.test.ts +1 -1
  74. package/tests/common.ts +5 -4
  75. package/tests/fetch/fetch.ts +1 -1
  76. package/tests/fs/dir.test.ts +3 -43
  77. package/tests/fs/directory.test.ts +4 -4
  78. package/tests/fs/errors.test.ts +2 -2
  79. package/tests/fs/links.test.ts +1 -1
  80. package/tests/fs/permissions.test.ts +3 -3
  81. package/tests/fs/read.test.ts +1 -1
  82. package/tests/fs/scaling.test.ts +1 -1
  83. package/tests/fs/stat.test.ts +1 -2
  84. package/tests/fs/times.test.ts +1 -1
  85. package/tests/fs/watch.test.ts +3 -2
  86. package/tests/setup/context.ts +1 -2
  87. package/tests/setup/cow.ts +1 -1
  88. package/tests/setup/index.ts +2 -2
  89. package/tests/setup/port.ts +1 -1
  90. package/tests/setup/single-buffer.ts +1 -1
  91. package/tests/setup.ts +4 -3
  92. package/dist/vfs/types.d.ts +0 -24
  93. package/tests/assignment.ts +0 -21
  94. /package/dist/{vfs/constants.d.ts → constants.d.ts} +0 -0
  95. /package/dist/{vfs/constants.js → constants.js} +0 -0
  96. /package/dist/{readline.d.ts → node/readline.d.ts} +0 -0
  97. /package/dist/{readline.js → node/readline.js} +0 -0
  98. /package/dist/{vfs → node}/streams.d.ts +0 -0
  99. /package/dist/{vfs → node}/types.js +0 -0
package/COPYING.md ADDED
@@ -0,0 +1,18 @@
1
+ _This document is supplemental to the license._
2
+
3
+ **This is a very easy requirement, please respect it.**
4
+ Feel free to reach out if you have any concerns regarding licensing.
5
+
6
+ If you convey copies of ZenFS (including in bundles), you must meet section 4 and 5 of the license. This includes at a minimum:
7
+
8
+ - disclosure that ZenFS is in use
9
+ - linking to the GitHub or npm pages
10
+ - referencing the license and copyright.
11
+
12
+ For example, in a list of libraries you could have:
13
+
14
+ > - [ZenFS](https://github.com/zen-fs/core), LGPL-3.0-or-later, Copyright © James Prevett and other ZenFS contributors
15
+
16
+ <br />
17
+
18
+ Before v2.4.0, ZenFS was licensed under the MIT. This still means you need to include the copyright notice and license.
@@ -19,7 +19,12 @@ ZenFS supports a number of other backends.
19
19
  Many are provided as separate packages under `@zenfs`.
20
20
  More backends can be defined by separate libraries by extending the `FileSystem` class and providing a `Backend` object.
21
21
 
22
- You can find all of the packages available over on [NPM](https://www.npmjs.com/org/zenfs).
22
+ You can find all of the packages available over on [NPM](https://www.npmjs.com/org/zenfs). Below is a list of the backends included with some of them:
23
+
24
+ - @zenfs/archives: `Zip`, `Iso`
25
+ - @zenfs/cloud: `Dropbox`, `GoogleDrive`, `S3Bucket`
26
+ - @zenfs/dom: `WebAccess` (Web [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API)/OPFS), `IndexedDB`, `WebStorage` (`localStorage`/`sessionStorage`), `XML` (DOM elements)
27
+ - @zenfs/emscripten: `Emscripten` and a plugin for Emscripten's file system API
23
28
 
24
29
  As an added bonus, all ZenFS backends support synchronous operations.
25
30
  Additionally, all of the backends included with the core are cross-platform.
@@ -34,7 +39,7 @@ npm install @zenfs/core
34
39
 
35
40
  If you're using ZenFS, especially for big projects, please consider supporting the project.
36
41
  Thousands of hours have been dedicated to its development.
37
- Your acknowledgment or financial support would go a long way toward improving ZenFS and its community.
42
+ Your financial support would go a long way toward improving ZenFS and its community.
38
43
 
39
44
  ## Usage
40
45
 
@@ -69,8 +74,8 @@ await configure({
69
74
  '/mnt/zip': { backend: Zip, data: await res.arrayBuffer() },
70
75
  '/tmp': InMemory,
71
76
  '/home': IndexedDB,
72
- }
73
- };
77
+ },
78
+ });
74
79
  ```
75
80
 
76
81
  Note that while you aren't required to use absolute paths for the keys of `mounts`, it is a good practice to do so.
@@ -140,7 +145,7 @@ await configure({
140
145
  },
141
146
  });
142
147
 
143
- fs.mkdirSync('/mnt');
148
+ fs.mkdirSync('/mnt/zip', { recursive: true });
144
149
 
145
150
  const res = await fetch('mydata.zip');
146
151
  const zipfs = await resolveMountConfig({ backend: Zip, data: await res.arrayBuffer() });
@@ -156,82 +161,19 @@ fs.umount('/mnt/zip'); // finished using the zip
156
161
 
157
162
  ### Devices and device files
158
163
 
159
- ZenFS includes support for device files. These are designed to follow Linux's device file behavior, for consistency and ease of use. You can automatically add some normal devices with the `addDevices` configuration option:
160
-
161
- ```ts
162
- await configure({
163
- mounts: {
164
- /* ... */
165
- },
166
- addDevices: true,
167
- });
168
-
169
- fs.writeFileSync('/dev/null', 'Some data to be discarded');
170
-
171
- const randomData = new Uint8Array(100);
172
-
173
- const random = fs.openSync('/dev/random', 'r');
174
- fs.readSync(random, randomData);
175
- fs.closeSync(random);
176
- ```
177
-
178
- You can create your own devices by implementing a `DeviceDriver`. For example, the null device looks similar to this:
179
-
180
- ```ts
181
- const customNullDevice = {
182
- name: 'custom_null',
183
- // only 1 can exist per DeviceFS
184
- singleton: true,
185
- // optional if false
186
- isBuffered: false,
187
- read() {
188
- return 0;
189
- },
190
- write() {
191
- return 0;
192
- },
193
- };
194
- ```
195
-
196
- Note the actual implementation's write is slightly more complicated since it adds to the file position. You can find more information on the docs.
197
-
198
- Finally, if you'd like to use your custom device with the file system:
164
+ ZenFS includes support for device files. These are designed to follow Linux's device file behavior, for consistency and ease of use. Check out the [Devices and Device Drivers](https://zenfs.dev/core/documents/Devices_and_Device_Drivers) documentation for more information.
199
165
 
200
- ```ts
201
- import { addDevice, fs } from '@zenfs/core';
202
-
203
- addDevice(customNullDevice);
204
-
205
- fs.writeFileSync('/dev/custom', 'This gets discarded.');
206
- ```
166
+ ## Bundling
207
167
 
208
- ## Using with bundlers
168
+ ZenFS exports a drop-in for Node's `fs` module, so you can use it for your bundler of preference using the default export.
209
169
 
210
- ZenFS exports a drop-in for Node's `fs` module (up to the version of `@types/node` in package.json), so you can use it for your bundler of preference using the default export.
170
+ > [!IMPORTANT]
171
+ > See [COPYING.md](./COPYING.md)
211
172
 
212
173
  ## Sponsors
213
174
 
214
- A huge thank you to [![Deco.cx logo](https://avatars.githubusercontent.com/deco-cx?size=20) Deco.cx](https://github.com/deco-cx) for sponsoring ZenFS and helping to make this possible.
215
-
216
- ## Building
217
-
218
- - Make sure you have Node and NPM installed. You must have Node v22 or newer.
219
- - Install dependencies with `npm install`
220
- - Build using `npx tsc` or `npm run build`
221
- - You can find the built code in `dist`.
175
+ A huge thank you to [deco.cx](https://github.com/deco-cx) for sponsoring ZenFS and helping to make this possible.
222
176
 
223
177
  ## Contact and Support
224
178
 
225
179
  You can reach out [on Discord](https://zenfs.dev/discord) or by emailing jp@zenfs.dev
226
-
227
- ### Testing
228
-
229
- Run unit tests with:
230
-
231
- - `npm test` to run all tests using the default configuration
232
- - `npx zenfs-test -abc` to run the common tests and run the full FS suite against all included backends
233
- - You can also run this command to test your own backends, the `--auto` (`-a`) flag will automatically detect any setup scripts matching `tests/setup/*` or `tests/setup-*.ts`. If you do, you'll need to include the `c8` dependency for coverage.
234
-
235
- ### BrowserFS Fork
236
-
237
- ZenFS is a fork of [BrowserFS](https://github.com/jvilk/BrowserFS). If you are using ZenFS in a research paper, you may want to [cite BrowserFS](https://github.com/jvilk/BrowserFS#citing).
@@ -6,7 +6,7 @@ import * as requests from 'utilium/requests.js';
6
6
  import { Index } from '../internal/file_index.js';
7
7
  import { IndexFS } from '../internal/index_fs.js';
8
8
  import { normalizePath } from '../utils.js';
9
- import { S_IFREG } from '../vfs/constants.js';
9
+ import { S_IFREG } from '../constants.js';
10
10
  /** Parse and throw */
11
11
  function parseError(error) {
12
12
  if (!('tag' in error))
@@ -1,4 +1,4 @@
1
- import { size_max } from '../vfs/constants.js';
1
+ import { size_max } from '../constants.js';
2
2
  import { StoreFS } from './store/fs.js';
3
3
  import { SyncMapTransaction } from './store/map.js';
4
4
  /**
@@ -1,8 +1,7 @@
1
- import type * as fs from 'node:fs';
2
1
  import type { CreationOptions, UsageInfo } from '../internal/filesystem.js';
3
2
  import { FileSystem } from '../internal/filesystem.js';
4
3
  import { type InodeLike } from '../internal/inode.js';
5
- export type NodeFS = typeof fs;
4
+ import type { NodeFS } from '../node/types.js';
6
5
  /**
7
6
  * Passthrough backend options
8
7
  * @category Backends and Configuration
@@ -87,7 +87,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
87
87
  // SPDX-License-Identifier: LGPL-3.0-or-later
88
88
  import { withErrno } from 'kerium';
89
89
  import { alert, crit, err, warn } from 'kerium/log';
90
- import { array, offsetof, packed, sizeof } from 'memium';
90
+ import { array, offsetof, sizeof } from 'memium';
91
91
  import { $from, field, struct, types as t } from 'memium/decorators';
92
92
  import { BufferView } from 'utilium/buffer.js';
93
93
  import { crc32c } from 'utilium/checksum.js';
@@ -58,10 +58,10 @@ import { _throw, canary, encodeUTF8 } from 'utilium';
58
58
  import { extendBuffer } from 'utilium/buffer.js';
59
59
  import { Index } from '../../internal/file_index.js';
60
60
  import { FileSystem } from '../../internal/filesystem.js';
61
- import { Inode, isDirectory, rootIno } from '../../internal/inode.js';
61
+ import { Inode, isDirectory, isFile, rootIno } from '../../internal/inode.js';
62
62
  import { basename, dirname, join, parse, relative } from '../../path.js';
63
63
  import { decodeDirListing, encodeDirListing } from '../../utils.js';
64
- import { S_IFDIR, S_IFREG, size_max } from '../../vfs/constants.js';
64
+ import { S_IFDIR, S_IFREG, size_max } from '../../constants.js';
65
65
  import { WrappedTransaction } from './store.js';
66
66
  /**
67
67
  * A file system which uses a `Store`
@@ -304,7 +304,7 @@ export class StoreFS extends FileSystem {
304
304
  : decodeDirListing((await tx.get(newDirNode.data)) ?? _throw(withErrno('ENODATA')));
305
305
  if (newDirList[_new.base]) {
306
306
  const existing = new Inode((await tx.get(newDirList[_new.base])) ?? _throw(withErrno('ENOENT')));
307
- if (!existing.toStats().isFile())
307
+ if (!isFile(existing))
308
308
  throw withErrno('EISDIR');
309
309
  await tx.remove(existing.data);
310
310
  await tx.remove(newDirList[_new.base]);
@@ -353,7 +353,7 @@ export class StoreFS extends FileSystem {
353
353
  const newDirList = sameParent ? oldDirList : decodeDirListing(tx.getSync(newDirNode.data) ?? _throw(withErrno('ENODATA')));
354
354
  if (newDirList[_new.base]) {
355
355
  const existing = new Inode(tx.getSync(newDirList[_new.base]) ?? _throw(withErrno('ENOENT')));
356
- if (!existing.toStats().isFile())
356
+ if (!isFile(existing))
357
357
  throw withErrno('EISDIR');
358
358
  tx.removeSync(existing.data);
359
359
  tx.removeSync(newDirList[_new.base]);
package/dist/config.js CHANGED
@@ -4,9 +4,9 @@ import { defaultContext } from './internal/contexts.js';
4
4
  import { createCredentials } from './internal/credentials.js';
5
5
  import { DeviceFS } from './internal/devices.js';
6
6
  import { FileSystem } from './internal/filesystem.js';
7
+ import { exists, mkdir, stat } from './node/promises.js';
7
8
  import { _setAccessChecks } from './vfs/config.js';
8
- import * as fs from './vfs/index.js';
9
- import { mounts } from './vfs/shared.js';
9
+ import { mount, mounts, umount } from './vfs/shared.js';
10
10
  /**
11
11
  * Update the configuration of a file system.
12
12
  * @category Backends and Configuration
@@ -69,8 +69,8 @@ export async function configureSingle(configuration) {
69
69
  throw new TypeError('Invalid single mount point configuration');
70
70
  }
71
71
  const resolved = await resolveMountConfig(configuration);
72
- fs.umount('/');
73
- fs.mount('/', resolved);
72
+ umount('/');
73
+ mount('/', resolved);
74
74
  }
75
75
  /**
76
76
  * Like `fs.mount`, but it also creates missing directories.
@@ -78,19 +78,19 @@ export async function configureSingle(configuration) {
78
78
  * This is implemented as a separate function to avoid a circular dependency between vfs/shared.ts and other vfs layer files.
79
79
  * @internal
80
80
  */
81
- async function mount(path, mount) {
81
+ async function mountWithMkdir(path, fs) {
82
82
  if (path == '/') {
83
- fs.mount(path, mount);
83
+ mount(path, fs);
84
84
  return;
85
85
  }
86
- const stats = await fs.promises.stat(path).catch(() => null);
86
+ const stats = await stat(path).catch(() => null);
87
87
  if (!stats) {
88
- await fs.promises.mkdir(path, { recursive: true });
88
+ await mkdir(path, { recursive: true });
89
89
  }
90
90
  else if (!stats.isDirectory()) {
91
91
  throw withErrno('ENOTDIR', 'Missing directory at mount point: ' + path);
92
92
  }
93
- fs.mount(path, mount);
93
+ mount(path, fs);
94
94
  }
95
95
  /**
96
96
  * @category Backends and Configuration
@@ -124,8 +124,8 @@ export async function configure(configuration) {
124
124
  mountConfig.caseFold ??= configuration.caseFold;
125
125
  }
126
126
  if (point == '/')
127
- fs.umount('/');
128
- await mount(point, await resolveMountConfig(mountConfig));
127
+ umount('/');
128
+ await mountWithMkdir(point, await resolveMountConfig(mountConfig));
129
129
  }
130
130
  }
131
131
  for (const fs of mounts.values()) {
@@ -135,17 +135,17 @@ export async function configure(configuration) {
135
135
  const devfs = new DeviceFS();
136
136
  devfs.addDefaults();
137
137
  await devfs.ready();
138
- await mount('/dev', devfs);
138
+ await mountWithMkdir('/dev', devfs);
139
139
  }
140
140
  if (configuration.defaultDirectories) {
141
141
  for (const dir of _defaultDirectories) {
142
- if (await fs.promises.exists(dir)) {
143
- const stats = await fs.promises.stat(dir);
142
+ if (await exists(dir)) {
143
+ const stats = await stat(dir);
144
144
  if (!stats.isDirectory())
145
145
  log.warn('Default directory exists but is not a directory: ' + dir);
146
146
  }
147
147
  else
148
- await fs.promises.mkdir(dir);
148
+ await mkdir(dir);
149
149
  }
150
150
  }
151
151
  }
package/dist/context.js CHANGED
@@ -3,7 +3,8 @@ import { bindFunctions } from 'utilium';
3
3
  import { defaultContext } from './internal/contexts.js';
4
4
  import { createCredentials } from './internal/credentials.js';
5
5
  import * as path from './path.js';
6
- import * as fs from './vfs/index.js';
6
+ import * as fs from './node/index.js';
7
+ import * as xattr from './vfs/xattr.js';
7
8
  // 0 is reserved for the global/default context
8
9
  let _nextId = 1;
9
10
  /**
@@ -33,7 +34,7 @@ export function bindContext({ root = this?.root || '/', pwd = this?.pwd || '/',
33
34
  fs: {
34
35
  ...bindFunctions(fs, ctx),
35
36
  promises: bindFunctions(fs.promises, ctx),
36
- xattr: bindFunctions(fs.xattr, ctx),
37
+ xattr: bindFunctions(xattr, ctx),
37
38
  },
38
39
  path: bindFunctions(path, ctx),
39
40
  bind: (init) => {
package/dist/index.d.ts CHANGED
@@ -1,12 +1,18 @@
1
+ /*!
2
+ * @zenfs/core — https://npmjs.com/package/@zenfs/core
3
+ * Copyright © James Prevett and other ZenFS contributors.
4
+ * SPDX-License-Identifier: LGPL-3.0-or-later
5
+ */
1
6
  export * from './backends/index.js';
2
7
  export * from './config.js';
3
8
  export * from './context.js';
4
9
  export * from './internal/index.js';
5
10
  export * from './mixins/index.js';
6
- export * from './vfs/stats.js';
7
11
  export * from './utils.js';
8
12
  export { mounts } from './vfs/shared.js';
9
- export * from './vfs/index.js';
13
+ import * as fs from './node/compat.js';
14
+ /** @primaryExport */
10
15
  export { fs };
11
- import * as fs from './vfs/index.js';
12
16
  export default fs;
17
+ export * from './node/compat.js';
18
+ export * as vfs from './vfs/index.js';
package/dist/index.js CHANGED
@@ -1,15 +1,20 @@
1
- // SPDX-License-Identifier: LGPL-3.0-or-later
1
+ /*!
2
+ * @zenfs/core — https://npmjs.com/package/@zenfs/core
3
+ * Copyright © James Prevett and other ZenFS contributors.
4
+ * SPDX-License-Identifier: LGPL-3.0-or-later
5
+ */
2
6
  export * from './backends/index.js';
3
7
  export * from './config.js';
4
8
  export * from './context.js';
5
9
  export * from './internal/index.js';
6
10
  export * from './mixins/index.js';
7
- export * from './vfs/stats.js';
8
11
  export * from './utils.js';
9
12
  export { mounts } from './vfs/shared.js';
10
- export * from './vfs/index.js';
13
+ import * as fs from './node/compat.js';
14
+ /** @primaryExport */
11
15
  export { fs };
12
- import * as fs from './vfs/index.js';
13
16
  export default fs;
17
+ export * from './node/compat.js';
18
+ export * as vfs from './vfs/index.js';
14
19
  import $pkg from '../package.json' with { type: 'json' };
15
20
  globalThis.__zenfs__ = Object.assign(Object.create(fs), { _version: $pkg.version });
@@ -1,7 +1,8 @@
1
1
  import type { Bound } from 'utilium';
2
+ import type * as fs from '../node/index.js';
2
3
  import type * as path from '../path.js';
3
- import type { SyncHandle } from '../vfs/file.js';
4
- import type * as fs from '../vfs/index.js';
4
+ import type { Handle } from '../vfs/file.js';
5
+ import type * as xattr from '../vfs/xattr.js';
5
6
  import type { Credentials, CredentialsInit } from './credentials.js';
6
7
  /**
7
8
  * A context used for FS operations
@@ -21,7 +22,7 @@ export interface FSContext {
21
22
  /** The credentials of the context, used for access checks */
22
23
  readonly credentials: Credentials;
23
24
  /** A map of open file descriptors to their handles */
24
- descriptors: Map<number, SyncHandle>;
25
+ descriptors: Map<number, Handle>;
25
26
  /** The parent context, if any. */
26
27
  parent: V_Context;
27
28
  /** The child contexts */
@@ -38,7 +39,7 @@ export type V_Context = void | null | (Partial<FSContext> & object);
38
39
  export interface BoundContext extends FSContext {
39
40
  fs: Bound<typeof fs, FSContext> & {
40
41
  promises: Bound<typeof fs.promises, FSContext>;
41
- xattr: Bound<typeof fs.xattr, FSContext>;
42
+ xattr: Bound<typeof xattr, FSContext>;
42
43
  };
43
44
  /** Path functions, bound to the context */
44
45
  path: Bound<typeof path, FSContext>;
@@ -8,7 +8,7 @@ import { decodeUTF8, omit } from 'utilium';
8
8
  import { InMemoryStore } from '../backends/memory.js';
9
9
  import { StoreFS } from '../backends/store/fs.js';
10
10
  import { basename, dirname } from '../path.js';
11
- import { S_IFCHR } from '../vfs/constants.js';
11
+ import { S_IFCHR } from '../constants.js';
12
12
  import { Inode } from './inode.js';
13
13
  /**
14
14
  * A temporary file system that manages and interfaces with devices
@@ -1,4 +1,5 @@
1
- import { Exception, type ExceptionJSON } from 'kerium';
1
+ import { Exception, type ExceptionExtra, type ExceptionJSON } from 'kerium';
2
+ import type { FileSystem } from './filesystem.js';
2
3
  /**
3
4
  * @deprecated Use {@link ExceptionJSON} instead
4
5
  * @category Internals
@@ -15,4 +16,12 @@ export declare const ErrnoError: typeof Exception;
15
16
  */
16
17
  export type ErrnoError = Exception;
17
18
  export declare function withPath<E extends Exception>(e: E, path: string): E;
18
- export declare function wrap<const FS, const Prop extends keyof FS & string>(fs: FS, prop: Prop, path: string, dest?: string): FS[Prop];
19
+ /**
20
+ * @internal @hidden
21
+ */
22
+ export declare function wrap<const FS, const Prop extends keyof FS & string>(fs: FS, prop: Prop, path: string | ExceptionExtra, dest?: string): FS[Prop];
23
+ /**
24
+ * @internal
25
+ * Wraps an `fs` so that thrown errors aren't empty
26
+ */
27
+ export declare function withExceptionContext<const FS extends FileSystem>(fs: FS, context: ExceptionExtra): FS;
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
- import { Exception, setUVMessage } from 'kerium';
2
+ import { Errno, Exception, setUVMessage, UV } from 'kerium';
3
3
  /**
4
4
  * @deprecated Use {@link Exception} instead
5
5
  * @category Internals
@@ -9,7 +9,11 @@ export function withPath(e, path) {
9
9
  e.path = path;
10
10
  return e;
11
11
  }
12
+ /**
13
+ * @internal @hidden
14
+ */
12
15
  export function wrap(fs, prop, path, dest) {
16
+ const extra = typeof path === 'string' ? { path, dest, syscall: prop.endsWith('Sync') ? prop.slice(0, -4) : prop } : path;
13
17
  const fn = fs[prop];
14
18
  if (typeof fn !== 'function')
15
19
  throw new TypeError(`${prop} is not a function`);
@@ -18,7 +22,39 @@ export function wrap(fs, prop, path, dest) {
18
22
  return fn.call(fs, ...args);
19
23
  }
20
24
  catch (e) {
21
- throw setUVMessage(Object.assign(e, { path, dest, syscall: prop.endsWith('Sync') ? prop.slice(0, -4) : prop }));
25
+ throw setUVMessage(Object.assign(e, extra));
22
26
  }
23
27
  };
24
28
  }
29
+ /**
30
+ * @internal
31
+ * Wraps an `fs` so that thrown errors aren't empty
32
+ */
33
+ export function withExceptionContext(fs, context) {
34
+ return new Proxy(fs, {
35
+ get(target, prop) {
36
+ const value = Reflect.get(target, prop);
37
+ if (typeof value != 'function')
38
+ return value;
39
+ return function __withContext(...args) {
40
+ try {
41
+ const result = value.apply(target, args);
42
+ if (!(result instanceof Promise))
43
+ return result;
44
+ return result.catch((e) => {
45
+ if ('code' in e)
46
+ throw setUVMessage(Object.assign(e, context));
47
+ if (e in Errno) {
48
+ const ex = UV(e, context);
49
+ Error.captureStackTrace(ex, __withContext);
50
+ }
51
+ throw e;
52
+ });
53
+ }
54
+ catch (e) {
55
+ throw setUVMessage(Object.assign(e, context));
56
+ }
57
+ };
58
+ },
59
+ });
60
+ }
@@ -4,7 +4,7 @@ import { withErrno } from 'kerium';
4
4
  import { sizeof } from 'memium';
5
5
  import { isJSON, randomInt } from 'utilium';
6
6
  import { basename, dirname } from '../path.js';
7
- import { S_IFDIR, S_IFMT, size_max } from '../vfs/constants.js';
7
+ import { S_IFDIR, S_IFMT, size_max } from '../constants.js';
8
8
  import { Inode } from './inode.js';
9
9
  export const version = 1;
10
10
  /**
@@ -1,4 +1,5 @@
1
1
  export { log } from 'kerium';
2
+ export * from './contexts.js';
2
3
  export * from './credentials.js';
3
4
  export * from './devices.js';
4
5
  export * from './error.js';
@@ -1,5 +1,6 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
- export { log } from 'kerium'; // DO NOT USE
2
+ export { log } from 'kerium'; // DO NOT USE @todo [BREAKING] Remove this
3
+ export * from './contexts.js';
3
4
  export * from './credentials.js';
4
5
  export * from './devices.js';
5
6
  export * from './error.js';
@@ -3,7 +3,7 @@
3
3
  import { withErrno } from 'kerium';
4
4
  import { _throw } from 'utilium';
5
5
  import { dirname, join, relative } from '../path.js';
6
- import { S_IFDIR, S_IFMT, S_IFREG, S_ISGID, S_ISUID } from '../vfs/constants.js';
6
+ import { S_IFDIR, S_IFMT, S_IFREG, S_ISGID, S_ISUID } from '../constants.js';
7
7
  import { Index } from './file_index.js';
8
8
  import { FileSystem } from './filesystem.js';
9
9
  import { Inode } from './inode.js';
@@ -1,5 +1,5 @@
1
1
  import { BufferView } from 'utilium/buffer.js';
2
- import { Stats, type StatsLike } from '../vfs/stats.js';
2
+ import { Stats } from '../node/stats.js';
3
3
  import { type V_Context } from './contexts.js';
4
4
  /**
5
5
  * Root inode
@@ -36,7 +36,51 @@ export interface InodeFields {
36
36
  * @category Internals
37
37
  * @internal
38
38
  */
39
- export interface InodeLike extends StatsLike<number>, InodeFields {
39
+ export interface InodeLike<T extends number | bigint = number> extends InodeFields {
40
+ /**
41
+ * Size of the item in bytes.
42
+ * For directories/symlinks, this is normally the size of the struct that represents the item.
43
+ */
44
+ size: T;
45
+ /**
46
+ * Unix-style file mode (e.g. 0o644) that includes the item type
47
+ */
48
+ mode: T;
49
+ /**
50
+ * Time of last access, since epoch
51
+ */
52
+ atimeMs: T;
53
+ /**
54
+ * Time of last modification, since epoch
55
+ */
56
+ mtimeMs: T;
57
+ /**
58
+ * Time of last time file status was changed, since epoch
59
+ */
60
+ ctimeMs: T;
61
+ /**
62
+ * Time of file creation, since epoch
63
+ */
64
+ birthtimeMs: T;
65
+ /**
66
+ * The id of the user that owns the file
67
+ */
68
+ uid: T;
69
+ /**
70
+ * The id of the group that owns the file
71
+ */
72
+ gid: T;
73
+ /**
74
+ * Inode number
75
+ */
76
+ ino: T;
77
+ /**
78
+ * Number of hard links
79
+ */
80
+ nlink: T;
81
+ /**
82
+ * Extended attributes
83
+ */
40
84
  attributes?: Attributes;
41
85
  }
42
86
  /**
@@ -153,6 +197,7 @@ export declare class Inode extends Inode_base implements InodeLike {
153
197
  toJSON(): InodeLike;
154
198
  /**
155
199
  * Handy function that converts the Inode to a Node Stats object.
200
+ * @deprecated Use `new Stats(inode)` instead.
156
201
  */
157
202
  toStats(): Stats;
158
203
  /**
@@ -194,4 +239,8 @@ export declare function isFIFO(metadata: {
194
239
  * @internal
195
240
  */
196
241
  export declare function hasAccess($: V_Context, inode: Pick<InodeLike, 'mode' | 'uid' | 'gid'>, access: number): boolean;
242
+ /**
243
+ * @hidden @internal
244
+ */
245
+ export declare function _chown(stats: Partial<InodeLike>, uid: number, gid: number): boolean;
197
246
  export {};
@@ -39,8 +39,8 @@ import { sizeof } from 'memium';
39
39
  import { $from, field, struct, types as t } from 'memium/decorators';
40
40
  import { decodeUTF8, encodeUTF8, pick } from 'utilium';
41
41
  import { BufferView } from 'utilium/buffer.js';
42
- import * as c from '../vfs/constants.js';
43
- import { Stats } from '../vfs/stats.js';
42
+ import { Stats } from '../node/stats.js';
43
+ import * as c from '../constants.js';
44
44
  import { defaultContext } from './contexts.js';
45
45
  /**
46
46
  * Root inode
@@ -549,6 +549,7 @@ let Inode = (() => {
549
549
  }
550
550
  /**
551
551
  * Handy function that converts the Inode to a Node Stats object.
552
+ * @deprecated Use `new Stats(inode)` instead.
552
553
  */
553
554
  toStats() {
554
555
  return new Stats(this);
@@ -647,3 +648,18 @@ export function hasAccess($, inode, access) {
647
648
  perm |= c.X_OK;
648
649
  return (perm & access) === access;
649
650
  }
651
+ /**
652
+ * @hidden @internal
653
+ */
654
+ export function _chown(stats, uid, gid) {
655
+ let valid = true;
656
+ if (!isNaN(uid) && uid >= 0 && uid < c.size_max)
657
+ stats.uid = uid;
658
+ else
659
+ valid = false;
660
+ if (!isNaN(gid) && gid >= 0 && gid < c.size_max)
661
+ stats.gid = gid;
662
+ else
663
+ valid = false;
664
+ return valid;
665
+ }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
- /* eslint-disable @typescript-eslint/no-empty-object-type,@typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
3
  /*
4
4
  Code shared by various mixins
5
5
  */