memfs 4.0.0 → 4.1.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 (105) hide show
  1. package/LICENSE +201 -24
  2. package/README.md +21 -92
  3. package/lib/Dirent.d.ts +2 -1
  4. package/lib/__tests__/util.d.ts +13 -0
  5. package/lib/__tests__/util.js +29 -0
  6. package/lib/consts/AMODE.d.ts +9 -0
  7. package/lib/consts/FLAG.d.ts +22 -0
  8. package/lib/consts/FLAG.js +2 -0
  9. package/lib/fsa/types.d.ts +3 -3
  10. package/lib/fsa-to-node/FsaNodeCore.d.ts +32 -0
  11. package/lib/fsa-to-node/FsaNodeCore.js +168 -0
  12. package/lib/fsa-to-node/FsaNodeDirent.d.ts +13 -0
  13. package/lib/fsa-to-node/FsaNodeDirent.js +31 -0
  14. package/lib/fsa-to-node/FsaNodeFs.d.ts +171 -0
  15. package/lib/fsa-to-node/FsaNodeFs.js +842 -0
  16. package/lib/fsa-to-node/FsaNodeFsOpenFile.d.ts +24 -0
  17. package/lib/fsa-to-node/FsaNodeFsOpenFile.js +46 -0
  18. package/lib/fsa-to-node/FsaNodeReadStream.d.ts +24 -0
  19. package/lib/fsa-to-node/FsaNodeReadStream.js +100 -0
  20. package/lib/fsa-to-node/FsaNodeStats.d.ts +30 -0
  21. package/lib/fsa-to-node/FsaNodeStats.js +52 -0
  22. package/lib/fsa-to-node/FsaNodeWriteStream.d.ts +48 -0
  23. package/lib/fsa-to-node/FsaNodeWriteStream.js +162 -0
  24. package/lib/fsa-to-node/__tests__/FsaNodeFs.test.d.ts +1 -0
  25. package/lib/fsa-to-node/__tests__/FsaNodeFs.test.js +873 -0
  26. package/lib/fsa-to-node/__tests__/util.test.d.ts +1 -0
  27. package/lib/fsa-to-node/__tests__/util.test.js +28 -0
  28. package/lib/fsa-to-node/constants.d.ts +3 -0
  29. package/lib/fsa-to-node/constants.js +2 -0
  30. package/lib/fsa-to-node/index.d.ts +2 -0
  31. package/lib/fsa-to-node/index.js +7 -0
  32. package/lib/fsa-to-node/json.d.ts +4 -0
  33. package/lib/fsa-to-node/json.js +7 -0
  34. package/lib/fsa-to-node/types.d.ts +37 -0
  35. package/lib/fsa-to-node/types.js +2 -0
  36. package/lib/fsa-to-node/util.d.ts +4 -0
  37. package/lib/fsa-to-node/util.js +40 -0
  38. package/lib/fsa-to-node/worker/FsaNodeSyncAdapterWorker.d.ts +10 -0
  39. package/lib/fsa-to-node/worker/FsaNodeSyncAdapterWorker.js +72 -0
  40. package/lib/fsa-to-node/worker/FsaNodeSyncWorker.d.ts +20 -0
  41. package/lib/fsa-to-node/worker/FsaNodeSyncWorker.js +190 -0
  42. package/lib/fsa-to-node/worker/SyncMessenger.d.ts +24 -0
  43. package/lib/fsa-to-node/worker/SyncMessenger.js +80 -0
  44. package/lib/fsa-to-node/worker/constants.d.ts +8 -0
  45. package/lib/fsa-to-node/worker/constants.js +2 -0
  46. package/lib/fsa-to-node/worker/types.d.ts +17 -0
  47. package/lib/fsa-to-node/worker/types.js +2 -0
  48. package/lib/index.d.ts +5 -4
  49. package/lib/index.js +1 -1
  50. package/lib/node/constants.d.ts +41 -0
  51. package/lib/node/constants.js +56 -0
  52. package/lib/node/options.d.ts +23 -0
  53. package/lib/node/options.js +94 -0
  54. package/lib/node/promises.d.ts +1 -1
  55. package/lib/node/promises.js +1 -3
  56. package/lib/node/types/FsCommonObjects.d.ts +17 -0
  57. package/lib/node/types/FsCommonObjects.js +2 -0
  58. package/lib/node/types/{sync.d.ts → FsSynchronousApi.d.ts} +36 -37
  59. package/lib/node/types/FsSynchronousApi.js +2 -0
  60. package/lib/node/types/callback.d.ts +57 -58
  61. package/lib/node/types/index.d.ts +1 -1
  62. package/lib/node/types/misc.d.ts +6 -8
  63. package/lib/node/types/options.d.ts +20 -6
  64. package/lib/node/types/promises.d.ts +2 -2
  65. package/lib/node/util.d.ts +19 -0
  66. package/lib/node/util.js +265 -1
  67. package/lib/node-to-fsa/NodeFileSystemDirectoryHandle.d.ts +71 -0
  68. package/lib/node-to-fsa/NodeFileSystemDirectoryHandle.js +264 -0
  69. package/lib/node-to-fsa/NodeFileSystemFileHandle.d.ts +28 -0
  70. package/lib/node-to-fsa/NodeFileSystemFileHandle.js +73 -0
  71. package/lib/node-to-fsa/NodeFileSystemHandle.d.ts +33 -0
  72. package/lib/node-to-fsa/NodeFileSystemHandle.js +53 -0
  73. package/lib/node-to-fsa/NodeFileSystemSyncAccessHandle.d.ts +42 -0
  74. package/lib/node-to-fsa/NodeFileSystemSyncAccessHandle.js +116 -0
  75. package/lib/node-to-fsa/NodeFileSystemWritableFileStream.d.ts +54 -0
  76. package/lib/node-to-fsa/NodeFileSystemWritableFileStream.js +195 -0
  77. package/lib/node-to-fsa/NodePermissionStatus.d.ts +8 -0
  78. package/lib/node-to-fsa/NodePermissionStatus.js +13 -0
  79. package/lib/node-to-fsa/__tests__/NodeFileSystemDirectoryHandle.test.d.ts +1 -0
  80. package/lib/node-to-fsa/__tests__/NodeFileSystemDirectoryHandle.test.js +627 -0
  81. package/lib/node-to-fsa/__tests__/NodeFileSystemFileHandle.test.d.ts +1 -0
  82. package/lib/node-to-fsa/__tests__/NodeFileSystemFileHandle.test.js +191 -0
  83. package/lib/node-to-fsa/__tests__/NodeFileSystemHandle.test.d.ts +1 -0
  84. package/lib/node-to-fsa/__tests__/NodeFileSystemHandle.test.js +49 -0
  85. package/lib/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.d.ts +1 -0
  86. package/lib/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.js +183 -0
  87. package/lib/node-to-fsa/__tests__/NodeFileSystemWritableFileStream.test.d.ts +1 -0
  88. package/lib/node-to-fsa/__tests__/NodeFileSystemWritableFileStream.test.js +106 -0
  89. package/lib/node-to-fsa/__tests__/scenarios.test.d.ts +1 -0
  90. package/lib/node-to-fsa/__tests__/scenarios.test.js +46 -0
  91. package/lib/node-to-fsa/__tests__/util.test.d.ts +1 -0
  92. package/lib/node-to-fsa/__tests__/util.test.js +25 -0
  93. package/lib/node-to-fsa/index.d.ts +7 -0
  94. package/lib/node-to-fsa/index.js +26 -0
  95. package/lib/node-to-fsa/types.d.ts +12 -0
  96. package/lib/node-to-fsa/types.js +2 -0
  97. package/lib/node-to-fsa/util.d.ts +11 -0
  98. package/lib/node-to-fsa/util.js +33 -0
  99. package/lib/node.d.ts +1 -1
  100. package/lib/volume.d.ts +45 -126
  101. package/lib/volume.js +204 -562
  102. package/lib/webfs/index.d.ts +1 -0
  103. package/lib/webfs/index.js +22 -0
  104. package/package.json +28 -5
  105. /package/lib/{node/types/sync.js → consts/AMODE.js} +0 -0
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const util_1 = require("../util");
4
+ describe('pathToLocation()', () => {
5
+ test('handles an empty string', () => {
6
+ expect((0, util_1.pathToLocation)('')).toStrictEqual([[], '']);
7
+ });
8
+ test('handles a single slash', () => {
9
+ expect((0, util_1.pathToLocation)('/')).toStrictEqual([[], '']);
10
+ });
11
+ test('no path, just filename', () => {
12
+ expect((0, util_1.pathToLocation)('scary.exe')).toStrictEqual([[], 'scary.exe']);
13
+ });
14
+ test('multiple steps in the path', () => {
15
+ expect((0, util_1.pathToLocation)('/gg/wp/hf/gl.txt')).toStrictEqual([['gg', 'wp', 'hf'], 'gl.txt']);
16
+ expect((0, util_1.pathToLocation)('gg/wp/hf/gl.txt')).toStrictEqual([['gg', 'wp', 'hf'], 'gl.txt']);
17
+ expect((0, util_1.pathToLocation)('/wp/hf/gl.txt')).toStrictEqual([['wp', 'hf'], 'gl.txt']);
18
+ expect((0, util_1.pathToLocation)('wp/hf/gl.txt')).toStrictEqual([['wp', 'hf'], 'gl.txt']);
19
+ expect((0, util_1.pathToLocation)('/hf/gl.txt')).toStrictEqual([['hf'], 'gl.txt']);
20
+ expect((0, util_1.pathToLocation)('hf/gl.txt')).toStrictEqual([['hf'], 'gl.txt']);
21
+ expect((0, util_1.pathToLocation)('/gl.txt')).toStrictEqual([[], 'gl.txt']);
22
+ expect((0, util_1.pathToLocation)('gl.txt')).toStrictEqual([[], 'gl.txt']);
23
+ });
24
+ test('handles double slashes', () => {
25
+ expect((0, util_1.pathToLocation)('/gg/wp//hf/gl.txt')).toStrictEqual([['gg', 'wp', '', 'hf'], 'gl.txt']);
26
+ expect((0, util_1.pathToLocation)('//gl.txt')).toStrictEqual([[''], 'gl.txt']);
27
+ });
28
+ });
@@ -0,0 +1,3 @@
1
+ export declare const enum FsaToNodeConstants {
2
+ Separator = "/"
3
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export { FsaNodeFs } from './FsaNodeFs';
2
+ export { FsaNodeSyncAdapterWorker } from './worker/FsaNodeSyncAdapterWorker';
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FsaNodeSyncAdapterWorker = exports.FsaNodeFs = void 0;
4
+ var FsaNodeFs_1 = require("./FsaNodeFs");
5
+ Object.defineProperty(exports, "FsaNodeFs", { enumerable: true, get: function () { return FsaNodeFs_1.FsaNodeFs; } });
6
+ var FsaNodeSyncAdapterWorker_1 = require("./worker/FsaNodeSyncAdapterWorker");
7
+ Object.defineProperty(exports, "FsaNodeSyncAdapterWorker", { enumerable: true, get: function () { return FsaNodeSyncAdapterWorker_1.FsaNodeSyncAdapterWorker; } });
@@ -0,0 +1,4 @@
1
+ import { CborEncoder } from 'json-joy/es6/json-pack/cbor/CborEncoder';
2
+ import { CborDecoder } from 'json-joy/es6/json-pack/cbor/CborDecoder';
3
+ export declare const encoder: CborEncoder;
4
+ export declare const decoder: CborDecoder;
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.decoder = exports.encoder = void 0;
4
+ const CborEncoder_1 = require("json-joy/es6/json-pack/cbor/CborEncoder");
5
+ const CborDecoder_1 = require("json-joy/es6/json-pack/cbor/CborDecoder");
6
+ exports.encoder = new CborEncoder_1.CborEncoder();
7
+ exports.decoder = new CborDecoder_1.CborDecoder();
@@ -0,0 +1,37 @@
1
+ import type { IFileSystemFileHandle } from '../fsa/types';
2
+ import type * as opts from '../node/types/options';
3
+ import type * as misc from '../node/types/misc';
4
+ export type FsLocation = [folder: string[], file: string];
5
+ /**
6
+ * Adapter which implements synchronous calls to the FSA API.
7
+ */
8
+ export interface FsaNodeSyncAdapterApi {
9
+ stat(location: FsLocation): FsaNodeSyncAdapterStats;
10
+ access(req: [filename: string, mode: number]): void;
11
+ readFile(req: [filename: string, opts?: opts.IReadFileOptions]): Uint8Array;
12
+ writeFile(req: [filename: string, data: Uint8Array, opts?: opts.IWriteFileOptions]): void;
13
+ appendFile(req: [filename: string, data: Uint8Array, opts?: opts.IAppendFileOptions]): void;
14
+ copy(req: [src: string, dst: string, flags?: number]): void;
15
+ move(req: [src: string, dst: string]): void;
16
+ rmdir(req: [filename: string, opts?: opts.IRmdirOptions]): void;
17
+ rm(req: [filename: string, opts?: opts.IRmOptions]): void;
18
+ mkdir(req: [filename: string, opts?: misc.TMode | opts.IMkdirOptions]): string | undefined;
19
+ mkdtemp(req: [filename: string, opts?: misc.TMode | opts.IOptions]): string;
20
+ trunc(req: [filename: string, len: number]): void;
21
+ unlink(req: [filename: string]): void;
22
+ readdir(req: [filename: string]): FsaNodeSyncAdapterEntry[];
23
+ read(req: [filename: string, position: number, length: number]): Uint8Array;
24
+ write(req: [filename: string, data: Uint8Array, position: number | null]): number;
25
+ open(req: [filename: string, flags: number, mode: number]): IFileSystemFileHandle;
26
+ }
27
+ export interface FsaNodeSyncAdapter {
28
+ call<K extends keyof FsaNodeSyncAdapterApi>(method: K, payload: Parameters<FsaNodeSyncAdapterApi[K]>[0]): ReturnType<FsaNodeSyncAdapterApi[K]>;
29
+ }
30
+ export interface FsaNodeSyncAdapterStats {
31
+ kind: 'file' | 'directory';
32
+ size?: number;
33
+ }
34
+ export interface FsaNodeSyncAdapterEntry {
35
+ kind: 'file' | 'directory';
36
+ name: string;
37
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import { IFileSystemDirectoryHandle } from '../fsa/types';
2
+ import type { FsLocation } from './types';
3
+ export declare const pathToLocation: (path: string) => FsLocation;
4
+ export declare const testDirectoryIsWritable: (dir: IFileSystemDirectoryHandle) => Promise<boolean>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.testDirectoryIsWritable = exports.pathToLocation = void 0;
13
+ const pathToLocation = (path) => {
14
+ if (path[0] === "/" /* FsaToNodeConstants.Separator */)
15
+ path = path.slice(1);
16
+ const lastSlashIndex = path.lastIndexOf("/" /* FsaToNodeConstants.Separator */);
17
+ if (lastSlashIndex === -1)
18
+ return [[], path];
19
+ const file = path.slice(lastSlashIndex + 1);
20
+ const folder = path.slice(0, lastSlashIndex).split("/" /* FsaToNodeConstants.Separator */);
21
+ return [folder, file];
22
+ };
23
+ exports.pathToLocation = pathToLocation;
24
+ const testDirectoryIsWritable = (dir) => __awaiter(void 0, void 0, void 0, function* () {
25
+ const testFileName = '__memfs_writable_test_file_' + Math.random().toString(36).slice(2) + Date.now();
26
+ try {
27
+ yield dir.getFileHandle(testFileName, { create: true });
28
+ return true;
29
+ }
30
+ catch (_a) {
31
+ return false;
32
+ }
33
+ finally {
34
+ try {
35
+ yield dir.removeEntry(testFileName);
36
+ }
37
+ catch (e) { }
38
+ }
39
+ });
40
+ exports.testDirectoryIsWritable = testDirectoryIsWritable;
@@ -0,0 +1,10 @@
1
+ import { SyncMessenger } from './SyncMessenger';
2
+ import type * as fsa from '../../fsa/types';
3
+ import type { FsaNodeSyncAdapter, FsaNodeSyncAdapterApi } from '../types';
4
+ export declare class FsaNodeSyncAdapterWorker implements FsaNodeSyncAdapter {
5
+ protected readonly messenger: SyncMessenger;
6
+ protected readonly root: fsa.IFileSystemDirectoryHandle;
7
+ static start(url: string, dir: fsa.IFileSystemDirectoryHandle | Promise<fsa.IFileSystemDirectoryHandle>): Promise<FsaNodeSyncAdapterWorker>;
8
+ constructor(messenger: SyncMessenger, root: fsa.IFileSystemDirectoryHandle);
9
+ call<K extends keyof FsaNodeSyncAdapterApi>(method: K, payload: Parameters<FsaNodeSyncAdapterApi[K]>[0]): ReturnType<FsaNodeSyncAdapterApi[K]>;
10
+ }
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.FsaNodeSyncAdapterWorker = void 0;
13
+ const Defer_1 = require("thingies/es6/Defer");
14
+ const SyncMessenger_1 = require("./SyncMessenger");
15
+ const json_1 = require("../json");
16
+ let rootId = 0;
17
+ class FsaNodeSyncAdapterWorker {
18
+ static start(url, dir) {
19
+ return __awaiter(this, void 0, void 0, function* () {
20
+ const worker = new Worker(url);
21
+ const future = new Defer_1.Defer();
22
+ let id = rootId++;
23
+ let messenger = undefined;
24
+ const _dir = yield dir;
25
+ worker.onmessage = e => {
26
+ const data = e.data;
27
+ if (!Array.isArray(data))
28
+ return;
29
+ const msg = data;
30
+ const code = msg[0];
31
+ switch (code) {
32
+ case 0 /* FsaNodeWorkerMessageCode.Init */: {
33
+ const [, sab] = msg;
34
+ messenger = new SyncMessenger_1.SyncMessenger(sab);
35
+ const setRootMessage = [1 /* FsaNodeWorkerMessageCode.SetRoot */, id, _dir];
36
+ worker.postMessage(setRootMessage);
37
+ break;
38
+ }
39
+ case 2 /* FsaNodeWorkerMessageCode.RootSet */: {
40
+ const [, rootId] = msg;
41
+ if (id !== rootId)
42
+ return;
43
+ const adapter = new FsaNodeSyncAdapterWorker(messenger, _dir);
44
+ future.resolve(adapter);
45
+ break;
46
+ }
47
+ }
48
+ };
49
+ return yield future.promise;
50
+ });
51
+ }
52
+ constructor(messenger, root) {
53
+ this.messenger = messenger;
54
+ this.root = root;
55
+ }
56
+ call(method, payload) {
57
+ const request = [3 /* FsaNodeWorkerMessageCode.Request */, method, payload];
58
+ const encoded = json_1.encoder.encode(request);
59
+ const encodedResponse = this.messenger.callSync(encoded);
60
+ const [code, data] = json_1.decoder.decode(encodedResponse);
61
+ switch (code) {
62
+ case 4 /* FsaNodeWorkerMessageCode.Response */:
63
+ return data;
64
+ case 5 /* FsaNodeWorkerMessageCode.ResponseError */:
65
+ throw data;
66
+ default: {
67
+ throw new Error('Invalid response message code');
68
+ }
69
+ }
70
+ }
71
+ }
72
+ exports.FsaNodeSyncAdapterWorker = FsaNodeSyncAdapterWorker;
@@ -0,0 +1,20 @@
1
+ import { AsyncCallback, SyncMessenger } from './SyncMessenger';
2
+ import { FsaNodeFs } from '../FsaNodeFs';
3
+ import type * as fsa from '../../fsa/types';
4
+ import type { FsaNodeWorkerMsg } from './types';
5
+ import type { FsaNodeSyncAdapterApi } from '../types';
6
+ export declare class FsaNodeSyncWorker {
7
+ protected readonly sab: SharedArrayBuffer;
8
+ protected readonly messenger: SyncMessenger;
9
+ protected root: fsa.IFileSystemDirectoryHandle;
10
+ protected fs: FsaNodeFs;
11
+ start(): void;
12
+ protected onPostMessage: (msg: FsaNodeWorkerMsg) => void;
13
+ protected readonly onRequest: AsyncCallback;
14
+ protected getDir(path: string[], create: boolean, funcName?: string): Promise<fsa.IFileSystemDirectoryHandle>;
15
+ protected getFile(path: string[], name: string, funcName?: string, create?: boolean): Promise<fsa.IFileSystemFileHandle>;
16
+ protected getFileOrDir(path: string[], name: string, funcName?: string, create?: boolean): Promise<fsa.IFileSystemFileHandle | fsa.IFileSystemDirectoryHandle>;
17
+ protected handlers: {
18
+ [K in keyof FsaNodeSyncAdapterApi]: (request: Parameters<FsaNodeSyncAdapterApi[K]>[0]) => Promise<ReturnType<FsaNodeSyncAdapterApi[K]>>;
19
+ };
20
+ }
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.FsaNodeSyncWorker = void 0;
13
+ const SyncMessenger_1 = require("./SyncMessenger");
14
+ const FsaNodeFs_1 = require("../FsaNodeFs");
15
+ const json_1 = require("../json");
16
+ class FsaNodeSyncWorker {
17
+ constructor() {
18
+ this.sab = new SharedArrayBuffer(1024 * 1024);
19
+ this.messenger = new SyncMessenger_1.SyncMessenger(this.sab);
20
+ this.onPostMessage = (msg) => {
21
+ switch (msg[0]) {
22
+ case 1 /* FsaNodeWorkerMessageCode.SetRoot */: {
23
+ const [, id, dir] = msg;
24
+ this.root = dir;
25
+ this.fs = new FsaNodeFs_1.FsaNodeFs(this.root);
26
+ const response = [2 /* FsaNodeWorkerMessageCode.RootSet */, id];
27
+ postMessage(response);
28
+ this.messenger.serveAsync(this.onRequest);
29
+ break;
30
+ }
31
+ }
32
+ };
33
+ this.onRequest = (request) => __awaiter(this, void 0, void 0, function* () {
34
+ try {
35
+ const message = json_1.decoder.decode(request);
36
+ if (!Array.isArray(message))
37
+ throw new Error('Invalid message format');
38
+ const code = message[0];
39
+ if (code !== 3 /* FsaNodeWorkerMessageCode.Request */)
40
+ throw new Error('Invalid message code');
41
+ const [, method, payload] = message;
42
+ const handler = this.handlers[method];
43
+ if (!handler)
44
+ throw new Error(`Unknown method ${method}`);
45
+ const response = yield handler(payload);
46
+ return json_1.encoder.encode([4 /* FsaNodeWorkerMessageCode.Response */, response]);
47
+ }
48
+ catch (err) {
49
+ const message = err && typeof err === 'object' && err.message ? err.message : 'Unknown error';
50
+ const error = { message };
51
+ if (err && typeof err === 'object' && (err.code || err.name))
52
+ error.code = err.code || err.name;
53
+ return json_1.encoder.encode([5 /* FsaNodeWorkerMessageCode.ResponseError */, error]);
54
+ }
55
+ });
56
+ this.handlers = {
57
+ stat: (location) => __awaiter(this, void 0, void 0, function* () {
58
+ const handle = yield this.getFileOrDir(location[0], location[1], 'statSync');
59
+ return {
60
+ kind: handle.kind,
61
+ };
62
+ }),
63
+ access: ([filename, mode]) => __awaiter(this, void 0, void 0, function* () {
64
+ yield this.fs.promises.access(filename, mode);
65
+ }),
66
+ readFile: ([filename, opts]) => __awaiter(this, void 0, void 0, function* () {
67
+ const buf = (yield this.fs.promises.readFile(filename, Object.assign(Object.assign({}, opts), { encoding: 'buffer' })));
68
+ const uint8 = new Uint8Array(buf, buf.byteOffset, buf.byteLength);
69
+ return uint8;
70
+ }),
71
+ writeFile: ([filename, data, opts]) => __awaiter(this, void 0, void 0, function* () {
72
+ yield this.fs.promises.writeFile(filename, data, Object.assign(Object.assign({}, opts), { encoding: 'buffer' }));
73
+ }),
74
+ appendFile: ([filename, data, opts]) => __awaiter(this, void 0, void 0, function* () {
75
+ yield this.fs.promises.appendFile(filename, data, Object.assign(Object.assign({}, opts), { encoding: 'buffer' }));
76
+ }),
77
+ copy: ([src, dst, flags]) => __awaiter(this, void 0, void 0, function* () {
78
+ yield this.fs.promises.copyFile(src, dst, flags);
79
+ }),
80
+ move: ([src, dst]) => __awaiter(this, void 0, void 0, function* () {
81
+ yield this.fs.promises.rename(src, dst);
82
+ }),
83
+ rmdir: ([filename, options]) => __awaiter(this, void 0, void 0, function* () {
84
+ yield this.fs.promises.rmdir(filename, options);
85
+ }),
86
+ rm: ([filename, options]) => __awaiter(this, void 0, void 0, function* () {
87
+ yield this.fs.promises.rm(filename, options);
88
+ }),
89
+ mkdir: ([filename, options]) => __awaiter(this, void 0, void 0, function* () {
90
+ return yield this.fs.promises.mkdir(filename, options);
91
+ }),
92
+ mkdtemp: ([filename]) => __awaiter(this, void 0, void 0, function* () {
93
+ return (yield this.fs.promises.mkdtemp(filename, { encoding: 'utf8' }));
94
+ }),
95
+ trunc: ([filename, len]) => __awaiter(this, void 0, void 0, function* () {
96
+ yield this.fs.promises.truncate(filename, len);
97
+ }),
98
+ unlink: ([filename]) => __awaiter(this, void 0, void 0, function* () {
99
+ yield this.fs.promises.unlink(filename);
100
+ }),
101
+ readdir: ([filename]) => __awaiter(this, void 0, void 0, function* () {
102
+ const list = (yield this.fs.promises.readdir(filename, { withFileTypes: true, encoding: 'utf8' }));
103
+ const res = list.map(entry => ({
104
+ kind: entry.isDirectory() ? 'directory' : 'file',
105
+ name: entry.name,
106
+ }));
107
+ return res;
108
+ }),
109
+ read: ([filename, position, length]) => __awaiter(this, void 0, void 0, function* () {
110
+ let uint8 = new Uint8Array(length);
111
+ const handle = yield this.fs.promises.open(filename, 'r');
112
+ const bytesRead = yield new Promise((resolve, reject) => {
113
+ this.fs.read(handle.fd, uint8, 0, length, position, (err, bytesRead) => {
114
+ if (err)
115
+ return reject(err);
116
+ resolve(bytesRead || length);
117
+ });
118
+ });
119
+ if (bytesRead < length)
120
+ uint8 = uint8.slice(0, bytesRead);
121
+ return uint8;
122
+ }),
123
+ write: ([filename, data, position]) => __awaiter(this, void 0, void 0, function* () {
124
+ const handle = yield this.fs.promises.open(filename, 'a');
125
+ const { bytesWritten } = yield handle.write(data, 0, data.length, position || undefined);
126
+ return bytesWritten;
127
+ }),
128
+ open: ([filename, flags, mode]) => __awaiter(this, void 0, void 0, function* () {
129
+ const handle = yield this.fs.promises.open(filename, flags, mode);
130
+ const file = yield this.fs.__getFileById(handle.fd);
131
+ yield handle.close();
132
+ return file;
133
+ }),
134
+ };
135
+ }
136
+ start() {
137
+ onmessage = e => {
138
+ if (!Array.isArray(e.data))
139
+ return;
140
+ this.onPostMessage(e.data);
141
+ };
142
+ const initMsg = [0 /* FsaNodeWorkerMessageCode.Init */, this.sab];
143
+ postMessage(initMsg);
144
+ }
145
+ getDir(path, create, funcName) {
146
+ return __awaiter(this, void 0, void 0, function* () {
147
+ let curr = this.root;
148
+ const options = { create };
149
+ try {
150
+ for (const name of path) {
151
+ curr = yield curr.getDirectoryHandle(name, options);
152
+ }
153
+ }
154
+ catch (error) {
155
+ // if (error && typeof error === 'object' && error.name === 'TypeMismatchError')
156
+ // throw createError('ENOTDIR', funcName, path.join(FsaToNodeConstants.Separator));
157
+ throw error;
158
+ }
159
+ return curr;
160
+ });
161
+ }
162
+ getFile(path, name, funcName, create) {
163
+ return __awaiter(this, void 0, void 0, function* () {
164
+ const dir = yield this.getDir(path, false, funcName);
165
+ const file = yield dir.getFileHandle(name, { create });
166
+ return file;
167
+ });
168
+ }
169
+ getFileOrDir(path, name, funcName, create) {
170
+ return __awaiter(this, void 0, void 0, function* () {
171
+ const dir = yield this.getDir(path, false, funcName);
172
+ try {
173
+ const file = yield dir.getFileHandle(name);
174
+ return file;
175
+ }
176
+ catch (error) {
177
+ if (error && typeof error === 'object') {
178
+ switch (error.name) {
179
+ case 'TypeMismatchError':
180
+ return yield dir.getDirectoryHandle(name);
181
+ // case 'NotFoundError':
182
+ // throw createError('ENOENT', funcName, path.join(FsaToNodeConstants.Separator));
183
+ }
184
+ }
185
+ throw error;
186
+ }
187
+ });
188
+ }
189
+ }
190
+ exports.FsaNodeSyncWorker = FsaNodeSyncWorker;
@@ -0,0 +1,24 @@
1
+ export type AsyncCallback = (request: Uint8Array) => Promise<Uint8Array>;
2
+ /**
3
+ * `SyncMessenger` allows to execute asynchronous code synchronously. The
4
+ * asynchronous code is executed in a Worker thread, while the main thread is
5
+ * blocked until the asynchronous code is finished.
6
+ *
7
+ * First four 4-byte words is the header, where the first word is used for Atomics
8
+ * notifications. The second word is used for spin-locking the main thread until
9
+ * the asynchronous code is finished. The third word is used to specify payload
10
+ * length. The fourth word is currently unused.
11
+ *
12
+ * The maximum payload size is the size of the SharedArrayBuffer minus the
13
+ * header size.
14
+ */
15
+ export declare class SyncMessenger {
16
+ protected readonly sab: SharedArrayBuffer;
17
+ protected readonly int32: Int32Array;
18
+ protected readonly uint8: Uint8Array;
19
+ protected readonly headerSize: any;
20
+ protected readonly dataSize: any;
21
+ constructor(sab: SharedArrayBuffer);
22
+ callSync(data: Uint8Array): Uint8Array;
23
+ serveAsync(callback: AsyncCallback): void;
24
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SyncMessenger = void 0;
13
+ /**
14
+ * @param condition Condition to wait for, when true, the function returns.
15
+ * @param ms Maximum time to wait in milliseconds.
16
+ */
17
+ const sleepUntil = (condition, ms = 100) => {
18
+ const start = Date.now();
19
+ while (!condition()) {
20
+ const now = Date.now();
21
+ if (now - start > ms)
22
+ throw new Error('Timeout');
23
+ }
24
+ };
25
+ /**
26
+ * `SyncMessenger` allows to execute asynchronous code synchronously. The
27
+ * asynchronous code is executed in a Worker thread, while the main thread is
28
+ * blocked until the asynchronous code is finished.
29
+ *
30
+ * First four 4-byte words is the header, where the first word is used for Atomics
31
+ * notifications. The second word is used for spin-locking the main thread until
32
+ * the asynchronous code is finished. The third word is used to specify payload
33
+ * length. The fourth word is currently unused.
34
+ *
35
+ * The maximum payload size is the size of the SharedArrayBuffer minus the
36
+ * header size.
37
+ */
38
+ class SyncMessenger {
39
+ constructor(sab) {
40
+ this.sab = sab;
41
+ this.int32 = new Int32Array(sab);
42
+ this.uint8 = new Uint8Array(sab);
43
+ this.headerSize = 4 * 4;
44
+ this.dataSize = sab.byteLength - this.headerSize;
45
+ }
46
+ callSync(data) {
47
+ const requestLength = data.length;
48
+ const headerSize = this.headerSize;
49
+ const int32 = this.int32;
50
+ int32[1] = 0;
51
+ int32[2] = requestLength;
52
+ this.uint8.set(data, headerSize);
53
+ Atomics.notify(int32, 0);
54
+ sleepUntil(() => int32[1] === 1);
55
+ const responseLength = int32[2];
56
+ const response = this.uint8.slice(headerSize, headerSize + responseLength);
57
+ return response;
58
+ }
59
+ serveAsync(callback) {
60
+ const headerSize = this.headerSize;
61
+ (() => __awaiter(this, void 0, void 0, function* () {
62
+ try {
63
+ const int32 = this.int32;
64
+ const res = Atomics.wait(int32, 0, 0);
65
+ if (res !== 'ok')
66
+ throw new Error(`Unexpected Atomics.wait result: ${res}`);
67
+ const requestLength = this.int32[2];
68
+ const request = this.uint8.slice(headerSize, headerSize + requestLength);
69
+ const response = yield callback(request);
70
+ const responseLength = response.length;
71
+ int32[2] = responseLength;
72
+ this.uint8.set(response, headerSize);
73
+ int32[1] = 1;
74
+ }
75
+ catch (_a) { }
76
+ this.serveAsync(callback);
77
+ }))().catch(() => { });
78
+ }
79
+ }
80
+ exports.SyncMessenger = SyncMessenger;
@@ -0,0 +1,8 @@
1
+ export declare const enum FsaNodeWorkerMessageCode {
2
+ Init = 0,
3
+ SetRoot = 1,
4
+ RootSet = 2,
5
+ Request = 3,
6
+ Response = 4,
7
+ ResponseError = 5
8
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,17 @@
1
+ import type { IFileSystemDirectoryHandle } from '../../fsa/types';
2
+ import type { FsaNodeWorkerMessageCode } from './constants';
3
+ export type FsaNodeWorkerMsgInit = [type: FsaNodeWorkerMessageCode.Init, sab: SharedArrayBuffer];
4
+ export type FsaNodeWorkerMsgSetRoot = [
5
+ type: FsaNodeWorkerMessageCode.SetRoot,
6
+ id: number,
7
+ dir: IFileSystemDirectoryHandle
8
+ ];
9
+ export type FsaNodeWorkerMsgRootSet = [type: FsaNodeWorkerMessageCode.RootSet, id: number];
10
+ export type FsaNodeWorkerMsgRequest = [type: FsaNodeWorkerMessageCode.Request, method: string, data: unknown];
11
+ export type FsaNodeWorkerMsgResponse = [type: FsaNodeWorkerMessageCode.Response, data: unknown];
12
+ export type FsaNodeWorkerMsgResponseError = [type: FsaNodeWorkerMessageCode.ResponseError, data: unknown];
13
+ export interface FsaNodeWorkerError {
14
+ message: string;
15
+ code?: string;
16
+ }
17
+ export type FsaNodeWorkerMsg = FsaNodeWorkerMsgInit | FsaNodeWorkerMsgSetRoot | FsaNodeWorkerMsgRootSet | FsaNodeWorkerMsgRequest | FsaNodeWorkerMsgResponse | FsaNodeWorkerMsgResponseError;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/lib/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  import Stats from './Stats';
2
2
  import Dirent from './Dirent';
3
- import { Volume as _Volume, StatWatcher, FSWatcher, IReadStream, IWriteStream, DirectoryJSON } from './volume';
3
+ import { Volume as _Volume, StatWatcher, FSWatcher, IWriteStream, DirectoryJSON, NestedDirectoryJSON } from './volume';
4
4
  import { constants } from './constants';
5
5
  import type { FsPromisesApi } from './node/types';
6
- export { DirectoryJSON };
6
+ import type * as misc from './node/types/misc';
7
+ export { DirectoryJSON, NestedDirectoryJSON };
7
8
  export declare const Volume: typeof _Volume;
8
9
  export declare const vol: _Volume;
9
10
  export interface IFs extends _Volume {
@@ -12,7 +13,7 @@ export interface IFs extends _Volume {
12
13
  Dirent: new (...args: any[]) => Dirent;
13
14
  StatWatcher: new () => StatWatcher;
14
15
  FSWatcher: new () => FSWatcher;
15
- ReadStream: new (...args: any[]) => IReadStream;
16
+ ReadStream: new (...args: any[]) => misc.IReadStream;
16
17
  WriteStream: new (...args: any[]) => IWriteStream;
17
18
  promises: FsPromisesApi;
18
19
  _toUnixTimestamp: any;
@@ -29,7 +30,7 @@ export declare const fs: IFs;
29
30
  * @returns A `memfs` file system instance, which is a drop-in replacement for
30
31
  * the `fs` module.
31
32
  */
32
- export declare const memfs: (json?: DirectoryJSON, cwd?: string) => IFs;
33
+ export declare const memfs: (json?: NestedDirectoryJSON, cwd?: string) => IFs;
33
34
  export type IFsWithVolume = IFs & {
34
35
  __vol: _Volume;
35
36
  };
package/lib/index.js CHANGED
@@ -41,7 +41,7 @@ exports.fs = createFsFromVolume(exports.vol);
41
41
  * the `fs` module.
42
42
  */
43
43
  const memfs = (json = {}, cwd = '/') => {
44
- const volume = exports.Volume.fromJSON(json, cwd);
44
+ const volume = exports.Volume.fromNestedJSON(json, cwd);
45
45
  const fs = createFsFromVolume(volume);
46
46
  return fs;
47
47
  };