@zenfs/core 0.9.7 → 0.10.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.
Files changed (71) hide show
  1. package/dist/backends/AsyncStore.js +29 -29
  2. package/dist/backends/Fetch.d.ts +84 -0
  3. package/dist/backends/Fetch.js +171 -0
  4. package/dist/backends/Index.js +19 -19
  5. package/dist/backends/Locked.d.ts +11 -11
  6. package/dist/backends/Locked.js +50 -49
  7. package/dist/backends/Overlay.js +21 -21
  8. package/dist/backends/SyncStore.js +27 -27
  9. package/dist/backends/backend.js +4 -4
  10. package/dist/backends/port/fs.d.ts +124 -0
  11. package/dist/backends/port/fs.js +241 -0
  12. package/dist/backends/port/rpc.d.ts +60 -0
  13. package/dist/backends/port/rpc.js +71 -0
  14. package/dist/backends/port/store.d.ts +30 -0
  15. package/dist/backends/port/store.js +142 -0
  16. package/dist/browser.min.js +4 -4
  17. package/dist/browser.min.js.map +4 -4
  18. package/dist/config.d.ts +8 -10
  19. package/dist/config.js +11 -11
  20. package/dist/emulation/async.js +6 -6
  21. package/dist/emulation/dir.js +2 -2
  22. package/dist/emulation/index.d.ts +1 -1
  23. package/dist/emulation/index.js +1 -1
  24. package/dist/emulation/path.d.ts +3 -2
  25. package/dist/emulation/path.js +19 -45
  26. package/dist/emulation/promises.d.ts +7 -12
  27. package/dist/emulation/promises.js +144 -146
  28. package/dist/emulation/shared.d.ts +5 -10
  29. package/dist/emulation/shared.js +8 -8
  30. package/dist/emulation/streams.js +3 -3
  31. package/dist/emulation/sync.js +25 -25
  32. package/dist/{ApiError.d.ts → error.d.ts} +13 -14
  33. package/dist/error.js +292 -0
  34. package/dist/file.d.ts +2 -0
  35. package/dist/file.js +10 -4
  36. package/dist/filesystem.js +15 -15
  37. package/dist/index.d.ts +4 -1
  38. package/dist/index.js +4 -1
  39. package/dist/mutex.js +2 -1
  40. package/dist/utils.d.ts +8 -7
  41. package/dist/utils.js +11 -12
  42. package/package.json +3 -3
  43. package/readme.md +17 -9
  44. package/src/backends/AsyncStore.ts +29 -29
  45. package/src/backends/Fetch.ts +230 -0
  46. package/src/backends/Index.ts +19 -19
  47. package/src/backends/Locked.ts +50 -49
  48. package/src/backends/Overlay.ts +23 -23
  49. package/src/backends/SyncStore.ts +27 -27
  50. package/src/backends/backend.ts +6 -6
  51. package/src/backends/port/fs.ts +308 -0
  52. package/src/backends/port/readme.md +59 -0
  53. package/src/backends/port/rpc.ts +144 -0
  54. package/src/backends/port/store.ts +187 -0
  55. package/src/config.ts +20 -24
  56. package/src/emulation/async.ts +6 -6
  57. package/src/emulation/dir.ts +2 -2
  58. package/src/emulation/index.ts +1 -1
  59. package/src/emulation/path.ts +25 -49
  60. package/src/emulation/promises.ts +150 -159
  61. package/src/emulation/shared.ts +12 -14
  62. package/src/emulation/streams.ts +3 -3
  63. package/src/emulation/sync.ts +28 -28
  64. package/src/{ApiError.ts → error.ts} +89 -89
  65. package/src/file.ts +12 -4
  66. package/src/filesystem.ts +15 -15
  67. package/src/index.ts +4 -1
  68. package/src/mutex.ts +3 -1
  69. package/src/utils.ts +16 -18
  70. package/tsconfig.json +2 -2
  71. package/dist/ApiError.js +0 -292
package/src/config.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { ApiError, ErrorCode } from './ApiError.js';
1
+ import { ErrnoError, Errno } from './error.js';
2
2
  import type { Backend, BackendConfiguration } from './backends/backend.js';
3
3
  import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
4
4
  import * as fs from './emulation/index.js';
5
- import { setCred, type MountMapping } from './emulation/shared.js';
5
+ import { setCred, type MountObject } from './emulation/shared.js';
6
6
  import { FileSystem } from './filesystem.js';
7
+ import type { AbsolutePath } from './emulation/path.js';
7
8
 
8
9
  /**
9
10
  * Configuration for a specific mount point
@@ -20,11 +21,11 @@ function isMountConfig(arg: unknown): arg is MountConfiguration {
20
21
  */
21
22
  export async function resolveMountConfig<FS extends FileSystem, TOptions extends object = object>(config: MountConfiguration<FS, TOptions>, _depth = 0): Promise<FS> {
22
23
  if (typeof config !== 'object' || config == null) {
23
- throw new ApiError(ErrorCode.EINVAL, 'Invalid options on mount configuration');
24
+ throw new ErrnoError(Errno.EINVAL, 'Invalid options on mount configuration');
24
25
  }
25
26
 
26
27
  if (!isMountConfig(config)) {
27
- throw new ApiError(ErrorCode.EINVAL, 'Invalid mount configuration');
28
+ throw new ErrnoError(Errno.EINVAL, 'Invalid mount configuration');
28
29
  }
29
30
 
30
31
  if (config instanceof FileSystem) {
@@ -45,7 +46,7 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
45
46
  }
46
47
 
47
48
  if (_depth > 10) {
48
- throw new ApiError(ErrorCode.EINVAL, 'Invalid configuration, too deep and possibly infinite');
49
+ throw new ErrnoError(Errno.EINVAL, 'Invalid configuration, too deep and possibly infinite');
49
50
  }
50
51
 
51
52
  (<Record<string, FileSystem>>config)[key] = await resolveMountConfig(value, ++_depth);
@@ -54,7 +55,7 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
54
55
  const { backend } = config;
55
56
 
56
57
  if (!(await backend.isAvailable())) {
57
- throw new ApiError(ErrorCode.EPERM, 'Backend not available: ' + backend);
58
+ throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
58
59
  }
59
60
  checkOptions(backend, config);
60
61
  const mount = backend.create(config);
@@ -63,39 +64,34 @@ export async function resolveMountConfig<FS extends FileSystem, TOptions extends
63
64
  }
64
65
 
65
66
  /**
66
- *A mapping of mount points to their configurations
67
+ * Configuration
67
68
  */
68
- export type MappingConfiguration = Partial<{
69
- uid: number;
70
- gid: number;
71
- }> &
72
- Record<string, FileSystem | BackendConfiguration | Backend>;
73
-
74
- /**
75
- * Configuration for the file systems
76
- */
77
- export type Configuration = MountConfiguration | MappingConfiguration;
69
+ export interface Configuration {
70
+ mounts: Record<AbsolutePath, MountConfiguration>;
71
+ uid?: number;
72
+ gid?: number;
73
+ }
78
74
 
79
75
  /**
80
76
  * Creates filesystems with the given configuration, and initializes ZenFS with it.
81
77
  * @see Configuration for more info on the configuration object.
82
78
  */
83
- export async function configure(config: Configuration): Promise<void> {
79
+ export async function configure(config: MountConfiguration | Configuration): Promise<void> {
84
80
  const uid = 'uid' in config ? config.uid || 0 : 0;
85
81
  const gid = 'gid' in config ? config.gid || 0 : 0;
86
82
 
87
83
  if (isMountConfig(config)) {
88
84
  // single FS
89
- config = { '/': config };
85
+ config = { mounts: { '/': config } };
90
86
  }
91
87
 
92
- for (const [point, value] of Object.entries(config)) {
93
- if (point == 'uid' || point == 'gid' || typeof value == 'number') {
94
- continue;
88
+ for (const [point, value] of Object.entries(config.mounts) as [AbsolutePath, MountConfiguration][]) {
89
+ if (!point.startsWith('/')) {
90
+ throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
95
91
  }
96
- config[point] = await resolveMountConfig(value);
92
+ config.mounts[point] = await resolveMountConfig(value);
97
93
  }
98
94
 
99
- fs.mountMapping(<MountMapping>config);
95
+ fs.mountObject(config.mounts as MountObject);
100
96
  setCred({ uid, gid, suid: uid, sgid: gid, euid: uid, egid: gid });
101
97
  }
@@ -1,5 +1,5 @@
1
1
  import type * as fs from 'node:fs';
2
- import { ApiError, ErrorCode } from '../ApiError.js';
2
+ import { ErrnoError, Errno } from '../error.js';
3
3
  import type { FileContents } from '../filesystem.js';
4
4
  import { BigIntStats, type BigIntStatsFs, type Stats, type StatsFs } from '../stats.js';
5
5
  import { nop, normalizeMode, type Callback } from '../utils.js';
@@ -262,7 +262,7 @@ export function ftruncate(fd: number, lenOrCB?: any, cb: Callback = nop): void {
262
262
  cb = typeof lenOrCB === 'function' ? lenOrCB : cb;
263
263
  const file = fd2file(fd);
264
264
  if (length < 0) {
265
- throw new ApiError(ErrorCode.EINVAL);
265
+ throw new ErrnoError(Errno.EINVAL);
266
266
  }
267
267
  file.truncate(length)
268
268
  .then(() => cb())
@@ -335,7 +335,7 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
335
335
  default:
336
336
  // ...try to find the callback and get out of here!
337
337
  cb = typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPos === 'function' ? cbPos : cb;
338
- (<Callback<[number, Uint8Array | string]>>cb)(new ApiError(ErrorCode.EINVAL, 'Invalid arguments.'));
338
+ (<Callback<[number, Uint8Array | string]>>cb)(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
339
339
  return;
340
340
  }
341
341
  buffer = Buffer.from(data);
@@ -661,7 +661,7 @@ access satisfies Omit<typeof fs.access, '__promisify__'>;
661
661
  export function watchFile(path: fs.PathLike, listener: (curr: Stats, prev: Stats) => void): void;
662
662
  export function watchFile(path: fs.PathLike, options: { persistent?: boolean; interval?: number }, listener: (curr: Stats, prev: Stats) => void): void;
663
663
  export function watchFile(path: fs.PathLike, optsListener: any, listener: (curr: Stats, prev: Stats) => void = nop): void {
664
- throw ApiError.With('ENOSYS', path.toString(), 'watchFile');
664
+ throw ErrnoError.With('ENOSYS', path.toString(), 'watchFile');
665
665
  }
666
666
  watchFile satisfies Omit<typeof fs.watchFile, '__promisify__'>;
667
667
 
@@ -669,7 +669,7 @@ watchFile satisfies Omit<typeof fs.watchFile, '__promisify__'>;
669
669
  * @todo Implement
670
670
  */
671
671
  export function unwatchFile(path: fs.PathLike, listener: (curr: Stats, prev: Stats) => void = nop): void {
672
- throw ApiError.With('ENOSYS', path.toString(), 'unwatchFile');
672
+ throw ErrnoError.With('ENOSYS', path.toString(), 'unwatchFile');
673
673
  }
674
674
  unwatchFile satisfies Omit<typeof fs.unwatchFile, '__promisify__'>;
675
675
 
@@ -679,7 +679,7 @@ unwatchFile satisfies Omit<typeof fs.unwatchFile, '__promisify__'>;
679
679
  export function watch(path: fs.PathLike, listener?: (event: string, filename: string) => any): fs.FSWatcher;
680
680
  export function watch(path: fs.PathLike, options: { persistent?: boolean }, listener?: (event: string, filename: string) => any): fs.FSWatcher;
681
681
  export function watch(path: fs.PathLike, options: any, listener: (event: string, filename: string) => any = nop): fs.FSWatcher {
682
- throw ApiError.With('ENOSYS', path.toString(), 'watch');
682
+ throw ErrnoError.With('ENOSYS', path.toString(), 'watch');
683
683
  }
684
684
  watch satisfies Omit<typeof fs.watch, '__promisify__'>;
685
685
 
@@ -2,7 +2,7 @@ import type { Dirent as _Dirent, Dir as _Dir } from 'fs';
2
2
  import type { Callback } from '../utils.js';
3
3
  import type { Stats } from '../stats.js';
4
4
  import { readdir } from './promises.js';
5
- import { ApiError, ErrorCode } from '../ApiError.js';
5
+ import { ErrnoError, Errno } from '../error.js';
6
6
  import { readdirSync } from './sync.js';
7
7
  import { basename } from './path.js';
8
8
 
@@ -47,7 +47,7 @@ export class Dir implements _Dir {
47
47
 
48
48
  protected checkClosed(): void {
49
49
  if (this.closed) {
50
- throw new ApiError(ErrorCode.EBADF, 'Can not use closed Dir');
50
+ throw new ErrnoError(Errno.EBADF, 'Can not use closed Dir');
51
51
  }
52
52
  }
53
53
 
@@ -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 { mountMapping, mounts, mount, umount } from './shared.js';
7
+ export { mountObject, mounts, mount, umount } from './shared.js';
8
8
  export { Stats, BigIntStats, StatsFs } from '../stats.js';
@@ -22,12 +22,14 @@ USE OR OTHER DEALINGS IN THE SOFTWARE.
22
22
  */
23
23
 
24
24
  /*
25
- This is the POSIX path code of NodeJS
25
+ This is derived from the POSIX path code of NodeJS
26
26
  https://raw.githubusercontent.com/nodejs/node/3907bd1/lib/path.js
27
27
  */
28
28
 
29
29
  import type { ParsedPath } from 'node:path';
30
30
 
31
+ export type AbsolutePath = `/${string}`;
32
+
31
33
  export let cwd = '/';
32
34
 
33
35
  export function cd(path: string): void {
@@ -36,12 +38,6 @@ export function cd(path: string): void {
36
38
 
37
39
  export const sep = '/';
38
40
 
39
- function validateString(str: unknown, name: string): void {
40
- if (typeof str != 'string') {
41
- throw new TypeError(`"${name}" is not a string`);
42
- }
43
- }
44
-
45
41
  function validateObject(str: unknown, name: string): void {
46
42
  if (typeof str != 'object') {
47
43
  throw new TypeError(`"${name}" is not an object`);
@@ -113,23 +109,23 @@ export function formatExt(ext: string): string {
113
109
  return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : '';
114
110
  }
115
111
 
116
- export function resolve(...args: string[]): string {
112
+ export function resolve(...parts: string[]): AbsolutePath {
117
113
  let resolved = '';
118
- let absolute = false;
119
114
 
120
- for (let i = args.length - 1; i >= -1 && !absolute; i--) {
121
- const path = i >= 0 ? args[i] : cwd;
122
- validateString(path, `paths[${i}]`);
123
-
124
- // Skip empty entries
125
- if (!path.length) {
115
+ for (const part of [...parts.reverse(), cwd]) {
116
+ if (!part.length) {
126
117
  continue;
127
118
  }
128
119
 
129
- resolved = `${path}/${resolved}`;
130
- absolute = path[0] == '/';
120
+ resolved = `${part}/${resolved}`;
121
+
122
+ if (part.startsWith('/')) {
123
+ break;
124
+ }
131
125
  }
132
126
 
127
+ const absolute = resolved.startsWith('/');
128
+
133
129
  // At this point the path should be resolved to a full absolute path, but
134
130
  // handle relative paths to be safe (might happen when cwd fails)
135
131
 
@@ -139,21 +135,19 @@ export function resolve(...args: string[]): string {
139
135
  if (absolute) {
140
136
  return `/${resolved}`;
141
137
  }
142
- return resolved.length > 0 ? resolved : '/';
138
+ return resolved.length ? (resolved as AbsolutePath) : '/';
143
139
  }
144
140
 
145
141
  export function normalize(path: string): string {
146
- validateString(path, 'path');
147
-
148
- if (path.length === 0) return '.';
142
+ if (!path.length) return '.';
149
143
 
150
- const isAbsolute = path[0] === '/';
151
- const trailingSeparator = path.at(-1) === '/';
144
+ const isAbsolute = path.startsWith('/');
145
+ const trailingSeparator = path.endsWith('/');
152
146
 
153
147
  // Normalize the path
154
148
  path = normalizeString(path, !isAbsolute);
155
149
 
156
- if (path.length === 0) {
150
+ if (!path.length) {
157
151
  if (isAbsolute) return '/';
158
152
  return trailingSeparator ? './' : '.';
159
153
  }
@@ -163,29 +157,17 @@ export function normalize(path: string): string {
163
157
  }
164
158
 
165
159
  export function isAbsolute(path: string): boolean {
166
- validateString(path, 'path');
167
- return path.length > 0 && path[0] === '/';
160
+ return path.startsWith('/');
168
161
  }
169
162
 
170
- export function join(...args: string[]): string {
171
- if (args.length === 0) return '.';
172
- let joined;
173
- for (let i = 0; i < args.length; ++i) {
174
- const arg = args[i];
175
- validateString(arg, 'path');
176
- if (arg.length > 0) {
177
- if (joined === undefined) joined = arg;
178
- else joined += `/${arg}`;
179
- }
180
- }
181
- if (joined === undefined) return '.';
163
+ export function join(...parts: string[]): string {
164
+ if (!parts.length) return '.';
165
+ const joined = parts.join('/');
166
+ if (!joined?.length) return '.';
182
167
  return normalize(joined);
183
168
  }
184
169
 
185
- export function relative(from: string, to: string) {
186
- validateString(from, 'from');
187
- validateString(to, 'to');
188
-
170
+ export function relative(from: string, to: string): string {
189
171
  if (from === to) return '';
190
172
 
191
173
  // Trim leading forward slashes.
@@ -249,7 +231,6 @@ export function relative(from: string, to: string) {
249
231
  }
250
232
 
251
233
  export function dirname(path: string): string {
252
- validateString(path, 'path');
253
234
  if (path.length === 0) return '.';
254
235
  const hasRoot = path[0] === '/';
255
236
  let end = -1;
@@ -272,9 +253,6 @@ export function dirname(path: string): string {
272
253
  }
273
254
 
274
255
  export function basename(path: string, suffix?: string): string {
275
- if (suffix !== undefined) validateString(suffix, 'ext');
276
- validateString(path, 'path');
277
-
278
256
  let start = 0;
279
257
  let end = -1;
280
258
  let matchedSlash = true;
@@ -341,7 +319,6 @@ export function basename(path: string, suffix?: string): string {
341
319
  }
342
320
 
343
321
  export function extname(path: string): string {
344
- validateString(path, 'path');
345
322
  let startDot = -1;
346
323
  let startPart = 0;
347
324
  let end = -1;
@@ -400,8 +377,7 @@ export function format(pathObject: ParsedPath): string {
400
377
  }
401
378
 
402
379
  export function parse(path: string): ParsedPath {
403
- validateString(path, 'path');
404
- const isAbsolute = path[0] === '/';
380
+ const isAbsolute = path.startsWith('/');
405
381
  const ret = { root: isAbsolute ? '/' : '', dir: '', base: '', ext: '', name: '' };
406
382
  if (path.length === 0) return ret;
407
383