@zenfs/core 1.11.3 → 2.0.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 (116) hide show
  1. package/dist/backends/backend.d.ts +19 -15
  2. package/dist/backends/backend.js +31 -15
  3. package/dist/backends/cow.d.ts +20 -30
  4. package/dist/backends/cow.js +52 -142
  5. package/dist/backends/fetch.d.ts +1 -0
  6. package/dist/backends/fetch.js +3 -1
  7. package/dist/backends/index.d.ts +1 -1
  8. package/dist/backends/index.js +1 -1
  9. package/dist/backends/memory.d.ts +5 -7
  10. package/dist/backends/memory.js +2 -3
  11. package/dist/backends/passthrough.d.ts +19 -22
  12. package/dist/backends/passthrough.js +85 -160
  13. package/dist/backends/port.d.ts +207 -0
  14. package/dist/backends/port.js +297 -0
  15. package/dist/backends/single_buffer.d.ts +11 -5
  16. package/dist/backends/single_buffer.js +18 -12
  17. package/dist/backends/store/fs.d.ts +11 -27
  18. package/dist/backends/store/fs.js +67 -91
  19. package/dist/backends/store/store.d.ts +7 -12
  20. package/dist/config.d.ts +1 -10
  21. package/dist/config.js +7 -8
  22. package/dist/context.d.ts +8 -21
  23. package/dist/context.js +33 -10
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.js +2 -1
  26. package/dist/internal/contexts.d.ts +63 -0
  27. package/dist/internal/contexts.js +15 -0
  28. package/dist/internal/credentials.d.ts +2 -11
  29. package/dist/internal/credentials.js +0 -19
  30. package/dist/internal/devices.d.ts +18 -80
  31. package/dist/internal/devices.js +76 -279
  32. package/dist/internal/file_index.js +3 -3
  33. package/dist/internal/filesystem.d.ts +31 -89
  34. package/dist/internal/filesystem.js +21 -20
  35. package/dist/internal/index.d.ts +0 -1
  36. package/dist/internal/index.js +0 -1
  37. package/dist/internal/index_fs.d.ts +12 -30
  38. package/dist/internal/index_fs.js +23 -55
  39. package/dist/internal/inode.d.ts +147 -9
  40. package/dist/internal/inode.js +333 -25
  41. package/dist/internal/log.d.ts +19 -13
  42. package/dist/internal/log.js +81 -80
  43. package/dist/mixins/async.js +26 -90
  44. package/dist/mixins/mutexed.d.ts +17 -16
  45. package/dist/mixins/mutexed.js +29 -31
  46. package/dist/mixins/readonly.d.ts +7 -6
  47. package/dist/mixins/readonly.js +6 -0
  48. package/dist/mixins/sync.js +8 -8
  49. package/dist/{vfs/path.d.ts → path.d.ts} +3 -4
  50. package/dist/{vfs/path.js → path.js} +6 -9
  51. package/dist/readline.d.ts +134 -0
  52. package/dist/readline.js +623 -0
  53. package/dist/utils.d.ts +4 -35
  54. package/dist/utils.js +8 -73
  55. package/dist/vfs/acl.d.ts +42 -0
  56. package/dist/vfs/acl.js +249 -0
  57. package/dist/vfs/async.d.ts +7 -21
  58. package/dist/vfs/async.js +19 -19
  59. package/dist/vfs/config.d.ts +6 -18
  60. package/dist/vfs/config.js +8 -18
  61. package/dist/vfs/dir.d.ts +3 -3
  62. package/dist/vfs/dir.js +9 -8
  63. package/dist/vfs/file.d.ts +106 -0
  64. package/dist/vfs/file.js +235 -0
  65. package/dist/vfs/flags.d.ts +19 -0
  66. package/dist/vfs/flags.js +62 -0
  67. package/dist/vfs/index.d.ts +4 -10
  68. package/dist/vfs/index.js +4 -13
  69. package/dist/vfs/ioctl.d.ts +87 -0
  70. package/dist/vfs/ioctl.js +304 -0
  71. package/dist/vfs/promises.d.ts +78 -16
  72. package/dist/vfs/promises.js +273 -122
  73. package/dist/vfs/shared.d.ts +7 -26
  74. package/dist/vfs/shared.js +25 -53
  75. package/dist/{stats.d.ts → vfs/stats.d.ts} +14 -28
  76. package/dist/{stats.js → vfs/stats.js} +11 -66
  77. package/dist/vfs/streams.d.ts +1 -0
  78. package/dist/vfs/streams.js +24 -19
  79. package/dist/vfs/sync.d.ts +4 -3
  80. package/dist/vfs/sync.js +143 -128
  81. package/dist/vfs/watchers.d.ts +2 -2
  82. package/dist/vfs/watchers.js +6 -6
  83. package/dist/vfs/xattr.d.ts +116 -0
  84. package/dist/vfs/xattr.js +218 -0
  85. package/package.json +3 -3
  86. package/readme.md +1 -1
  87. package/tests/backend/config.worker.js +4 -1
  88. package/tests/backend/fetch.test.ts +3 -0
  89. package/tests/backend/port.test.ts +21 -35
  90. package/tests/backend/remote.worker.js +4 -1
  91. package/tests/backend/single-buffer.test.ts +24 -0
  92. package/tests/common/context.test.ts +1 -1
  93. package/tests/common/handle.test.ts +17 -12
  94. package/tests/common/path.test.ts +1 -1
  95. package/tests/common/readline.test.ts +104 -0
  96. package/tests/common.ts +4 -19
  97. package/tests/fetch/fetch.ts +1 -1
  98. package/tests/fs/links.test.ts +1 -1
  99. package/tests/fs/permissions.test.ts +7 -6
  100. package/tests/fs/readFile.test.ts +3 -3
  101. package/tests/fs/stat.test.ts +6 -6
  102. package/tests/fs/streams.test.ts +2 -11
  103. package/tests/fs/times.test.ts +1 -1
  104. package/tests/fs/xattr.test.ts +85 -0
  105. package/tests/logs.js +22 -0
  106. package/tests/setup/context.ts +1 -1
  107. package/tests/setup/index.ts +3 -3
  108. package/tests/setup/port.ts +1 -1
  109. package/dist/backends/port/fs.d.ts +0 -84
  110. package/dist/backends/port/fs.js +0 -151
  111. package/dist/backends/port/rpc.d.ts +0 -77
  112. package/dist/backends/port/rpc.js +0 -100
  113. package/dist/backends/store/simple.d.ts +0 -20
  114. package/dist/backends/store/simple.js +0 -13
  115. package/dist/internal/file.d.ts +0 -351
  116. package/dist/internal/file.js +0 -739
@@ -0,0 +1,104 @@
1
+ import assert from 'node:assert/strict';
2
+ import { PassThrough } from 'node:stream';
3
+ import { suite, test } from 'node:test';
4
+ import { wait } from 'utilium';
5
+ import { createInterface, Interface } from '../../dist/readline.js';
6
+
7
+ suite('Readline interface', { skip: true }, () => {
8
+ test('creates interface with readable stream', async () => {
9
+ const input = new PassThrough();
10
+ await using rl = createInterface({ input });
11
+
12
+ assert.ok(rl instanceof Interface);
13
+ assert.equal(rl.input, input);
14
+ });
15
+
16
+ test('emits line events when receiving data', async () => {
17
+ const input = new PassThrough();
18
+ await using rl = createInterface({ input });
19
+
20
+ const lines: string[] = [];
21
+ rl.on('line', (line: string) => lines.push(line));
22
+
23
+ input.write('first line\n');
24
+ input.write('second line\r\n');
25
+ input.write('third line\n');
26
+
27
+ await wait(10);
28
+
29
+ assert.deepEqual(lines, ['first line', 'second line', 'third line']);
30
+ });
31
+
32
+ test('handles partial lines correctly', async () => {
33
+ const input = new PassThrough();
34
+ await using rl = createInterface({ input });
35
+
36
+ const lines: string[] = [];
37
+ rl.on('line', (line: string) => lines.push(line));
38
+
39
+ input.write('partial ');
40
+ input.write('line\n');
41
+ input.write('another ');
42
+ input.write('partial line\n');
43
+
44
+ await wait(10);
45
+
46
+ assert.deepEqual(lines, ['partial line', 'another partial line']);
47
+ });
48
+
49
+ test('emits remaining buffer on close', async () => {
50
+ const input = new PassThrough();
51
+ await using rl = createInterface({ input });
52
+
53
+ const lines: string[] = [];
54
+ rl.on('line', (line: string) => lines.push(line));
55
+
56
+ input.write('line with newline\n');
57
+ input.write('line without newline');
58
+
59
+ await wait(10);
60
+
61
+ assert.deepEqual(lines, ['line with newline']);
62
+
63
+ await wait(10);
64
+
65
+ assert.deepEqual(lines, ['line with newline', 'line without newline']);
66
+ });
67
+
68
+ test('tracks history correctly', async () => {
69
+ const input = new PassThrough();
70
+ await using rl = createInterface({ input });
71
+
72
+ let history: string[] = [];
73
+ rl.on('history', (h: string[]) => (history = h));
74
+
75
+ input.write('first command\n');
76
+ input.write('second command\n');
77
+
78
+ await wait(10);
79
+
80
+ assert.deepEqual(history, ['first command', 'second command']);
81
+ });
82
+
83
+ test('pause and resume functionality', async () => {
84
+ const input = new PassThrough();
85
+ await using rl = createInterface({ input });
86
+
87
+ const lines: string[] = [];
88
+ rl.on('line', (line: string) => lines.push(line));
89
+
90
+ rl.pause();
91
+ input.write('should not be processed\n');
92
+
93
+ await wait(10);
94
+
95
+ assert.deepEqual(lines, []);
96
+
97
+ rl.resume();
98
+ input.write('should be processed\n');
99
+
100
+ await wait(10);
101
+
102
+ assert.deepEqual(lines, ['should be processed']);
103
+ });
104
+ });
package/tests/common.ts CHANGED
@@ -1,26 +1,11 @@
1
1
  import { join, resolve } from 'node:path';
2
- import { fs as defaultFS, log } from '../dist/index.js';
2
+ import { fs as defaultFS } from '../dist/index.js';
3
+ import { setupLogs } from './logs.js';
3
4
  export type * from '../dist/index.js';
4
5
 
5
- const { ZENFS_LOG_LEVEL, SETUP } = process.env;
6
+ setupLogs();
6
7
 
7
- let level: log.Level | (typeof log.levels)[log.Level] = log.Level.CRIT;
8
-
9
- if (ZENFS_LOG_LEVEL) {
10
- const tmp = parseInt(ZENFS_LOG_LEVEL);
11
- if (Number.isSafeInteger(tmp)) level = tmp;
12
- else level = ZENFS_LOG_LEVEL as (typeof log.levels)[log.Level];
13
- }
14
-
15
- log.configure({
16
- enabled: true,
17
- format: log.formats.ansi_message,
18
- dumpBacklog: true,
19
- level,
20
- output: console.error,
21
- });
22
-
23
- const setupPath = resolve(SETUP || join(import.meta.dirname, 'setup/memory.ts'));
8
+ const setupPath = resolve(process.env.SETUP || join(import.meta.dirname, 'setup/memory.ts'));
24
9
 
25
10
  const setup = await import(setupPath).catch(error => {
26
11
  console.log('Failed to import test setup:');
@@ -13,7 +13,7 @@ await configure({
13
13
  log: {
14
14
  enabled: true,
15
15
  output: console.error,
16
- format: log.formats.ansi_message,
16
+ format: log.fancy({ style: 'ansi', colorize: 'message' }),
17
17
  level: log.Level.INFO,
18
18
  dumpBacklog: true,
19
19
  },
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { suite, test } from 'node:test';
3
- import { join } from '../../dist/vfs/path.js';
3
+ import { join } from '../../dist/path.js';
4
4
  import { fs } from '../common.js';
5
5
  import type { ErrnoError } from '../../dist/index.js';
6
6
 
@@ -1,9 +1,10 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { suite, test } from 'node:test';
3
- import { credentials, ErrnoError } from '../../dist/index.js';
4
- import { encodeUTF8 } from '../../dist/utils.js';
3
+ import { encodeUTF8 } from 'utilium';
4
+ import { ErrnoError } from '../../dist/index.js';
5
+ import { defaultContext } from '../../dist/internal/contexts.js';
6
+ import { join } from '../../dist/path.js';
5
7
  import { R_OK, W_OK, X_OK } from '../../dist/vfs/constants.js';
6
- import { join } from '../../dist/vfs/path.js';
7
8
  import { fs } from '../common.js';
8
9
 
9
10
  const asyncMode = 0o777;
@@ -87,8 +88,8 @@ suite('Permissions', () => {
87
88
  assert(stats.hasAccess(R_OK));
88
89
  }
89
90
 
90
- const copy = { ...credentials };
91
- Object.assign(credentials, { uid: 1000, gid: 1000, euid: 1000, egid: 1000 });
91
+ const copy = { ...defaultContext.credentials };
92
+ Object.assign(defaultContext.credentials, { uid: 1000, gid: 1000, euid: 1000, egid: 1000 });
92
93
  test('Access controls: /', () => test_item('/'));
93
- Object.assign(credentials, copy);
94
+ Object.assign(defaultContext.credentials, copy);
94
95
  });
@@ -36,7 +36,7 @@ suite('Read and Unlink', () => {
36
36
  });
37
37
  });
38
38
 
39
- suite('Read File Test', () => {
39
+ suite('Read File', () => {
40
40
  const fn = 'empty.txt';
41
41
 
42
42
  test('read file asynchronously', async () => {
@@ -64,10 +64,10 @@ suite('fs file reading', () => {
64
64
  test('read file synchronously and verify the content', () => {
65
65
  const content = fs.readFileSync('elipses.txt', 'utf8');
66
66
 
67
+ assert.equal(content.length, 10000);
68
+
67
69
  for (let i = 0; i < content.length; i++) {
68
70
  assert.equal(content[i], '…');
69
71
  }
70
-
71
- assert.equal(content.length, 10000);
72
72
  });
73
73
  });
@@ -1,7 +1,7 @@
1
1
  import assert from 'node:assert/strict';
2
2
  import { suite, test } from 'node:test';
3
- import { credentials } from '../../dist/index.js';
4
- import { Stats } from '../../dist/stats.js';
3
+ import { defaultContext } from '../../dist/internal/contexts.js';
4
+ import { Stats } from '../../dist/vfs/stats.js';
5
5
  import { fs } from '../common.js';
6
6
 
7
7
  suite('Stats', () => {
@@ -40,7 +40,7 @@ suite('Stats', () => {
40
40
 
41
41
  fs.writeFileSync(newFile, 'hello', { mode: 0o640 });
42
42
 
43
- const prevCredentials = { ...credentials };
43
+ const prevCredentials = { ...defaultContext.credentials };
44
44
  const uid = 33;
45
45
  const nonRootCredentials = {
46
46
  uid,
@@ -53,7 +53,7 @@ suite('Stats', () => {
53
53
 
54
54
  fs.chownSync(newFile, 0, nonRootCredentials.gid); // creating with root-user so that non-root user can access
55
55
 
56
- Object.assign(credentials, nonRootCredentials);
56
+ Object.assign(defaultContext.credentials, nonRootCredentials);
57
57
  const stat = fs.statSync(newFile);
58
58
 
59
59
  assert.equal(stat.gid, nonRootCredentials.gid);
@@ -63,13 +63,13 @@ suite('Stats', () => {
63
63
  assert.equal(stat.hasAccess(fs.constants.X_OK), false);
64
64
  // changing group
65
65
 
66
- Object.assign(credentials, { ...nonRootCredentials, gid: 44 });
66
+ Object.assign(defaultContext.credentials, { ...nonRootCredentials, gid: 44 });
67
67
 
68
68
  assert.equal(stat.hasAccess(fs.constants.R_OK), false);
69
69
  assert.equal(stat.hasAccess(fs.constants.W_OK), false);
70
70
  assert.equal(stat.hasAccess(fs.constants.X_OK), false);
71
71
 
72
- Object.assign(credentials, prevCredentials);
72
+ Object.assign(defaultContext.credentials, prevCredentials);
73
73
  });
74
74
 
75
75
  test('stat file', async () => {
@@ -129,21 +129,12 @@ suite('Streams', () => {
129
129
  test('FileHandle.createReadStream after close should give an error', async () => {
130
130
  const fileHandle = await fs.promises.open(testFilePath, 'r');
131
131
  await fileHandle.close();
132
- const stream = fileHandle.createReadStream();
133
- const { promise, resolve, reject } = Promise.withResolvers();
134
- setTimeout(resolve, 100);
135
- stream.on('error', reject);
136
- assert.rejects(promise);
132
+ assert.throws(() => fileHandle.createReadStream(), { code: 'EBADF' });
137
133
  });
138
134
 
139
135
  test('FileHandle.createWriteStream after close should give an error', async () => {
140
136
  const fileHandle = await fs.promises.open(testFilePathWrite, 'w');
141
137
  await fileHandle.close();
142
- const stream = fileHandle.createWriteStream();
143
- const { promise, resolve, reject } = Promise.withResolvers();
144
- setTimeout(resolve, 100);
145
- stream.on('error', reject);
146
- assert.rejects(promise);
147
- stream.write('Nuh-uh');
138
+ assert.throws(() => fileHandle.createWriteStream(), { code: 'EBADF' });
148
139
  });
149
140
  });
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
2
2
  import { suite, test } from 'node:test';
3
3
  import { wait } from 'utilium';
4
4
  import { ErrnoError } from '../../dist/index.js';
5
- import type { StatsLike } from '../../dist/stats.js';
5
+ import type { StatsLike } from '../../dist/vfs/stats.js';
6
6
  import { fs } from '../common.js';
7
7
 
8
8
  const path = 'x.txt';
@@ -0,0 +1,85 @@
1
+ import { Buffer } from 'buffer';
2
+ import assert from 'node:assert/strict';
3
+ import { suite, test } from 'node:test';
4
+ import { fs } from '../common.js';
5
+
6
+ suite('Extended Attributes', () => {
7
+ const testFile = 'xattr-test.txt';
8
+ const testValue = 'test value';
9
+ const testName = 'user.test';
10
+
11
+ test.before(() => fs.promises.writeFile(testFile, 'test content'));
12
+ test.after(() => fs.promises.unlink(testFile));
13
+
14
+ test('Non-user attribute set fails', () => {
15
+ assert.rejects(fs.xattr.set(testFile, 'system.test', 'value'), { code: 'EPERM' });
16
+ });
17
+
18
+ test('set and get attributes', async () => {
19
+ await fs.xattr.set(testFile, testName, testValue);
20
+ const value = await fs.xattr.get(testFile, testName, { encoding: 'utf8' });
21
+ assert.equal(value, testValue);
22
+ });
23
+
24
+ test('get attributes with buffer encoding', async () => {
25
+ await fs.xattr.set(testFile, 'user.buffer', 'buffer value');
26
+ const buffer = await fs.xattr.get(testFile, 'user.buffer', { encoding: 'buffer' });
27
+ assert(buffer instanceof Uint8Array);
28
+ assert.equal(Buffer.from(buffer).toString(), 'buffer value');
29
+ });
30
+
31
+ test('remove attributes', async () => {
32
+ await fs.xattr.set(testFile, 'user.to-remove', testValue);
33
+ await fs.xattr.remove(testFile, 'user.to-remove');
34
+
35
+ assert.rejects(fs.xattr.get(testFile, 'user.to-remove', { encoding: 'utf8' }), { code: 'ENODATA' });
36
+ });
37
+
38
+ test('list attributes', async () => {
39
+ await fs.xattr.set(testFile, 'user.list1', 'value1');
40
+ await fs.xattr.set(testFile, 'user.list2', 'value2');
41
+
42
+ const attrs = await fs.xattr.list(testFile);
43
+ assert(attrs.includes('user.list1'));
44
+ assert(attrs.includes('user.list2'));
45
+ });
46
+
47
+ test('handle create and replace options', async () => {
48
+ const flagTestName = 'user.flag-test';
49
+
50
+ await fs.xattr.set(testFile, flagTestName, 'original', { create: true });
51
+
52
+ assert.rejects(fs.xattr.set(testFile, flagTestName, 'new value', { create: true }), { code: 'EEXIST' });
53
+
54
+ await fs.xattr.set(testFile, flagTestName, 'updated', { replace: true });
55
+ const value = await fs.xattr.get(testFile, flagTestName, { encoding: 'utf8' });
56
+ assert.equal(value, 'updated');
57
+
58
+ assert.rejects(fs.xattr.set(testFile, 'user.nonexistent', 'value', { replace: true }), { code: 'ENODATA' });
59
+ });
60
+
61
+ test('file must exist', () => {
62
+ assert.rejects(fs.xattr.set('nonexistent-file.txt', testName, 'value'), { code: 'ENOENT' });
63
+ });
64
+
65
+ test('synchronous operations', () => {
66
+ const syncAttrName = 'user.sync-test';
67
+
68
+ fs.xattr.setSync(testFile, syncAttrName, testValue);
69
+ const value = fs.xattr.getSync(testFile, syncAttrName, { encoding: 'utf8' });
70
+ assert.equal(value, testValue);
71
+
72
+ fs.xattr.removeSync(testFile, syncAttrName);
73
+
74
+ assert.throws(() => fs.xattr.getSync(testFile, syncAttrName, { encoding: 'utf8' }), { code: 'ENODATA' });
75
+
76
+ const syncList1 = 'user.sync-list1';
77
+ const syncList2 = 'user.sync-list2';
78
+ fs.xattr.setSync(testFile, syncList1, 'value1');
79
+ fs.xattr.setSync(testFile, syncList2, 'value2');
80
+
81
+ const attrs = fs.xattr.listSync(testFile);
82
+ assert(attrs.includes(syncList1));
83
+ assert(attrs.includes(syncList2));
84
+ });
85
+ });
package/tests/logs.js ADDED
@@ -0,0 +1,22 @@
1
+ import * as log from '../dist/internal/log.js';
2
+
3
+ export function setupLogs(prefix) {
4
+ const { ZENFS_LOG_LEVEL } = process.env;
5
+
6
+ let level = log.Level.CRIT;
7
+
8
+ if (ZENFS_LOG_LEVEL) {
9
+ const tmp = parseInt(ZENFS_LOG_LEVEL);
10
+ if (Number.isSafeInteger(tmp)) level = tmp;
11
+ else level = ZENFS_LOG_LEVEL;
12
+ }
13
+
14
+ log.configure({
15
+ enabled: true,
16
+ format: log.fancy({ style: 'ansi', colorize: 'message' }),
17
+ dumpBacklog: true,
18
+ level,
19
+ stack: true,
20
+ output: (...msg) => (prefix ? console.error(prefix, ...msg) : console.error(...msg)),
21
+ });
22
+ }
@@ -4,6 +4,6 @@ import { copySync, data } from '../setup.js';
4
4
 
5
5
  _fs.mkdirSync('/new_root');
6
6
 
7
- export const { fs } = bindContext('/new_root');
7
+ export const { fs } = bindContext({ root: '/new_root' });
8
8
 
9
9
  copySync(data, fs);
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path/posix';
3
- import { configureSingle, InMemory, InMemoryStore, mounts, Overlay, Readonly, resolveMountConfig, StoreFS } from '../../dist/index.js';
3
+ import { configureSingle, CopyOnWrite, InMemory, InMemoryStore, mounts, Readonly, StoreFS } from '../../dist/index.js';
4
4
  import { S_IFDIR } from '../../dist/vfs/constants.js';
5
5
  import { copySync, data } from '../setup.js';
6
6
 
@@ -32,7 +32,7 @@ const readable = new MockFS();
32
32
  await readable.ready();
33
33
 
34
34
  await configureSingle({
35
- backend: Overlay,
35
+ backend: CopyOnWrite,
36
36
  readable,
37
- writable: await resolveMountConfig({ backend: InMemory, name: 'cow' }),
37
+ writable: { backend: InMemory, label: 'cow' },
38
38
  });
@@ -5,7 +5,7 @@ import { copySync, data } from '../setup.js';
5
5
  const { port1: localPort, port2: remotePort } = new MessageChannel();
6
6
 
7
7
  fs.umount('/');
8
- const tmpfs = await resolveMountConfig({ backend: InMemory, name: 'tmp' });
8
+ const tmpfs = await resolveMountConfig({ backend: InMemory, label: 'tmp' });
9
9
 
10
10
  fs.mount('/', tmpfs);
11
11
  copySync(data, fs);
@@ -1,84 +0,0 @@
1
- import type { ExtractProperties } from 'utilium';
2
- import type { Inode, InodeLike } from '../..//internal/inode.js';
3
- import type { MountConfiguration } from '../../config.js';
4
- import type { File } from '../../internal/file.js';
5
- import type { CreationOptions, UsageInfo } from '../../internal/filesystem.js';
6
- import type { Backend, FilesystemOf } from '../backend.js';
7
- import { FileSystem } from '../../internal/filesystem.js';
8
- import { Stats } from '../../stats.js';
9
- import * as RPC from './rpc.js';
10
- type FSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<any> | UsageInfo>;
11
- type FSMethod = keyof FSMethods;
12
- export type FSRequest<TMethod extends FSMethod = FSMethod> = RPC.Message & {
13
- [M in TMethod]: {
14
- method: M;
15
- args: Parameters<FSMethods[M]>;
16
- };
17
- }[TMethod];
18
- declare const PortFS_base: import("../../index.js").Mixin<typeof FileSystem, import("../../mixins/async.js").AsyncMixin>;
19
- /**
20
- * PortFS lets you access an FS instance that is running in a port, or the other way around.
21
- *
22
- * Note that *direct* synchronous operations are not permitted on the PortFS,
23
- * regardless of the configuration option of the remote FS.
24
- */
25
- export declare class PortFS extends PortFS_base {
26
- readonly options: RPC.Options;
27
- readonly port: RPC.Port;
28
- /**`
29
- * @hidden
30
- */
31
- _sync: import("../index.js").StoreFS<import("../memory.js").InMemoryStore>;
32
- /**
33
- * Constructs a new PortFS instance that connects with the FS running on `options.port`.
34
- */
35
- constructor(options: RPC.Options);
36
- protected rpc<const T extends FSMethod>(method: T, ...args: Parameters<FSMethods[T]>): Promise<Awaited<ReturnType<FSMethods[T]>>>;
37
- ready(): Promise<void>;
38
- rename(oldPath: string, newPath: string): Promise<void>;
39
- stat(path: string): Promise<Stats>;
40
- sync(path: string, data: Uint8Array | undefined, stats: Readonly<InodeLike | Inode>): Promise<void>;
41
- openFile(path: string, flag: string): Promise<File>;
42
- createFile(path: string, flag: string, mode: number, options: CreationOptions): Promise<File>;
43
- unlink(path: string): Promise<void>;
44
- rmdir(path: string): Promise<void>;
45
- mkdir(path: string, mode: number, options: CreationOptions): Promise<void>;
46
- readdir(path: string): Promise<string[]>;
47
- exists(path: string): Promise<boolean>;
48
- link(srcpath: string, dstpath: string): Promise<void>;
49
- read(path: string, buffer: Uint8Array, offset: number, length: number): Promise<void>;
50
- write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
51
- }
52
- /** @internal */
53
- export declare function handleRequest(port: RPC.Port, fs: FileSystem & {
54
- _descriptors?: Map<number, File>;
55
- }, request: FSRequest): Promise<void>;
56
- export declare function attachFS(port: RPC.Port, fs: FileSystem): void;
57
- export declare function detachFS(port: RPC.Port, fs: FileSystem): void;
58
- declare const _Port: {
59
- name: string;
60
- options: {
61
- port: {
62
- type: "object";
63
- required: true;
64
- validator(port: RPC.Port): void;
65
- };
66
- timeout: {
67
- type: "number";
68
- required: false;
69
- };
70
- };
71
- create(options: RPC.Options): PortFS;
72
- };
73
- type _Port = typeof _Port;
74
- export interface Port extends _Port {
75
- }
76
- /**
77
- * @category Backends and Configuration
78
- */
79
- export declare const Port: Port;
80
- /**
81
- * @category Backends and Configuration
82
- */
83
- export declare function resolveRemoteMount<T extends Backend>(port: RPC.Port, config: MountConfiguration<T>, _depth?: number): Promise<FilesystemOf<T>>;
84
- export {};
@@ -1,151 +0,0 @@
1
- import { pick } from 'utilium';
2
- import { resolveMountConfig } from '../../config.js';
3
- import { Errno, ErrnoError } from '../../internal/error.js';
4
- import { FileSystem } from '../../internal/filesystem.js';
5
- import { err, info } from '../../internal/log.js';
6
- import { Async } from '../../mixins/async.js';
7
- import { Stats } from '../../stats.js';
8
- import { InMemory } from '../memory.js';
9
- import * as RPC from './rpc.js';
10
- /**
11
- * PortFS lets you access an FS instance that is running in a port, or the other way around.
12
- *
13
- * Note that *direct* synchronous operations are not permitted on the PortFS,
14
- * regardless of the configuration option of the remote FS.
15
- */
16
- export class PortFS extends Async(FileSystem) {
17
- /**
18
- * Constructs a new PortFS instance that connects with the FS running on `options.port`.
19
- */
20
- constructor(options) {
21
- super(0x706f7274, 'portfs');
22
- this.options = options;
23
- /**`
24
- * @hidden
25
- */
26
- this._sync = InMemory.create({ name: 'tmpfs:port' });
27
- this.port = options.port;
28
- RPC.attach(this.port, RPC.handleResponse);
29
- }
30
- rpc(method, ...args) {
31
- return RPC.request({ method, args }, {
32
- ...this.options,
33
- fs: this,
34
- });
35
- }
36
- async ready() {
37
- await this.rpc('ready');
38
- await super.ready();
39
- }
40
- rename(oldPath, newPath) {
41
- return this.rpc('rename', oldPath, newPath);
42
- }
43
- async stat(path) {
44
- return new Stats(await this.rpc('stat', path));
45
- }
46
- sync(path, data, stats) {
47
- stats = 'toJSON' in stats ? stats.toJSON() : stats;
48
- return this.rpc('sync', path, data, stats);
49
- }
50
- openFile(path, flag) {
51
- return this.rpc('openFile', path, flag);
52
- }
53
- createFile(path, flag, mode, options) {
54
- return this.rpc('createFile', path, flag, mode, options);
55
- }
56
- unlink(path) {
57
- return this.rpc('unlink', path);
58
- }
59
- rmdir(path) {
60
- return this.rpc('rmdir', path);
61
- }
62
- mkdir(path, mode, options) {
63
- return this.rpc('mkdir', path, mode, options);
64
- }
65
- readdir(path) {
66
- return this.rpc('readdir', path);
67
- }
68
- exists(path) {
69
- return this.rpc('exists', path);
70
- }
71
- link(srcpath, dstpath) {
72
- return this.rpc('link', srcpath, dstpath);
73
- }
74
- async read(path, buffer, offset, length) {
75
- const _buf = (await this.rpc('read', path, buffer, offset, length));
76
- buffer.set(_buf);
77
- }
78
- write(path, buffer, offset) {
79
- return this.rpc('write', path, buffer, offset);
80
- }
81
- }
82
- /** @internal */
83
- export async function handleRequest(port, fs, request) {
84
- if (!RPC.isMessage(request))
85
- return;
86
- const { method, args, id, stack } = request;
87
- let value, error = false;
88
- try {
89
- // @ts-expect-error 2556
90
- value = await fs[method](...args);
91
- switch (method) {
92
- case 'openFile':
93
- case 'createFile': {
94
- value = {
95
- path: args[0],
96
- flag: args[1],
97
- stats: await fs.stat(args[0]),
98
- };
99
- break;
100
- }
101
- case 'read':
102
- value = args[1];
103
- break;
104
- }
105
- }
106
- catch (e) {
107
- value = e instanceof ErrnoError ? e.toJSON() : pick(e, 'message', 'stack');
108
- error = true;
109
- }
110
- port.postMessage({ _zenfs: true, id, error, method, stack, value });
111
- }
112
- export function attachFS(port, fs) {
113
- RPC.attach(port, request => handleRequest(port, fs, request));
114
- }
115
- export function detachFS(port, fs) {
116
- RPC.detach(port, request => handleRequest(port, fs, request));
117
- }
118
- const _Port = {
119
- name: 'Port',
120
- options: {
121
- port: {
122
- type: 'object',
123
- required: true,
124
- validator(port) {
125
- // Check for a `postMessage` function.
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'));
128
- }
129
- },
130
- },
131
- timeout: { type: 'number', required: false },
132
- },
133
- create(options) {
134
- return new PortFS(options);
135
- },
136
- };
137
- /**
138
- * @category Backends and Configuration
139
- */
140
- export const Port = _Port;
141
- /**
142
- * @category Backends and Configuration
143
- */
144
- export async function resolveRemoteMount(port, config, _depth = 0) {
145
- const stopAndReplay = RPC.catchMessages(port);
146
- const fs = await resolveMountConfig(config, _depth);
147
- attachFS(port, fs);
148
- await stopAndReplay(fs);
149
- info('Resolved remote mount: ' + fs.toString());
150
- return fs;
151
- }