@zenfs/core 0.5.11 → 0.6.0

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/config.d.ts CHANGED
@@ -1,23 +1,27 @@
1
- import type { Backend, BackendConfig } from './backends/backend.js';
1
+ import type { Backend, BackendConfiguration } from './backends/backend.js';
2
2
  import { FileSystem } from './filesystem.js';
3
3
  /**
4
- * Initializes ZenFS with the given file systems.
4
+ * Configuration for a specific mount point
5
5
  */
6
- export declare function initialize(mounts: {
7
- [point: string]: FileSystem;
8
- }, uid?: number, gid?: number): void;
6
+ export type MountConfiguration<FS extends FileSystem = FileSystem> = FS | BackendConfiguration<FS> | Backend<FS>;
9
7
  /**
10
- * Defines a mapping of mount points to their configurations
8
+ * Retrieve a file system with the given configuration.
9
+ * @param config A BackendConfig object.
11
10
  */
12
- export interface ConfigMapping {
13
- [mountPoint: string]: FileSystem | BackendConfig | Backend;
14
- }
11
+ export declare function resolveMountConfig<FS extends FileSystem>(config: MountConfiguration<FS>, _depth?: number): Promise<FS>;
15
12
  /**
16
- * A configuration for ZenFS
13
+ *A mapping of mount points to their configurations
17
14
  */
18
- export type Configuration = FileSystem | BackendConfig | ConfigMapping;
15
+ export type MappingConfiguration = Partial<{
16
+ uid: number;
17
+ gid: number;
18
+ }> & Record<string, FileSystem | BackendConfiguration | Backend>;
19
+ /**
20
+ * Configuration for the file systems
21
+ */
22
+ export type Configuration = MountConfiguration | MappingConfiguration;
19
23
  /**
20
24
  * Creates filesystems with the given configuration, and initializes ZenFS with it.
21
- * See the Configuration type for more info on the configuration object.
25
+ * @see Configuration for more info on the configuration object.
22
26
  */
23
27
  export declare function configure(config: Configuration): Promise<void>;
package/dist/config.js CHANGED
@@ -1,35 +1,66 @@
1
- import { isBackend, resolveBackend } from './backends/backend.js';
1
+ import { ApiError, ErrorCode } from './ApiError.js';
2
+ import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
2
3
  import * as fs from './emulation/index.js';
3
4
  import { setCred } from './emulation/shared.js';
4
5
  import { FileSystem } from './filesystem.js';
6
+ function isMountConfig(arg) {
7
+ return isBackendConfig(arg) || isBackend(arg) || arg instanceof FileSystem;
8
+ }
5
9
  /**
6
- * Initializes ZenFS with the given file systems.
10
+ * Retrieve a file system with the given configuration.
11
+ * @param config A BackendConfig object.
7
12
  */
8
- export function initialize(mounts, uid = 0, gid = 0) {
9
- setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
10
- fs.initialize(mounts);
13
+ export async function resolveMountConfig(config, _depth = 0) {
14
+ if (typeof config !== 'object' || config == null) {
15
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid options on mount configuration');
16
+ }
17
+ if (!isMountConfig(config)) {
18
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid mount configuration');
19
+ }
20
+ if (config instanceof FileSystem) {
21
+ return config;
22
+ }
23
+ if (isBackend(config)) {
24
+ config = { backend: config };
25
+ }
26
+ for (const [key, value] of Object.entries(config)) {
27
+ if (key == 'backend') {
28
+ continue;
29
+ }
30
+ if (!isMountConfig(value)) {
31
+ continue;
32
+ }
33
+ if (_depth > 10) {
34
+ throw new ApiError(ErrorCode.EINVAL, 'Invalid configuration, too deep and possibly infinite');
35
+ }
36
+ config[key] = await resolveMountConfig(value, ++_depth);
37
+ }
38
+ const { backend } = config;
39
+ if (!(await backend.isAvailable())) {
40
+ throw new ApiError(ErrorCode.EPERM, 'Backend not available: ' + backend);
41
+ }
42
+ checkOptions(backend, config);
43
+ const mount = backend.create(config);
44
+ await mount.ready();
45
+ return mount;
11
46
  }
12
47
  /**
13
48
  * Creates filesystems with the given configuration, and initializes ZenFS with it.
14
- * See the Configuration type for more info on the configuration object.
49
+ * @see Configuration for more info on the configuration object.
15
50
  */
16
51
  export async function configure(config) {
17
- if ('backend' in config || config instanceof FileSystem) {
52
+ const uid = 'uid' in config ? +config.uid || 0 : 0;
53
+ const gid = 'gid' in config ? +config.gid || 0 : 0;
54
+ if (isMountConfig(config)) {
18
55
  // single FS
19
56
  config = { '/': config };
20
57
  }
21
- for (let [point, value] of Object.entries(config)) {
22
- if (typeof value == 'number') {
23
- //should never happen
58
+ for (const [point, value] of Object.entries(config)) {
59
+ if (point == 'uid' || point == 'gid' || typeof value == 'number') {
24
60
  continue;
25
61
  }
26
- if (value instanceof FileSystem) {
27
- continue;
28
- }
29
- if (isBackend(value)) {
30
- value = { backend: value };
31
- }
32
- config[point] = await resolveBackend(value);
62
+ config[point] = await resolveMountConfig(value);
33
63
  }
34
- initialize(config);
64
+ fs.mountMapping(config);
65
+ setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
35
66
  }
@@ -4,5 +4,5 @@ export * as promises from './promises.js';
4
4
  export * as constants from './constants.js';
5
5
  export * from './streams.js';
6
6
  export * from './dir.js';
7
- export { initialize, mounts, mount, umount, _toUnixTimestamp } from './shared.js';
7
+ export { mountMapping, mounts, mount, umount, _toUnixTimestamp } from './shared.js';
8
8
  export { Stats, BigIntStats, StatsFs } from '../stats.js';
@@ -4,5 +4,5 @@ export * as promises from './promises.js';
4
4
  export * as constants from './constants.js';
5
5
  export * from './streams.js';
6
6
  export * from './dir.js';
7
- export { initialize, mounts, mount, umount, _toUnixTimestamp } from './shared.js';
7
+ export { mountMapping, mounts, mount, umount, _toUnixTimestamp } from './shared.js';
8
8
  export { Stats, BigIntStats, StatsFs } from '../stats.js';
@@ -108,26 +108,26 @@ export function formatExt(ext) {
108
108
  return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : '';
109
109
  }
110
110
  export function resolve(...args) {
111
- let resolvedPath = '';
112
- let resolvedAbsolute = false;
113
- for (let i = args.length - 1; i >= -1 && !resolvedAbsolute; i--) {
111
+ let resolved = '';
112
+ let absolute = false;
113
+ for (let i = args.length - 1; i >= -1 && !absolute; i--) {
114
114
  const path = i >= 0 ? args[i] : cwd;
115
115
  validateString(path, `paths[${i}]`);
116
116
  // Skip empty entries
117
- if (path.length === 0) {
117
+ if (!path.length) {
118
118
  continue;
119
119
  }
120
- resolvedPath = `${path}/${resolvedPath}`;
121
- resolvedAbsolute = path[0] === '/';
120
+ resolved = `${path}/${resolved}`;
121
+ absolute = path[0] == '/';
122
122
  }
123
123
  // At this point the path should be resolved to a full absolute path, but
124
- // handle relative paths to be safe (might happen when process.cwd() fails)
124
+ // handle relative paths to be safe (might happen when cwd fails)
125
125
  // Normalize the path
126
- resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
127
- if (resolvedAbsolute) {
128
- return `/${resolvedPath}`;
126
+ resolved = normalizeString(resolved, !absolute);
127
+ if (absolute) {
128
+ return `/${resolved}`;
129
129
  }
130
- return resolvedPath.length > 0 ? resolvedPath : '.';
130
+ return resolved.length > 0 ? resolved : '/';
131
131
  }
132
132
  export function normalize(path) {
133
133
  validateString(path, 'path');
@@ -8,13 +8,14 @@ import { Buffer } from 'buffer';
8
8
  import type * as Node from 'node:fs';
9
9
  import type * as promises from 'node:fs/promises';
10
10
  import type { CreateReadStreamOptions, CreateWriteStreamOptions, FileChangeInfo, FileReadResult, FlagAndOpenMode } from 'node:fs/promises';
11
+ import type { ReadableStream } from 'node:stream/web';
12
+ import type { Interface as ReadlineInterface } from 'readline';
11
13
  import { FileContents } from '../filesystem.js';
12
14
  import { BigIntStats, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
13
15
  import { Dirent, type Dir } from './dir.js';
14
16
  import type { PathLike } from './shared.js';
17
+ import { ReadStream, WriteStream } from './streams.js';
15
18
  export * as constants from './constants.js';
16
- import type { ReadableStream } from 'node:stream/web';
17
- import type { Interface as ReadlineInterface } from 'readline';
18
19
  export declare class FileHandle implements promises.FileHandle {
19
20
  /**
20
21
  * Gets the file descriptor for this file handle.
@@ -161,8 +162,8 @@ export declare class FileHandle implements promises.FileHandle {
161
162
  * @todo Implement
162
163
  */
163
164
  readv(buffers: readonly NodeJS.ArrayBufferView[], position?: number): Promise<Node.ReadVResult>;
164
- createReadStream(options?: CreateReadStreamOptions): Node.ReadStream;
165
- createWriteStream(options?: CreateWriteStreamOptions): Node.WriteStream;
165
+ createReadStream(options?: CreateReadStreamOptions): ReadStream;
166
+ createWriteStream(options?: CreateWriteStreamOptions): WriteStream;
166
167
  }
167
168
  /**
168
169
  * Renames a file
@@ -4,8 +4,8 @@ import { ActionType, isAppendable, isReadable, isWriteable, parseFlag, pathExist
4
4
  import { BigIntStats, FileType } from '../stats.js';
5
5
  import { F_OK } from './constants.js';
6
6
  import { Dirent } from './dir.js';
7
- import { dirname, join } from './path.js';
8
- import { cred, fd2file, fdMap, fixError, getFdForFile, mounts, normalizeMode, normalizeOptions, normalizePath, normalizeTime, resolveFS } from './shared.js';
7
+ import { dirname, join, parse } from './path.js';
8
+ import { cred, fd2file, fdMap, fixError, getFdForFile, mounts, normalizeMode, normalizeOptions, normalizePath, normalizeTime, resolveMount } from './shared.js';
9
9
  export * as constants from './constants.js';
10
10
  export class FileHandle {
11
11
  constructor(
@@ -227,7 +227,8 @@ export class FileHandle {
227
227
  */
228
228
  async function doOp(...[name, resolveSymlinks, rawPath, ...args]) {
229
229
  rawPath = normalizePath(rawPath);
230
- const { fs, path } = resolveFS(resolveSymlinks && (await exists(rawPath)) ? await realpath(rawPath) : rawPath);
230
+ const _path = resolveSymlinks && (await exists(rawPath)) ? await realpath(rawPath) : rawPath;
231
+ const { fs, path } = resolveMount(_path);
231
232
  try {
232
233
  // @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
233
234
  return fs[name](path, ...args);
@@ -245,8 +246,8 @@ async function doOp(...[name, resolveSymlinks, rawPath, ...args]) {
245
246
  export async function rename(oldPath, newPath) {
246
247
  oldPath = normalizePath(oldPath);
247
248
  newPath = normalizePath(newPath);
248
- const src = resolveFS(oldPath);
249
- const dst = resolveFS(newPath);
249
+ const src = resolveMount(oldPath);
250
+ const dst = resolveMount(newPath);
250
251
  try {
251
252
  if (src.mountPoint == dst.mountPoint) {
252
253
  await src.fs.rename(src.path, dst.path, cred);
@@ -266,8 +267,8 @@ rename;
266
267
  */
267
268
  export async function exists(_path) {
268
269
  try {
269
- const { fs, path } = resolveFS(_path);
270
- return fs.exists(path, cred);
270
+ const { fs, path } = resolveMount(await realpath(_path));
271
+ return await fs.exists(path, cred);
271
272
  }
272
273
  catch (e) {
273
274
  if (e.errno == ErrorCode.ENOENT) {
@@ -394,7 +395,7 @@ async function _readFile(fname, flag, resolveSymlinks) {
394
395
  }
395
396
  }
396
397
  export async function readFile(filename, _options) {
397
- const options = normalizeOptions(_options, null, 'r', null);
398
+ const options = normalizeOptions(_options, null, 'r', 0);
398
399
  const flag = parseFlag(options.flag);
399
400
  if (!isReadable(flag)) {
400
401
  throw new ApiError(ErrorCode.EINVAL, 'Flag passed must allow for reading.');
@@ -630,17 +631,18 @@ export async function lutimes(path, atime, mtime) {
630
631
  lutimes;
631
632
  export async function realpath(path, options) {
632
633
  path = normalizePath(path);
633
- const { fs, path: resolvedPath, mountPoint } = resolveFS(path);
634
+ const { base, dir } = parse(path);
635
+ const lpath = join(dir == '/' ? '/' : await realpath(dir), base);
636
+ const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
634
637
  try {
635
638
  const stats = await fs.stat(resolvedPath, cred);
636
639
  if (!stats.isSymbolicLink()) {
637
- return path;
640
+ return lpath;
638
641
  }
639
- const dst = mountPoint + normalizePath(Buffer.from(await _readFile(resolvedPath, 'r+', false)).toString());
640
- return realpath(dst);
642
+ return realpath(mountPoint + (await readlink(lpath)));
641
643
  }
642
644
  catch (e) {
643
- throw fixError(e, { [resolvedPath]: path });
645
+ throw fixError(e, { [resolvedPath]: lpath });
644
646
  }
645
647
  }
646
648
  realpath;
@@ -70,7 +70,7 @@ export declare function umount(mountPoint: string): void;
70
70
  /**
71
71
  * Gets the internal FileSystem for the path, then returns it along with the path relative to the FS' root
72
72
  */
73
- export declare function resolveFS(path: string): {
73
+ export declare function resolveMount(path: string): {
74
74
  fs: FileSystem;
75
75
  path: string;
76
76
  mountPoint: string;
@@ -84,7 +84,7 @@ export declare function fixPaths(text: string, paths: {
84
84
  export declare function fixError<E extends Error>(e: E, paths: {
85
85
  [from: string]: string;
86
86
  }): E;
87
- export declare function initialize(mountMapping: MountMapping): void;
87
+ export declare function mountMapping(mountMapping: MountMapping): void;
88
88
  /**
89
89
  * Types supports as path parameters.
90
90
  *
@@ -153,7 +153,7 @@ export function umount(mountPoint) {
153
153
  /**
154
154
  * Gets the internal FileSystem for the path, then returns it along with the path relative to the FS' root
155
155
  */
156
- export function resolveFS(path) {
156
+ export function resolveMount(path) {
157
157
  path = normalizePath(path);
158
158
  const sortedMounts = [...mounts].sort((a, b) => (a[0].length > b[0].length ? -1 : 1)); // decending order of the string length
159
159
  for (const [mountPoint, fs] of sortedMounts) {
@@ -184,7 +184,7 @@ export function fixError(e, paths) {
184
184
  e.message = fixPaths(e.message, paths);
185
185
  return e;
186
186
  }
187
- export function initialize(mountMapping) {
187
+ export function mountMapping(mountMapping) {
188
188
  if ('/' in mountMapping) {
189
189
  umount('/');
190
190
  }
@@ -1,11 +1,12 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
2
  /// <reference types="node" resolution-mode="require"/>
3
+ import { Buffer } from 'buffer';
4
+ import type * as Node from 'fs';
5
+ import type { BufferEncodingOption, EncodingOption, ReadSyncOptions, symlink } from 'fs';
3
6
  import { FileContents } from '../filesystem.js';
4
7
  import { BigIntStats, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
5
- import type { symlink, ReadSyncOptions, EncodingOption, BufferEncodingOption } from 'fs';
6
- import type * as Node from 'fs';
7
- import { PathLike } from './shared.js';
8
8
  import { Dir, Dirent } from './dir.js';
9
+ import { PathLike } from './shared.js';
9
10
  /**
10
11
  * Synchronous rename.
11
12
  * @param oldPath
@@ -1,12 +1,13 @@
1
+ import { Buffer } from 'buffer';
1
2
  import { ApiError, ErrorCode } from '../ApiError.js';
2
3
  import { ActionType, isAppendable, isReadable, isWriteable, parseFlag, pathExistsAction, pathNotExistsAction } from '../file.js';
3
4
  import { BigIntStats, FileType } from '../stats.js';
4
- import { normalizePath, cred, getFdForFile, normalizeMode, normalizeOptions, fdMap, fd2file, normalizeTime, resolveFS, fixError, mounts } from './shared.js';
5
5
  import { Dirent } from './dir.js';
6
- import { dirname, join } from './path.js';
6
+ import { dirname, join, parse } from './path.js';
7
+ import { cred, fd2file, fdMap, fixError, getFdForFile, mounts, normalizeMode, normalizeOptions, normalizePath, normalizeTime, resolveMount } from './shared.js';
7
8
  function doOp(...[name, resolveSymlinks, path, ...args]) {
8
9
  path = normalizePath(path);
9
- const { fs, path: resolvedPath } = resolveFS(resolveSymlinks && existsSync(path) ? realpathSync(path) : path);
10
+ const { fs, path: resolvedPath } = resolveMount(resolveSymlinks && existsSync(path) ? realpathSync(path) : path);
10
11
  try {
11
12
  // @ts-expect-error 2556 (since ...args is not correctly picked up as being a tuple)
12
13
  return fs[name](resolvedPath, ...args);
@@ -23,8 +24,8 @@ function doOp(...[name, resolveSymlinks, path, ...args]) {
23
24
  export function renameSync(oldPath, newPath) {
24
25
  oldPath = normalizePath(oldPath);
25
26
  newPath = normalizePath(newPath);
26
- const _old = resolveFS(oldPath);
27
- const _new = resolveFS(newPath);
27
+ const _old = resolveMount(oldPath);
28
+ const _new = resolveMount(newPath);
28
29
  const paths = { [_old.path]: oldPath, [_new.path]: newPath };
29
30
  try {
30
31
  if (_old === _new) {
@@ -45,7 +46,7 @@ renameSync;
45
46
  export function existsSync(path) {
46
47
  path = normalizePath(path);
47
48
  try {
48
- const { fs, path: resolvedPath } = resolveFS(path);
49
+ const { fs, path: resolvedPath } = resolveMount(realpathSync(path));
49
50
  return fs.existsSync(resolvedPath, cred);
50
51
  }
51
52
  catch (e) {
@@ -514,17 +515,18 @@ export function lutimesSync(path, atime, mtime) {
514
515
  lutimesSync;
515
516
  export function realpathSync(path, options) {
516
517
  path = normalizePath(path);
517
- const { fs, path: resolvedPath, mountPoint } = resolveFS(path);
518
+ const { base, dir } = parse(path);
519
+ const lpath = join(dir == '/' ? '/' : realpathSync(dir), base);
520
+ const { fs, path: resolvedPath, mountPoint } = resolveMount(lpath);
518
521
  try {
519
522
  const stats = fs.statSync(resolvedPath, cred);
520
523
  if (!stats.isSymbolicLink()) {
521
- return path;
524
+ return lpath;
522
525
  }
523
- const dst = normalizePath(mountPoint + Buffer.from(_readFileSync(resolvedPath, 'r+', false)).toString());
524
- return realpathSync(dst);
526
+ return realpathSync(mountPoint + readlinkSync(lpath));
525
527
  }
526
528
  catch (e) {
527
- throw fixError(e, { [resolvedPath]: path });
529
+ throw fixError(e, { [resolvedPath]: lpath });
528
530
  }
529
531
  }
530
532
  realpathSync;
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export * from './inode.js';
15
15
  export * from './mutex.js';
16
16
  export * from './stats.js';
17
17
  export * from './utils.js';
18
+ export * from './emulation/index.js';
18
19
  import * as fs from './emulation/index.js';
19
20
  export { fs };
20
21
  export default fs;
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export * from './inode.js';
15
15
  export * from './mutex.js';
16
16
  export * from './stats.js';
17
17
  export * from './utils.js';
18
+ export * from './emulation/index.js';
18
19
  import * as fs from './emulation/index.js';
19
20
  export { fs };
20
21
  export default fs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "0.5.11",
3
+ "version": "0.6.0",
4
4
  "description": "A filesystem in your browser",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist",
@@ -32,7 +32,8 @@
32
32
  },
33
33
  "exports": {
34
34
  ".": "./dist/index.js",
35
- "./*": "./dist/*"
35
+ "./*": "./dist/*",
36
+ "./promises": "./dist/emulations/promises.js"
36
37
  },
37
38
  "typesVersions": {
38
39
  "*": {