@zenfs/core 2.7.1 → 2.7.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.
package/README.md CHANGED
@@ -43,6 +43,9 @@ Your financial support would go a long way toward improving ZenFS and its commun
43
43
 
44
44
  ## Usage
45
45
 
46
+ > [!IMPORTANT]
47
+ > **[Check out the ZenFS docs!](https://zenfs.dev/start/usage/)**
48
+
46
49
  ```js
47
50
  import { fs } from '@zenfs/core'; // You can also use the default export
48
51
 
@@ -52,15 +55,9 @@ const contents = fs.readFileSync('/test.txt', 'utf-8');
52
55
  console.log(contents);
53
56
  ```
54
57
 
55
- #### Using different and/or multiple backends
56
-
57
- A single `InMemory` backend is created by default, mounted on `/`.
58
-
59
- You can configure ZenFS to use a different backend and mount multiple backends. It is strongly recommended to do so using the `configure` function.
60
-
61
- You can use multiple backends by passing an object to `configure` which maps paths to file systems.
62
-
63
- The following example mounts a zip file to `/zip`, in-memory storage to `/tmp`, and IndexedDB to `/home`. Note that `/` has the default in-memory backend.
58
+ A single `InMemory` backend is created by default, mounted on `/`. To use different backends, and to
59
+ mount more than one, configure ZenFS with `configure`. This mounts a zip file to `/mnt/zip`,
60
+ in-memory storage to `/tmp`, and IndexedDB to `/home`:
64
61
 
65
62
  ```js
66
63
  import { configure, InMemory } from '@zenfs/core';
@@ -78,110 +75,16 @@ await configure({
78
75
  });
79
76
  ```
80
77
 
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.
82
-
83
- > [!TIP]
84
- > When configuring a mount point, you can pass in
85
- >
86
- > 1. A `Backend` object, if the backend has no required options
87
- > 2. An object that has the options accepted by the backend and a `backend` property which is a `Backend` object
88
- > 3. A `FileSystem` instance
89
-
90
- Here is an example that mounts the `WebStorage` backend from `@zenfs/dom` on `/`:
91
-
92
- ```js
93
- import { configureSingle, fs } from '@zenfs/core';
94
- import { WebStorage } from '@zenfs/dom';
95
-
96
- await configureSingle({ backend: WebStorage });
97
-
98
- if (!fs.existsSync('/test.txt')) {
99
- fs.writeFileSync('/test.txt', 'This will persist across reloads!');
100
- }
101
-
102
- const contents = fs.readFileSync('/test.txt', 'utf-8');
103
- console.log(contents);
104
- ```
105
-
106
- #### FS Promises
107
-
108
- The FS promises API is exposed as `promises`.
109
-
110
- ```js
111
- import { configureSingle } from '@zenfs/core';
112
- import { exists, writeFile } from '@zenfs/core/promises';
113
- import { IndexedDB } from '@zenfs/dom';
114
-
115
- await configureSingle({ backend: IndexedDB });
116
-
117
- const exists = await exists('/myfile.txt');
118
- if (!exists) {
119
- await writeFile('/myfile.txt', 'Lots of persistent data');
120
- }
121
- ```
122
-
123
- > [!NOTE]
124
- > You can import the promises API using:
125
- >
126
- > 1. Exports from `@zenfs/core/promises`
127
- > 2. The `promises` export from `@zenfs/core`
128
- > 3. `fs.promises` on the exported `fs` from `@zenfs/core`.
78
+ The `fs/promises` API is available from `@zenfs/core/promises`, as the `promises` export, or as
79
+ `fs.promises`.
129
80
 
130
- #### Mounting and unmounting, creating backends
131
-
132
- If you would like to create backends without configure (e.g. to do something dynamic at runtime), you may do so by importing the backend and calling `resolveMountConfig` with it.
133
-
134
- You can then mount and unmount the backend instance by using `mount` and `umount`.
135
-
136
- ```js
137
- import { configure, resolveMountConfig, InMemory } from '@zenfs/core';
138
- import { IndexedDB } from '@zenfs/dom';
139
- import { Zip } from '@zenfs/archives';
140
-
141
- await configure({
142
- mounts: {
143
- '/tmp': InMemory,
144
- '/home': IndexedDB,
145
- },
146
- });
147
-
148
- fs.mkdirSync('/mnt/zip', { recursive: true });
149
-
150
- const res = await fetch('mydata.zip');
151
- const zipfs = await resolveMountConfig({ backend: Zip, data: await res.arrayBuffer() });
152
- fs.mount('/mnt/zip', zipfs);
153
-
154
- // do stuff with the mounted zip
155
-
156
- fs.umount('/mnt/zip'); // finished using the zip
157
- ```
158
-
159
- > [!CAUTION]
160
- > Instances of backends follow the _internal_ API. You should never use a backend's methods unless you are extending a backend.
161
-
162
- ### Devices and device files
163
-
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.
165
-
166
- ## `node:*` emulation
167
-
168
- ZenFS also includes emulation of some other `node:` modules for various reasons, importable from `@zenfs/core/<name>`:
169
-
170
- - `node:path`
171
- - `node:readline`
172
-
173
- For example:
174
-
175
- ```ts
176
- import * as path from '@zenfs/core/path';
177
- ```
81
+ For the full usage guide, see **[the documentation](https://zenfs.dev/start/usage/)**.
82
+ This includes mounting at runtime, contexts and permissions, devices, and the `node:*`module emulation.
178
83
 
179
84
  ## Bundling
180
85
 
181
86
  ZenFS exports a drop-in for Node's `fs` module, so you can use it for your bundler of preference using the default export.
182
-
183
- > [!IMPORTANT]
184
- > See [COPYING.md](./COPYING.md)
87
+ See [COPYING.md](./COPYING.md) for more info.
185
88
 
186
89
  ## Sponsors
187
90
 
package/dist/config.d.ts CHANGED
@@ -56,6 +56,8 @@ export interface Configuration<T extends ConfigMounts> extends SharedConfig {
56
56
  /**
57
57
  * Whether to automatically add normal Linux devices
58
58
  * @default false
59
+ * @deprecated Use `@zenfs/linux`!
60
+ * @todo [breaking] remove
59
61
  */
60
62
  addDevices: boolean;
61
63
  /**
@@ -97,6 +99,8 @@ export declare function configureSingle<T extends Backend>(configuration: MountC
97
99
  export declare function configureSingleSync<T extends Backend>(configuration: MountConfiguration<T>): void;
98
100
  /**
99
101
  * @category Backends and Configuration
102
+ * @deprecated Use `@zenfs/linux`!
103
+ * @todo [breaking] remove
100
104
  */
101
105
  export declare function addDevice(driver: DeviceDriver, options?: object): Device;
102
106
  /**
package/dist/config.js CHANGED
@@ -189,6 +189,8 @@ function mountWithMkdirSync(path, fs) {
189
189
  }
190
190
  /**
191
191
  * @category Backends and Configuration
192
+ * @deprecated Use `@zenfs/linux`!
193
+ * @todo [breaking] remove
192
194
  */
193
195
  export function addDevice(driver, options) {
194
196
  const devfs = defaultContext.mounts.get('/dev');
package/dist/context.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
2
  import { UV } from 'kerium';
3
3
  import { bindFunctions } from 'utilium';
4
- import { contextOf, createChildContext } from './internal/contexts.js';
4
+ import { contextOf, createChildContext, defaultContext } from './internal/contexts.js';
5
5
  import * as fs from './node/index.js';
6
6
  import * as path from './path.js';
7
7
  import * as xattr from './vfs/xattr.js';
@@ -17,9 +17,9 @@ export const boundContexts = new Map();
17
17
  * @category Contexts
18
18
  */
19
19
  export function bindContext(init = {}) {
20
- const $ = contextOf(this);
21
- if (!fs.statSync.call(this, $.root).isDirectory())
22
- throw UV('ENOTDIR', { syscall: 'chroot', path: $.root });
20
+ const $ = contextOf(this), parent = $.parent || defaultContext;
21
+ if (!fs.statSync.call(parent, parent.root).isDirectory())
22
+ throw UV('ENOTDIR', { syscall: 'chroot', path: parent.root });
23
23
  const ctx = createChildContext($, init);
24
24
  const bound = Object.assign(ctx, {
25
25
  fs: {
@@ -46,7 +46,7 @@ let _nextId = 2;
46
46
  */
47
47
  export function createChildContext(parent, init = {}) {
48
48
  assertContext(parent);
49
- const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials), mounts = Object.fromEntries(parent.mounts), } = init;
49
+ const { root = parent.root, pwd = parent.pwd, credentials = structuredClone(parent.credentials), mounts } = init;
50
50
  const ctx = {
51
51
  [kIsContext]: true,
52
52
  id: _nextId++,
@@ -56,7 +56,7 @@ export function createChildContext(parent, init = {}) {
56
56
  descriptors: new Map(),
57
57
  parent: parent,
58
58
  children: [],
59
- mounts: new Map(Object.entries(mounts)),
59
+ mounts: mounts ? new Map(Object.entries(mounts)) : parent.mounts,
60
60
  };
61
61
  Object.defineProperties(ctx, {
62
62
  id: { configurable: false, writable: false },
@@ -4,10 +4,11 @@ import type { CreationOptions } from './filesystem.js';
4
4
  import { Inode, type InodeLike } from './inode.js';
5
5
  /**
6
6
  * A device
7
- * @todo Maybe add some other device information, like a UUID?
8
7
  * @category Internals
9
8
  * @privateRemarks
10
9
  * UUIDs were considered, however they don't make sense without an easy mechanism for persistence
10
+ * @deprecated Use `@zenfs/linux`!
11
+ * @todo [breaking] remove
11
12
  */
12
13
  export interface Device<TData = any> {
13
14
  /**
@@ -34,6 +35,8 @@ export interface Device<TData = any> {
34
35
  }
35
36
  /**
36
37
  * @category Internals
38
+ * @deprecated Use `@zenfs/linux`!
39
+ * @todo [breaking] remove
37
40
  */
38
41
  export interface DeviceInit<TData = any> {
39
42
  data?: TData;
@@ -45,6 +48,8 @@ export interface DeviceInit<TData = any> {
45
48
  /**
46
49
  * A device driver
47
50
  * @category Internals
51
+ * @deprecated Use `@zenfs/linux`!
52
+ * @todo [breaking] remove
48
53
  */
49
54
  export interface DeviceDriver<TData = any> {
50
55
  /**
@@ -88,6 +93,8 @@ export interface DeviceDriver<TData = any> {
88
93
  /**
89
94
  * A temporary file system that manages and interfaces with devices
90
95
  * @category Internals
96
+ * @deprecated Use `@zenfs/linux`!
97
+ * @todo [breaking] remove
91
98
  */
92
99
  export declare class DeviceFS extends StoreFS<InMemoryStore> {
93
100
  protected readonly devices: Map<string, Device<any>>;
@@ -132,6 +139,8 @@ export declare class DeviceFS extends StoreFS<InMemoryStore> {
132
139
  * - Writes discard data, advancing the file position.
133
140
  * @category Internals
134
141
  * @internal
142
+ * @deprecated Use `@zenfs/linux`!
143
+ * @todo [breaking] remove
135
144
  */
136
145
  export declare const nullDevice: DeviceDriver;
137
146
  /**
@@ -144,6 +153,8 @@ export declare const nullDevice: DeviceDriver;
144
153
  * - Provides basic file metadata, treating it as a character device.
145
154
  * @category Internals
146
155
  * @internal
156
+ * @deprecated Use `@zenfs/linux`!
157
+ * @todo [breaking] remove
147
158
  */
148
159
  export declare const zeroDevice: DeviceDriver;
149
160
  /**
@@ -152,6 +163,8 @@ export declare const zeroDevice: DeviceDriver;
152
163
  * - Writes always fail with ENOSPC (no space left on device).
153
164
  * @category Internals
154
165
  * @internal
166
+ * @deprecated Use `@zenfs/linux`!
167
+ * @todo [breaking] remove
155
168
  */
156
169
  export declare const fullDevice: DeviceDriver;
157
170
  /**
@@ -160,12 +173,16 @@ export declare const fullDevice: DeviceDriver;
160
173
  * - Writes discard data, advancing the file position.
161
174
  * @category Internals
162
175
  * @internal
176
+ * @deprecated Use `@zenfs/linux`!
177
+ * @todo [breaking] remove
163
178
  */
164
179
  export declare const randomDevice: DeviceDriver;
165
180
  /**
166
181
  * Shortcuts for importing.
167
182
  * @category Internals
168
183
  * @internal
184
+ * @deprecated Use `@zenfs/linux`!
185
+ * @todo [breaking] remove
169
186
  */
170
187
  export declare const devices: {
171
188
  null: DeviceDriver<any>;
@@ -13,6 +13,8 @@ import { Inode } from './inode.js';
13
13
  /**
14
14
  * A temporary file system that manages and interfaces with devices
15
15
  * @category Internals
16
+ * @deprecated Use `@zenfs/linux`!
17
+ * @todo [breaking] remove
16
18
  */
17
19
  export class DeviceFS extends StoreFS {
18
20
  devices = new Map();
@@ -231,6 +233,8 @@ const emptyBuffer = new Uint8Array();
231
233
  * - Writes discard data, advancing the file position.
232
234
  * @category Internals
233
235
  * @internal
236
+ * @deprecated Use `@zenfs/linux`!
237
+ * @todo [breaking] remove
234
238
  */
235
239
  export const nullDevice = {
236
240
  name: 'null',
@@ -255,6 +259,8 @@ export const nullDevice = {
255
259
  * - Provides basic file metadata, treating it as a character device.
256
260
  * @category Internals
257
261
  * @internal
262
+ * @deprecated Use `@zenfs/linux`!
263
+ * @todo [breaking] remove
258
264
  */
259
265
  export const zeroDevice = {
260
266
  name: 'zero',
@@ -275,6 +281,8 @@ export const zeroDevice = {
275
281
  * - Writes always fail with ENOSPC (no space left on device).
276
282
  * @category Internals
277
283
  * @internal
284
+ * @deprecated Use `@zenfs/linux`!
285
+ * @todo [breaking] remove
278
286
  */
279
287
  export const fullDevice = {
280
288
  name: 'full',
@@ -295,6 +303,8 @@ export const fullDevice = {
295
303
  * - Writes discard data, advancing the file position.
296
304
  * @category Internals
297
305
  * @internal
306
+ * @deprecated Use `@zenfs/linux`!
307
+ * @todo [breaking] remove
298
308
  */
299
309
  export const randomDevice = {
300
310
  name: 'random',
@@ -315,6 +325,8 @@ export const randomDevice = {
315
325
  * Simulates the `/dev/console` device.
316
326
  * @category Internals
317
327
  * @experimental @internal
328
+ * @deprecated Use `@zenfs/linux`!
329
+ * @todo [breaking] remove
318
330
  */
319
331
  const consoleDevice = {
320
332
  name: 'console',
@@ -334,6 +346,8 @@ const consoleDevice = {
334
346
  * Shortcuts for importing.
335
347
  * @category Internals
336
348
  * @internal
349
+ * @deprecated Use `@zenfs/linux`!
350
+ * @todo [breaking] remove
337
351
  */
338
352
  export const devices = {
339
353
  null: nullDevice,
@@ -128,7 +128,7 @@ export function chroot(path) {
128
128
  throw UV('EBUSY', 'chroot', handle.path);
129
129
  handle.path = handle.path.slice($.root.length);
130
130
  }
131
- if (newRoot.length > $.root.length)
131
+ if (newRoot.length < $.root.length)
132
132
  throw withErrno('EPERM', 'Can not chroot() outside of current root');
133
133
  $.root = newRoot;
134
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "2.7.1",
3
+ "version": "2.7.2",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
- import { bindContext, configure, fs, InMemory } from '@zenfs/core';
2
+ import { bindContext, configure, fs, InMemory, type BoundContext } from '@zenfs/core';
3
3
  import assert from 'node:assert/strict';
4
4
  import { suite, test } from 'node:test';
5
5
  import { canary } from 'utilium';
@@ -124,4 +124,14 @@ suite('Context', () => {
124
124
  assert.equal(ctx.readFileSync('/copy.txt', 'utf-8'), 'not in real root!');
125
125
  assert(fs.readdirSync('/ctx').includes('copy.txt'));
126
126
  });
127
+
128
+ test('bindContext should resolve root relative to the parent context', () => {
129
+ fs.mkdirSync('/secure');
130
+ const ctx = bindContext({ root: '/secure', credentials: { uid: 333, gid: 333 } });
131
+
132
+ const plain = bindContext({ credentials: { uid: 1, gid: 1 } });
133
+ console.log('child of / context: ok, root =', plain.root);
134
+
135
+ assert.doesNotThrow(() => ctx.bind({ credentials: { uid: 500, gid: 500 } }));
136
+ });
127
137
  });