memfs 3.6.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.
- package/LICENSE +201 -24
- package/README.md +21 -92
- package/lib/Dirent.d.ts +2 -1
- package/lib/__tests__/util.d.ts +13 -0
- package/lib/__tests__/util.js +29 -0
- package/lib/consts/AMODE.d.ts +9 -0
- package/lib/consts/FLAG.d.ts +22 -0
- package/lib/consts/FLAG.js +2 -0
- package/lib/fsa/types.d.ts +82 -0
- package/lib/fsa/types.js +2 -0
- package/lib/fsa-to-node/FsaNodeCore.d.ts +32 -0
- package/lib/fsa-to-node/FsaNodeCore.js +168 -0
- package/lib/fsa-to-node/FsaNodeDirent.d.ts +13 -0
- package/lib/fsa-to-node/FsaNodeDirent.js +31 -0
- package/lib/fsa-to-node/FsaNodeFs.d.ts +171 -0
- package/lib/fsa-to-node/FsaNodeFs.js +842 -0
- package/lib/fsa-to-node/FsaNodeFsOpenFile.d.ts +24 -0
- package/lib/fsa-to-node/FsaNodeFsOpenFile.js +46 -0
- package/lib/fsa-to-node/FsaNodeReadStream.d.ts +24 -0
- package/lib/fsa-to-node/FsaNodeReadStream.js +100 -0
- package/lib/fsa-to-node/FsaNodeStats.d.ts +30 -0
- package/lib/fsa-to-node/FsaNodeStats.js +52 -0
- package/lib/fsa-to-node/FsaNodeWriteStream.d.ts +48 -0
- package/lib/fsa-to-node/FsaNodeWriteStream.js +162 -0
- package/lib/fsa-to-node/__tests__/FsaNodeFs.test.d.ts +1 -0
- package/lib/fsa-to-node/__tests__/FsaNodeFs.test.js +873 -0
- package/lib/fsa-to-node/__tests__/util.test.d.ts +1 -0
- package/lib/fsa-to-node/__tests__/util.test.js +28 -0
- package/lib/fsa-to-node/constants.d.ts +3 -0
- package/lib/fsa-to-node/constants.js +2 -0
- package/lib/fsa-to-node/index.d.ts +2 -0
- package/lib/fsa-to-node/index.js +7 -0
- package/lib/fsa-to-node/json.d.ts +4 -0
- package/lib/fsa-to-node/json.js +7 -0
- package/lib/fsa-to-node/types.d.ts +37 -0
- package/lib/fsa-to-node/types.js +2 -0
- package/lib/fsa-to-node/util.d.ts +4 -0
- package/lib/fsa-to-node/util.js +40 -0
- package/lib/fsa-to-node/worker/FsaNodeSyncAdapterWorker.d.ts +10 -0
- package/lib/fsa-to-node/worker/FsaNodeSyncAdapterWorker.js +72 -0
- package/lib/fsa-to-node/worker/FsaNodeSyncWorker.d.ts +20 -0
- package/lib/fsa-to-node/worker/FsaNodeSyncWorker.js +190 -0
- package/lib/fsa-to-node/worker/SyncMessenger.d.ts +24 -0
- package/lib/fsa-to-node/worker/SyncMessenger.js +80 -0
- package/lib/fsa-to-node/worker/constants.d.ts +8 -0
- package/lib/fsa-to-node/worker/constants.js +2 -0
- package/lib/fsa-to-node/worker/types.d.ts +17 -0
- package/lib/fsa-to-node/worker/types.js +2 -0
- package/lib/index.d.ts +5 -4
- package/lib/index.js +1 -1
- package/lib/node/constants.d.ts +41 -0
- package/lib/node/constants.js +56 -0
- package/lib/node/options.d.ts +23 -0
- package/lib/node/options.js +94 -0
- package/lib/node/promises.d.ts +1 -1
- package/lib/node/promises.js +1 -3
- package/lib/node/types/FsCommonObjects.d.ts +17 -0
- package/lib/node/types/FsCommonObjects.js +2 -0
- package/lib/node/types/{sync.d.ts → FsSynchronousApi.d.ts} +36 -37
- package/lib/node/types/FsSynchronousApi.js +2 -0
- package/lib/node/types/callback.d.ts +57 -58
- package/lib/node/types/index.d.ts +1 -1
- package/lib/node/types/misc.d.ts +6 -8
- package/lib/node/types/options.d.ts +20 -6
- package/lib/node/types/promises.d.ts +2 -2
- package/lib/node/util.d.ts +19 -0
- package/lib/node/util.js +265 -1
- package/lib/node-to-fsa/NodeFileSystemDirectoryHandle.d.ts +71 -0
- package/lib/node-to-fsa/NodeFileSystemDirectoryHandle.js +264 -0
- package/lib/node-to-fsa/NodeFileSystemFileHandle.d.ts +28 -0
- package/lib/node-to-fsa/NodeFileSystemFileHandle.js +73 -0
- package/lib/node-to-fsa/NodeFileSystemHandle.d.ts +33 -0
- package/lib/node-to-fsa/NodeFileSystemHandle.js +53 -0
- package/lib/node-to-fsa/NodeFileSystemSyncAccessHandle.d.ts +42 -0
- package/lib/node-to-fsa/NodeFileSystemSyncAccessHandle.js +116 -0
- package/lib/node-to-fsa/NodeFileSystemWritableFileStream.d.ts +54 -0
- package/lib/node-to-fsa/NodeFileSystemWritableFileStream.js +195 -0
- package/lib/node-to-fsa/NodePermissionStatus.d.ts +8 -0
- package/lib/node-to-fsa/NodePermissionStatus.js +13 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemDirectoryHandle.test.d.ts +1 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemDirectoryHandle.test.js +627 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemFileHandle.test.d.ts +1 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemFileHandle.test.js +191 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemHandle.test.d.ts +1 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemHandle.test.js +49 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.d.ts +1 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemSyncAccessHandle.test.js +183 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemWritableFileStream.test.d.ts +1 -0
- package/lib/node-to-fsa/__tests__/NodeFileSystemWritableFileStream.test.js +106 -0
- package/lib/node-to-fsa/__tests__/scenarios.test.d.ts +1 -0
- package/lib/node-to-fsa/__tests__/scenarios.test.js +46 -0
- package/lib/node-to-fsa/__tests__/util.test.d.ts +1 -0
- package/lib/node-to-fsa/__tests__/util.test.js +25 -0
- package/lib/node-to-fsa/index.d.ts +7 -0
- package/lib/node-to-fsa/index.js +26 -0
- package/lib/node-to-fsa/types.d.ts +12 -0
- package/lib/node-to-fsa/types.js +2 -0
- package/lib/node-to-fsa/util.d.ts +11 -0
- package/lib/node-to-fsa/util.js +33 -0
- package/lib/node.d.ts +1 -1
- package/lib/volume.d.ts +45 -126
- package/lib/volume.js +204 -562
- package/lib/webfs/index.d.ts +1 -0
- package/lib/webfs/index.js +22 -0
- package/package.json +28 -5
- /package/lib/{node/types/sync.js → consts/AMODE.js} +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type * as fsa from '../fsa/types';
|
|
2
|
+
import type * as misc from '../node/types/misc';
|
|
3
|
+
/**
|
|
4
|
+
* Represents an open file. Stores additional metadata about the open file, such
|
|
5
|
+
* as the seek position.
|
|
6
|
+
*/
|
|
7
|
+
export declare class FsaNodeFsOpenFile {
|
|
8
|
+
readonly fd: number;
|
|
9
|
+
readonly createMode: misc.TMode;
|
|
10
|
+
readonly flags: number;
|
|
11
|
+
readonly file: fsa.IFileSystemFileHandle;
|
|
12
|
+
readonly filename: string;
|
|
13
|
+
protected seek: number;
|
|
14
|
+
/**
|
|
15
|
+
* This influences the behavior of the next write operation. On the first
|
|
16
|
+
* write we want to overwrite the file or keep the existing data, depending
|
|
17
|
+
* with which flags the file was opened. On subsequent writes we want to
|
|
18
|
+
* append to the file.
|
|
19
|
+
*/
|
|
20
|
+
protected keepExistingData: boolean;
|
|
21
|
+
constructor(fd: number, createMode: misc.TMode, flags: number, file: fsa.IFileSystemFileHandle, filename: string);
|
|
22
|
+
close(): Promise<void>;
|
|
23
|
+
write(data: ArrayBufferView, seek: number | null): Promise<void>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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.FsaNodeFsOpenFile = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Represents an open file. Stores additional metadata about the open file, such
|
|
15
|
+
* as the seek position.
|
|
16
|
+
*/
|
|
17
|
+
class FsaNodeFsOpenFile {
|
|
18
|
+
constructor(fd, createMode, flags, file, filename) {
|
|
19
|
+
this.fd = fd;
|
|
20
|
+
this.createMode = createMode;
|
|
21
|
+
this.flags = flags;
|
|
22
|
+
this.file = file;
|
|
23
|
+
this.filename = filename;
|
|
24
|
+
this.seek = 0;
|
|
25
|
+
this.keepExistingData = !!(flags & 1024 /* FLAG.O_APPEND */);
|
|
26
|
+
}
|
|
27
|
+
close() {
|
|
28
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
29
|
+
}
|
|
30
|
+
write(data, seek) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
if (typeof seek !== 'number')
|
|
33
|
+
seek = this.seek;
|
|
34
|
+
const writer = yield this.file.createWritable({ keepExistingData: this.keepExistingData });
|
|
35
|
+
yield writer.write({
|
|
36
|
+
type: 'write',
|
|
37
|
+
data,
|
|
38
|
+
position: seek,
|
|
39
|
+
});
|
|
40
|
+
yield writer.close();
|
|
41
|
+
this.keepExistingData = true;
|
|
42
|
+
this.seek += data.byteLength;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
exports.FsaNodeFsOpenFile = FsaNodeFsOpenFile;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import { Defer } from 'thingies/es6/Defer';
|
|
4
|
+
import type { FsaNodeFsOpenFile } from './FsaNodeFsOpenFile';
|
|
5
|
+
import type { IReadStream } from '../node/types/misc';
|
|
6
|
+
import type { IReadStreamOptions } from '../node/types/options';
|
|
7
|
+
import type { FsaNodeFs } from './FsaNodeFs';
|
|
8
|
+
export declare class FsaNodeReadStream extends Readable implements IReadStream {
|
|
9
|
+
protected readonly fs: FsaNodeFs;
|
|
10
|
+
protected readonly handle: Promise<FsaNodeFsOpenFile>;
|
|
11
|
+
readonly path: string;
|
|
12
|
+
protected readonly options: IReadStreamOptions;
|
|
13
|
+
protected __pending__: boolean;
|
|
14
|
+
protected __closed__: boolean;
|
|
15
|
+
protected __bytes__: number;
|
|
16
|
+
protected readonly __mutex__: <T = unknown>(code: import("thingies/es6/types").Code<T>) => Promise<T>;
|
|
17
|
+
protected readonly __file__: Defer<FsaNodeFsOpenFile>;
|
|
18
|
+
constructor(fs: FsaNodeFs, handle: Promise<FsaNodeFsOpenFile>, path: string, options: IReadStreamOptions);
|
|
19
|
+
private __read__;
|
|
20
|
+
private __close__;
|
|
21
|
+
get bytesRead(): number;
|
|
22
|
+
get pending(): boolean;
|
|
23
|
+
_read(): void;
|
|
24
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
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.FsaNodeReadStream = void 0;
|
|
13
|
+
const stream_1 = require("stream");
|
|
14
|
+
const Defer_1 = require("thingies/es6/Defer");
|
|
15
|
+
const concurrency_1 = require("thingies/es6/concurrency");
|
|
16
|
+
class FsaNodeReadStream extends stream_1.Readable {
|
|
17
|
+
constructor(fs, handle, path, options) {
|
|
18
|
+
super();
|
|
19
|
+
this.fs = fs;
|
|
20
|
+
this.handle = handle;
|
|
21
|
+
this.path = path;
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.__pending__ = true;
|
|
24
|
+
this.__closed__ = false;
|
|
25
|
+
this.__bytes__ = 0;
|
|
26
|
+
this.__mutex__ = (0, concurrency_1.concurrency)(1);
|
|
27
|
+
this.__file__ = new Defer_1.Defer();
|
|
28
|
+
handle
|
|
29
|
+
.then(file => {
|
|
30
|
+
if (this.__closed__)
|
|
31
|
+
return;
|
|
32
|
+
this.__file__.resolve(file);
|
|
33
|
+
if (this.options.fd !== undefined)
|
|
34
|
+
this.emit('open', file.fd);
|
|
35
|
+
this.emit('ready');
|
|
36
|
+
})
|
|
37
|
+
.catch(error => {
|
|
38
|
+
this.__file__.reject(error);
|
|
39
|
+
})
|
|
40
|
+
.finally(() => {
|
|
41
|
+
this.__pending__ = false;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
__read__() {
|
|
45
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
return yield this.__mutex__(() => __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
if (this.__closed__)
|
|
48
|
+
return;
|
|
49
|
+
const { file } = yield this.__file__.promise;
|
|
50
|
+
const blob = yield file.getFile();
|
|
51
|
+
const buffer = yield blob.arrayBuffer();
|
|
52
|
+
const start = this.options.start || 0;
|
|
53
|
+
let end = typeof this.options.end === 'number' ? this.options.end + 1 : buffer.byteLength;
|
|
54
|
+
if (end > buffer.byteLength)
|
|
55
|
+
end = buffer.byteLength;
|
|
56
|
+
const uint8 = new Uint8Array(buffer, start, end - start);
|
|
57
|
+
return uint8;
|
|
58
|
+
}));
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
__close__() {
|
|
62
|
+
if (this.__closed__)
|
|
63
|
+
return;
|
|
64
|
+
this.__closed__ = true;
|
|
65
|
+
if (this.options.autoClose) {
|
|
66
|
+
this.__file__.promise
|
|
67
|
+
.then(file => {
|
|
68
|
+
this.fs.close(file.fd, () => {
|
|
69
|
+
this.emit('close');
|
|
70
|
+
});
|
|
71
|
+
return file.close();
|
|
72
|
+
})
|
|
73
|
+
.catch(error => { });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// -------------------------------------------------------------- IReadStream
|
|
77
|
+
get bytesRead() {
|
|
78
|
+
return this.__bytes__;
|
|
79
|
+
}
|
|
80
|
+
get pending() {
|
|
81
|
+
return this.__pending__;
|
|
82
|
+
}
|
|
83
|
+
// ----------------------------------------------------------------- Readable
|
|
84
|
+
_read() {
|
|
85
|
+
this.__read__().then((uint8) => {
|
|
86
|
+
if (this.__closed__)
|
|
87
|
+
return;
|
|
88
|
+
if (!uint8)
|
|
89
|
+
return this.push(null);
|
|
90
|
+
this.__bytes__ += uint8.length;
|
|
91
|
+
this.__close__();
|
|
92
|
+
this.push(uint8);
|
|
93
|
+
this.push(null);
|
|
94
|
+
}, error => {
|
|
95
|
+
this.__close__();
|
|
96
|
+
this.destroy(error);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.FsaNodeReadStream = FsaNodeReadStream;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type * as misc from '../node/types/misc';
|
|
2
|
+
export declare class FsaNodeStats<T = misc.TStatNumber> implements misc.IStats<T> {
|
|
3
|
+
protected readonly kind: 'file' | 'directory';
|
|
4
|
+
readonly uid: T;
|
|
5
|
+
readonly gid: T;
|
|
6
|
+
readonly rdev: T;
|
|
7
|
+
readonly blksize: T;
|
|
8
|
+
readonly ino: T;
|
|
9
|
+
readonly size: T;
|
|
10
|
+
readonly blocks: T;
|
|
11
|
+
readonly atime: Date;
|
|
12
|
+
readonly mtime: Date;
|
|
13
|
+
readonly ctime: Date;
|
|
14
|
+
readonly birthtime: Date;
|
|
15
|
+
readonly atimeMs: T;
|
|
16
|
+
readonly mtimeMs: T;
|
|
17
|
+
readonly ctimeMs: T;
|
|
18
|
+
readonly birthtimeMs: T;
|
|
19
|
+
readonly dev: T;
|
|
20
|
+
readonly mode: T;
|
|
21
|
+
readonly nlink: T;
|
|
22
|
+
constructor(isBigInt: boolean, size: T, kind: 'file' | 'directory');
|
|
23
|
+
isDirectory(): boolean;
|
|
24
|
+
isFile(): boolean;
|
|
25
|
+
isBlockDevice(): boolean;
|
|
26
|
+
isCharacterDevice(): boolean;
|
|
27
|
+
isSymbolicLink(): boolean;
|
|
28
|
+
isFIFO(): boolean;
|
|
29
|
+
isSocket(): boolean;
|
|
30
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FsaNodeStats = void 0;
|
|
4
|
+
const time = 0;
|
|
5
|
+
const timex = typeof BigInt === 'function' ? BigInt(time) : time;
|
|
6
|
+
const date = new Date(time);
|
|
7
|
+
class FsaNodeStats {
|
|
8
|
+
constructor(isBigInt, size, kind) {
|
|
9
|
+
this.kind = kind;
|
|
10
|
+
const dummy = (isBigInt ? timex : time);
|
|
11
|
+
this.uid = dummy;
|
|
12
|
+
this.gid = dummy;
|
|
13
|
+
this.rdev = dummy;
|
|
14
|
+
this.blksize = dummy;
|
|
15
|
+
this.ino = dummy;
|
|
16
|
+
this.size = size;
|
|
17
|
+
this.blocks = dummy;
|
|
18
|
+
this.atime = date;
|
|
19
|
+
this.mtime = date;
|
|
20
|
+
this.ctime = date;
|
|
21
|
+
this.birthtime = date;
|
|
22
|
+
this.atimeMs = dummy;
|
|
23
|
+
this.mtimeMs = dummy;
|
|
24
|
+
this.ctimeMs = dummy;
|
|
25
|
+
this.birthtimeMs = dummy;
|
|
26
|
+
this.dev = dummy;
|
|
27
|
+
this.mode = dummy;
|
|
28
|
+
this.nlink = dummy;
|
|
29
|
+
}
|
|
30
|
+
isDirectory() {
|
|
31
|
+
return this.kind === 'directory';
|
|
32
|
+
}
|
|
33
|
+
isFile() {
|
|
34
|
+
return this.kind === 'file';
|
|
35
|
+
}
|
|
36
|
+
isBlockDevice() {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
isCharacterDevice() {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
isSymbolicLink() {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
isFIFO() {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
isSocket() {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
exports.FsaNodeStats = FsaNodeStats;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Writable } from 'stream';
|
|
3
|
+
import { FsaNodeFsOpenFile } from './FsaNodeFsOpenFile';
|
|
4
|
+
import type { IFileSystemWritableFileStream } from '../fsa/types';
|
|
5
|
+
import type { IWriteStream } from '../node/types/misc';
|
|
6
|
+
import type { IWriteStreamOptions } from '../node/types/options';
|
|
7
|
+
/**
|
|
8
|
+
* This WriteStream implementation does not build on top of the `fs` module,
|
|
9
|
+
* but instead uses the lower-level `FileSystemFileHandle` interface. The reason
|
|
10
|
+
* is the different semantics in `fs` and FSA (File System Access API) write streams.
|
|
11
|
+
*
|
|
12
|
+
* When data is written to an FSA file, a new FSA stream is created, it copies
|
|
13
|
+
* the file to a temporary swap file. After each written chunk, that swap file
|
|
14
|
+
* is closed and the original file is replaced with the swap file. This means,
|
|
15
|
+
* if WriteStream was built on top of `fs`, each chunk write would result in
|
|
16
|
+
* a file copy, write, close, rename operations, which is not what we want.
|
|
17
|
+
*
|
|
18
|
+
* Instead this implementation hooks into the lower-level and closes the swap
|
|
19
|
+
* file only once the stream is closed. The downside is that the written data
|
|
20
|
+
* is not immediately visible to other processes (because it is written to the
|
|
21
|
+
* swap file), but that is the trade-off we have to make.
|
|
22
|
+
*
|
|
23
|
+
* @todo Could make this flush the data to the original file periodically, so that
|
|
24
|
+
* the data is visible to other processes.
|
|
25
|
+
* @todo This stream could work through `FileSystemSyncAccessHandle.write` in a
|
|
26
|
+
* Worker thread instead.
|
|
27
|
+
*/
|
|
28
|
+
export declare class FsaNodeWriteStream extends Writable implements IWriteStream {
|
|
29
|
+
readonly path: string;
|
|
30
|
+
protected readonly options: IWriteStreamOptions;
|
|
31
|
+
protected __pending__: boolean;
|
|
32
|
+
protected __closed__: boolean;
|
|
33
|
+
protected __bytes__: number;
|
|
34
|
+
protected readonly __stream__: Promise<IFileSystemWritableFileStream>;
|
|
35
|
+
protected readonly __mutex__: <T = unknown>(code: import("thingies/es6/types").Code<T>) => Promise<T>;
|
|
36
|
+
constructor(handle: Promise<FsaNodeFsOpenFile>, path: string, options: IWriteStreamOptions);
|
|
37
|
+
private ___write___;
|
|
38
|
+
private __close__;
|
|
39
|
+
get bytesWritten(): number;
|
|
40
|
+
get pending(): boolean;
|
|
41
|
+
close(cb: any): void;
|
|
42
|
+
_write(chunk: any, encoding: string, callback: (error?: Error | null) => void): void;
|
|
43
|
+
_writev(chunks: Array<{
|
|
44
|
+
chunk: any;
|
|
45
|
+
encoding: string;
|
|
46
|
+
}>, callback: (error?: Error | null) => void): void;
|
|
47
|
+
_final(callback: (error?: Error | null) => void): void;
|
|
48
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
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.FsaNodeWriteStream = void 0;
|
|
13
|
+
const stream_1 = require("stream");
|
|
14
|
+
const Defer_1 = require("thingies/es6/Defer");
|
|
15
|
+
const concurrency_1 = require("thingies/es6/concurrency");
|
|
16
|
+
const util_1 = require("../node/util");
|
|
17
|
+
/**
|
|
18
|
+
* This WriteStream implementation does not build on top of the `fs` module,
|
|
19
|
+
* but instead uses the lower-level `FileSystemFileHandle` interface. The reason
|
|
20
|
+
* is the different semantics in `fs` and FSA (File System Access API) write streams.
|
|
21
|
+
*
|
|
22
|
+
* When data is written to an FSA file, a new FSA stream is created, it copies
|
|
23
|
+
* the file to a temporary swap file. After each written chunk, that swap file
|
|
24
|
+
* is closed and the original file is replaced with the swap file. This means,
|
|
25
|
+
* if WriteStream was built on top of `fs`, each chunk write would result in
|
|
26
|
+
* a file copy, write, close, rename operations, which is not what we want.
|
|
27
|
+
*
|
|
28
|
+
* Instead this implementation hooks into the lower-level and closes the swap
|
|
29
|
+
* file only once the stream is closed. The downside is that the written data
|
|
30
|
+
* is not immediately visible to other processes (because it is written to the
|
|
31
|
+
* swap file), but that is the trade-off we have to make.
|
|
32
|
+
*
|
|
33
|
+
* @todo Could make this flush the data to the original file periodically, so that
|
|
34
|
+
* the data is visible to other processes.
|
|
35
|
+
* @todo This stream could work through `FileSystemSyncAccessHandle.write` in a
|
|
36
|
+
* Worker thread instead.
|
|
37
|
+
*/
|
|
38
|
+
class FsaNodeWriteStream extends stream_1.Writable {
|
|
39
|
+
constructor(handle, path, options) {
|
|
40
|
+
super();
|
|
41
|
+
this.path = path;
|
|
42
|
+
this.options = options;
|
|
43
|
+
this.__pending__ = true;
|
|
44
|
+
this.__closed__ = false;
|
|
45
|
+
this.__bytes__ = 0;
|
|
46
|
+
this.__mutex__ = (0, concurrency_1.concurrency)(1);
|
|
47
|
+
if (options.start !== undefined) {
|
|
48
|
+
if (typeof options.start !== 'number') {
|
|
49
|
+
throw new TypeError('"start" option must be a Number');
|
|
50
|
+
}
|
|
51
|
+
if (options.start < 0) {
|
|
52
|
+
throw new TypeError('"start" must be >= zero');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const stream = new Defer_1.Defer();
|
|
56
|
+
this.__stream__ = stream.promise;
|
|
57
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
var _a, _b;
|
|
59
|
+
const fsaHandle = yield handle;
|
|
60
|
+
const fileWasOpened = !options.fd;
|
|
61
|
+
if (fileWasOpened)
|
|
62
|
+
this.emit('open', fsaHandle.fd);
|
|
63
|
+
const flags = (0, util_1.flagsToNumber)((_a = options.flags) !== null && _a !== void 0 ? _a : 'w');
|
|
64
|
+
const keepExistingData = flags & 1024 /* FLAG.O_APPEND */ ? true : false;
|
|
65
|
+
const writable = yield fsaHandle.file.createWritable({ keepExistingData });
|
|
66
|
+
if (keepExistingData) {
|
|
67
|
+
const start = Number((_b = options.start) !== null && _b !== void 0 ? _b : 0);
|
|
68
|
+
if (start)
|
|
69
|
+
yield writable.seek(start);
|
|
70
|
+
}
|
|
71
|
+
this.__pending__ = false;
|
|
72
|
+
stream.resolve(writable);
|
|
73
|
+
}))().catch(error => {
|
|
74
|
+
stream.reject(error);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
___write___(buffers) {
|
|
78
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
yield this.__mutex__(() => __awaiter(this, void 0, void 0, function* () {
|
|
80
|
+
if (this.__closed__)
|
|
81
|
+
return;
|
|
82
|
+
// if (this.__closed__) throw new Error('WriteStream is closed');
|
|
83
|
+
const writable = yield this.__stream__;
|
|
84
|
+
for (const buffer of buffers) {
|
|
85
|
+
yield writable.write(buffer);
|
|
86
|
+
this.__bytes__ += buffer.byteLength;
|
|
87
|
+
}
|
|
88
|
+
}));
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
__close__() {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const emitClose = this.options.emitClose;
|
|
94
|
+
yield this.__mutex__(() => __awaiter(this, void 0, void 0, function* () {
|
|
95
|
+
if (this.__closed__ && emitClose) {
|
|
96
|
+
process.nextTick(() => this.emit('close'));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const writable = yield this.__stream__;
|
|
101
|
+
this.__closed__ = true;
|
|
102
|
+
yield writable.close();
|
|
103
|
+
if (emitClose)
|
|
104
|
+
this.emit('close');
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
this.emit('error', error);
|
|
108
|
+
if (emitClose)
|
|
109
|
+
this.emit('close', error);
|
|
110
|
+
}
|
|
111
|
+
}));
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// ------------------------------------------------------------- IWriteStream
|
|
115
|
+
get bytesWritten() {
|
|
116
|
+
return this.__bytes__;
|
|
117
|
+
}
|
|
118
|
+
get pending() {
|
|
119
|
+
return this.__pending__;
|
|
120
|
+
}
|
|
121
|
+
close(cb) {
|
|
122
|
+
if (cb)
|
|
123
|
+
this.once('close', cb);
|
|
124
|
+
this.__close__().catch(() => { });
|
|
125
|
+
}
|
|
126
|
+
// ----------------------------------------------------------------- Writable
|
|
127
|
+
_write(chunk, encoding, callback) {
|
|
128
|
+
this.___write___([chunk])
|
|
129
|
+
.then(() => {
|
|
130
|
+
if (callback)
|
|
131
|
+
callback(null);
|
|
132
|
+
})
|
|
133
|
+
.catch(error => {
|
|
134
|
+
if (callback)
|
|
135
|
+
callback(error);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
_writev(chunks, callback) {
|
|
139
|
+
const buffers = chunks.map(({ chunk }) => chunk);
|
|
140
|
+
this.___write___(buffers)
|
|
141
|
+
.then(() => {
|
|
142
|
+
if (callback)
|
|
143
|
+
callback(null);
|
|
144
|
+
})
|
|
145
|
+
.catch(error => {
|
|
146
|
+
if (callback)
|
|
147
|
+
callback(error);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
_final(callback) {
|
|
151
|
+
this.__close__()
|
|
152
|
+
.then(() => {
|
|
153
|
+
if (callback)
|
|
154
|
+
callback(null);
|
|
155
|
+
})
|
|
156
|
+
.catch(error => {
|
|
157
|
+
if (callback)
|
|
158
|
+
callback(error);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.FsaNodeWriteStream = FsaNodeWriteStream;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|