pwd-fs 3.2.4 → 3.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bitmask.d.ts +4 -0
- package/dist/bitmask.js +17 -0
- package/dist/bitmask.test.d.ts +1 -0
- package/dist/bitmask.test.js +29 -0
- package/dist/{src/index.d.ts → index.d.ts} +3 -0
- package/dist/{src/index.js → index.js} +3 -0
- package/dist/powered-file-system/append.d.ts +9 -0
- package/dist/powered-file-system/append.js +10 -0
- package/dist/powered-file-system/append.test.d.ts +1 -0
- package/dist/powered-file-system/append.test.js +47 -0
- package/dist/powered-file-system/chmod.d.ts +7 -0
- package/dist/powered-file-system/chmod.js +28 -0
- package/dist/powered-file-system/chmod.test.d.ts +1 -0
- package/dist/powered-file-system/chmod.test.js +71 -0
- package/dist/powered-file-system/chown.d.ts +9 -0
- package/dist/powered-file-system/chown.js +45 -0
- package/dist/powered-file-system/chown.test.d.ts +1 -0
- package/dist/powered-file-system/chown.test.js +82 -0
- package/dist/powered-file-system/copy.d.ts +8 -0
- package/dist/powered-file-system/copy.js +29 -0
- package/dist/powered-file-system/copy.test.d.ts +1 -0
- package/dist/powered-file-system/copy.test.js +82 -0
- package/dist/powered-file-system/mkdir.d.ts +8 -0
- package/dist/powered-file-system/mkdir.js +28 -0
- package/dist/powered-file-system/mkdir.test.d.ts +1 -0
- package/dist/powered-file-system/mkdir.test.js +116 -0
- package/dist/powered-file-system/read.d.ts +9 -0
- package/dist/powered-file-system/read.js +29 -0
- package/dist/powered-file-system/read.test.d.ts +1 -0
- package/dist/powered-file-system/read.test.js +75 -0
- package/dist/powered-file-system/readdir.d.ts +8 -0
- package/dist/powered-file-system/readdir.js +26 -0
- package/dist/powered-file-system/readdir.test.d.ts +1 -0
- package/dist/powered-file-system/readdir.test.js +72 -0
- package/dist/powered-file-system/remove.d.ts +7 -0
- package/dist/powered-file-system/remove.js +36 -0
- package/dist/powered-file-system/remove.test.d.ts +1 -0
- package/dist/powered-file-system/remove.test.js +78 -0
- package/dist/powered-file-system/rename.d.ts +7 -0
- package/dist/powered-file-system/rename.js +28 -0
- package/dist/powered-file-system/rename.test.d.ts +1 -0
- package/dist/powered-file-system/rename.test.js +70 -0
- package/dist/powered-file-system/stat.d.ts +7 -0
- package/dist/powered-file-system/stat.js +26 -0
- package/dist/powered-file-system/stat.test.d.ts +1 -0
- package/dist/powered-file-system/stat.test.js +79 -0
- package/dist/powered-file-system/symlink.d.ts +4 -0
- package/dist/powered-file-system/symlink.js +52 -0
- package/dist/powered-file-system/symlink.test.d.ts +1 -0
- package/dist/powered-file-system/symlink.test.js +77 -0
- package/dist/powered-file-system/test.d.ts +8 -0
- package/dist/powered-file-system/test.js +30 -0
- package/dist/powered-file-system/test.test.d.ts +1 -0
- package/dist/powered-file-system/test.test.js +76 -0
- package/dist/powered-file-system/write.d.ts +10 -0
- package/dist/powered-file-system/write.js +36 -0
- package/dist/powered-file-system/write.test.d.ts +1 -0
- package/dist/powered-file-system/write.test.js +97 -0
- package/dist/powered-file-system.d.ts +127 -0
- package/dist/powered-file-system.js +144 -0
- package/dist/powered-file-system.test.d.ts +1 -0
- package/dist/powered-file-system.test.js +21 -0
- package/dist/recurse-io-sync.d.ts +20 -0
- package/dist/{src/recurse-io-sync.js → recurse-io-sync.js} +37 -37
- package/dist/{src/recurse-io.d.ts → recurse-io.d.ts} +15 -1
- package/dist/{src/recurse-io.js → recurse-io.js} +52 -58
- package/dist/suite.test.d.ts +1 -0
- package/dist/suite.test.js +40 -0
- package/dist/test-utils.d.ts +18 -0
- package/dist/test-utils.js +72 -0
- package/package.json +31 -14
- package/readme.md +331 -246
- package/.travis.yml +0 -12
- package/appveyor.yml +0 -16
- package/dist/src/bitmask.d.ts +0 -1
- package/dist/src/bitmask.js +0 -18
- package/dist/src/powered-file-system.d.ts +0 -173
- package/dist/src/powered-file-system.js +0 -262
- package/dist/src/recurse-io-sync.d.ts +0 -5
- package/src/bitmask.ts +0 -20
- package/src/index.ts +0 -7
- package/src/powered-file-system.ts +0 -508
- package/src/recurse-io-sync.ts +0 -108
- package/src/recurse-io.ts +0 -238
- package/test/__fmock.ts +0 -45
- package/test/append.spec.ts +0 -71
- package/test/bitmask.spec.ts +0 -23
- package/test/chmod.spec.ts +0 -77
- package/test/chown.spec.ts +0 -92
- package/test/constructor.spec.ts +0 -15
- package/test/copy.spec.ts +0 -101
- package/test/mkdir.spec.ts +0 -121
- package/test/read.spec.ts +0 -91
- package/test/readdir.spec.ts +0 -86
- package/test/remove.spec.ts +0 -78
- package/test/rename.spec.ts +0 -84
- package/test/stat.spec.ts +0 -99
- package/test/symlink.spec.ts +0 -95
- package/test/test.spec.ts +0 -80
- package/test/write.spec.ts +0 -104
- package/tsconfig.json +0 -14
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const chance_1 = __importDefault(require("chance"));
|
|
9
|
+
const node_test_1 = require("node:test");
|
|
10
|
+
const index_1 = require("../index");
|
|
11
|
+
const test_utils_1 = require("../test-utils");
|
|
12
|
+
/**
|
|
13
|
+
* Ensures `stat()` preserves file type reporting for files, directories, and symlinks.
|
|
14
|
+
*/
|
|
15
|
+
(0, node_test_1.describe)('stat(src [, options])', () => {
|
|
16
|
+
const chance = new chance_1.default();
|
|
17
|
+
let tmpDir = '';
|
|
18
|
+
(0, node_test_1.beforeEach)(() => {
|
|
19
|
+
tmpDir = (0, test_utils_1.createTmpDir)();
|
|
20
|
+
(0, test_utils_1.fmock)({
|
|
21
|
+
[node_path_1.default.join(tmpDir, 'tings.txt')]: {
|
|
22
|
+
type: 'file',
|
|
23
|
+
data: chance.string()
|
|
24
|
+
},
|
|
25
|
+
[node_path_1.default.join(tmpDir, 'digest')]: { type: 'directory' },
|
|
26
|
+
[node_path_1.default.join(tmpDir, 'flexapp')]: {
|
|
27
|
+
type: 'symlink',
|
|
28
|
+
target: node_path_1.default.join(tmpDir, 'tings.txt')
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
(0, node_test_1.afterEach)(() => {
|
|
33
|
+
(0, test_utils_1.restore)(tmpDir);
|
|
34
|
+
});
|
|
35
|
+
(0, node_test_1.it)('Positive: Must return information a file', async () => {
|
|
36
|
+
const stats = await index_1.pfs.stat(node_path_1.default.join(tmpDir, 'tings.txt'));
|
|
37
|
+
(0, node_assert_1.default)(stats.isFile());
|
|
38
|
+
});
|
|
39
|
+
(0, node_test_1.it)('Positive: Must return information a directory', async () => {
|
|
40
|
+
const stats = await index_1.pfs.stat(node_path_1.default.join(tmpDir, 'digest'));
|
|
41
|
+
(0, node_assert_1.default)(stats.isDirectory());
|
|
42
|
+
});
|
|
43
|
+
(0, node_test_1.it)('Positive: Must return information a symlink', async () => {
|
|
44
|
+
const stats = await index_1.pfs.stat(node_path_1.default.join(tmpDir, 'flexapp'));
|
|
45
|
+
(0, node_assert_1.default)(stats.isSymbolicLink());
|
|
46
|
+
});
|
|
47
|
+
(0, node_test_1.it)('Negative: Throw if not exists resource', async () => {
|
|
48
|
+
const guid = chance.guid();
|
|
49
|
+
await node_assert_1.default.rejects(async () => {
|
|
50
|
+
await index_1.pfs.stat(node_path_1.default.join(tmpDir, guid));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
(0, node_test_1.it)('[sync] Positive: Must return information a file', () => {
|
|
54
|
+
const stats = index_1.pfs.stat(node_path_1.default.join(tmpDir, 'tings.txt'), {
|
|
55
|
+
sync: true
|
|
56
|
+
});
|
|
57
|
+
(0, node_assert_1.default)(stats.isFile());
|
|
58
|
+
});
|
|
59
|
+
(0, node_test_1.it)('[sync] Positive: Must return information a directory in ', () => {
|
|
60
|
+
const stats = index_1.pfs.stat(node_path_1.default.join(tmpDir, 'digest'), {
|
|
61
|
+
sync: true
|
|
62
|
+
});
|
|
63
|
+
(0, node_assert_1.default)(stats.isDirectory());
|
|
64
|
+
});
|
|
65
|
+
(0, node_test_1.it)('[sync] Positive: Must return information a symlink', () => {
|
|
66
|
+
const stats = index_1.pfs.stat(node_path_1.default.join(tmpDir, 'flexapp'), {
|
|
67
|
+
sync: true
|
|
68
|
+
});
|
|
69
|
+
(0, node_assert_1.default)(stats.isSymbolicLink());
|
|
70
|
+
});
|
|
71
|
+
(0, node_test_1.it)('[sync] Negative: Throw if not exists resource', () => {
|
|
72
|
+
const guid = chance.guid();
|
|
73
|
+
node_assert_1.default.throws(() => {
|
|
74
|
+
index_1.pfs.stat(node_path_1.default.join(tmpDir, guid), {
|
|
75
|
+
sync: true
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.symlink = symlink;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
/**
|
|
10
|
+
* Windows requires an explicit link type. Non-Windows platforms infer it.
|
|
11
|
+
*/
|
|
12
|
+
function resolveSymlinkType(src) {
|
|
13
|
+
if (process.platform !== 'win32') {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const stats = node_fs_1.default.lstatSync(src);
|
|
17
|
+
return stats.isDirectory() ? 'junction' : 'file';
|
|
18
|
+
}
|
|
19
|
+
function symlink(src, dest, options) {
|
|
20
|
+
src = node_path_1.default.resolve(this.pwd, src);
|
|
21
|
+
dest = node_path_1.default.resolve(this.pwd, dest);
|
|
22
|
+
const { sync = false } = options ?? {};
|
|
23
|
+
if (sync) {
|
|
24
|
+
const type = resolveSymlinkType(src);
|
|
25
|
+
node_fs_1.default.symlinkSync(src, dest, type);
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
if (process.platform === 'win32') {
|
|
30
|
+
node_fs_1.default.lstat(src, (err, stats) => {
|
|
31
|
+
if (err) {
|
|
32
|
+
return reject(err);
|
|
33
|
+
}
|
|
34
|
+
const type = stats.isDirectory() ? 'junction' : 'file';
|
|
35
|
+
node_fs_1.default.symlink(src, dest, type, (err) => {
|
|
36
|
+
if (err) {
|
|
37
|
+
return reject(err);
|
|
38
|
+
}
|
|
39
|
+
resolve();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
node_fs_1.default.symlink(src, dest, (err) => {
|
|
45
|
+
if (err) {
|
|
46
|
+
return reject(err);
|
|
47
|
+
}
|
|
48
|
+
resolve();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const chance_1 = __importDefault(require("chance"));
|
|
10
|
+
const node_test_1 = require("node:test");
|
|
11
|
+
const index_1 = require("../index");
|
|
12
|
+
const test_utils_1 = require("../test-utils");
|
|
13
|
+
/**
|
|
14
|
+
* Covers symlink creation for file and directory targets.
|
|
15
|
+
*/
|
|
16
|
+
(0, node_test_1.describe)('symlink(src, use [, options])', () => {
|
|
17
|
+
const chance = new chance_1.default();
|
|
18
|
+
let tmpDir = '';
|
|
19
|
+
(0, node_test_1.beforeEach)(() => {
|
|
20
|
+
tmpDir = (0, test_utils_1.createTmpDir)();
|
|
21
|
+
const frame = {
|
|
22
|
+
[node_path_1.default.join(tmpDir, 'tings.txt')]: {
|
|
23
|
+
type: 'file',
|
|
24
|
+
data: chance.string()
|
|
25
|
+
},
|
|
26
|
+
[node_path_1.default.join(tmpDir, 'digest')]: { type: 'directory' },
|
|
27
|
+
[node_path_1.default.join(tmpDir, 'flexapp')]: {
|
|
28
|
+
type: 'symlink',
|
|
29
|
+
target: node_path_1.default.join(tmpDir, 'tings.txt')
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const counter = chance.natural({ max: 7 });
|
|
33
|
+
for (let i = 0; i < counter; i++) {
|
|
34
|
+
frame[node_path_1.default.join(tmpDir, String(i))] = { type: 'directory' };
|
|
35
|
+
}
|
|
36
|
+
(0, test_utils_1.fmock)(frame);
|
|
37
|
+
});
|
|
38
|
+
(0, node_test_1.afterEach)(() => {
|
|
39
|
+
(0, test_utils_1.restore)(tmpDir);
|
|
40
|
+
});
|
|
41
|
+
(0, node_test_1.it)('Positive: Must be created a symbolic link', async () => {
|
|
42
|
+
await index_1.pfs.symlink(node_path_1.default.join(tmpDir, 'tings.txt'), node_path_1.default.join(tmpDir, 'linkapp'));
|
|
43
|
+
const stat = node_fs_1.default.lstatSync(node_path_1.default.join(tmpDir, 'linkapp'));
|
|
44
|
+
(0, node_assert_1.default)(stat.isSymbolicLink());
|
|
45
|
+
});
|
|
46
|
+
(0, node_test_1.it)('Positive: Must be created a symbolic link for directory', async () => {
|
|
47
|
+
await index_1.pfs.symlink(node_path_1.default.join(tmpDir, 'digest'), node_path_1.default.join(tmpDir, 'linkapp'));
|
|
48
|
+
const stat = node_fs_1.default.lstatSync(node_path_1.default.join(tmpDir, 'linkapp'));
|
|
49
|
+
(0, node_assert_1.default)(stat.isSymbolicLink());
|
|
50
|
+
});
|
|
51
|
+
(0, node_test_1.it)('Negative: Throw if destination already exists', async () => {
|
|
52
|
+
await node_assert_1.default.rejects(async () => {
|
|
53
|
+
await index_1.pfs.symlink(node_path_1.default.join(tmpDir, 'tings.txt'), node_path_1.default.join(tmpDir, 'flexapp'));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
(0, node_test_1.it)('[sync] Positive: Must be created a symbolic link', () => {
|
|
57
|
+
index_1.pfs.symlink(node_path_1.default.join(tmpDir, 'tings.txt'), node_path_1.default.join(tmpDir, 'linkapp'), {
|
|
58
|
+
sync: true
|
|
59
|
+
});
|
|
60
|
+
const stat = node_fs_1.default.lstatSync(node_path_1.default.join(tmpDir, 'linkapp'));
|
|
61
|
+
(0, node_assert_1.default)(stat.isSymbolicLink());
|
|
62
|
+
});
|
|
63
|
+
(0, node_test_1.it)('[sync] Positive: Must be created a symbolic link for directory', () => {
|
|
64
|
+
index_1.pfs.symlink(node_path_1.default.join(tmpDir, 'digest'), node_path_1.default.join(tmpDir, 'linkapp'), {
|
|
65
|
+
sync: true
|
|
66
|
+
});
|
|
67
|
+
const stat = node_fs_1.default.lstatSync(node_path_1.default.join(tmpDir, 'linkapp'));
|
|
68
|
+
(0, node_assert_1.default)(stat.isSymbolicLink());
|
|
69
|
+
});
|
|
70
|
+
(0, node_test_1.it)('[sync] Negative: Throw if destination already exists', () => {
|
|
71
|
+
node_assert_1.default.throws(() => {
|
|
72
|
+
index_1.pfs.symlink(node_path_1.default.join(tmpDir, 'tings.txt'), node_path_1.default.join(tmpDir, 'flexapp'), {
|
|
73
|
+
sync: true
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Mode, PoweredFileSystem } from '../powered-file-system';
|
|
2
|
+
/**
|
|
3
|
+
* Thin wrapper around `fs.access` that resolves paths against the instance root.
|
|
4
|
+
*/
|
|
5
|
+
export declare function test<T extends boolean = false>(this: PoweredFileSystem, src: string, options?: {
|
|
6
|
+
sync?: T;
|
|
7
|
+
flag?: Mode;
|
|
8
|
+
}): T extends true ? boolean : Promise<boolean>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.test = test;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
/**
|
|
10
|
+
* Thin wrapper around `fs.access` that resolves paths against the instance root.
|
|
11
|
+
*/
|
|
12
|
+
function test(src, options) {
|
|
13
|
+
const { sync = false, flag = 'e' } = options ?? {};
|
|
14
|
+
const mode = this.constants[flag];
|
|
15
|
+
src = node_path_1.default.resolve(this.pwd, src);
|
|
16
|
+
if (sync) {
|
|
17
|
+
try {
|
|
18
|
+
node_fs_1.default.accessSync(src, mode);
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
node_fs_1.default.access(src, mode, (err) => {
|
|
27
|
+
resolve(!err);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const chance_1 = __importDefault(require("chance"));
|
|
10
|
+
const node_test_1 = require("node:test");
|
|
11
|
+
const index_1 = require("../index");
|
|
12
|
+
const test_utils_1 = require("../test-utils");
|
|
13
|
+
/**
|
|
14
|
+
* Verifies existence and access checks exposed by `test()`.
|
|
15
|
+
*/
|
|
16
|
+
(0, node_test_1.describe)('test(src[, options])', () => {
|
|
17
|
+
const chance = new chance_1.default();
|
|
18
|
+
let tmpDir = '';
|
|
19
|
+
(0, node_test_1.beforeEach)(() => {
|
|
20
|
+
tmpDir = (0, test_utils_1.createTmpDir)();
|
|
21
|
+
(0, test_utils_1.fmock)({
|
|
22
|
+
[node_path_1.default.join(tmpDir, 'tings.txt')]: {
|
|
23
|
+
type: 'file',
|
|
24
|
+
data: chance.string()
|
|
25
|
+
},
|
|
26
|
+
[node_path_1.default.join(tmpDir, 'digest')]: { type: 'directory' }
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
(0, node_test_1.afterEach)(() => {
|
|
30
|
+
(0, test_utils_1.restore)(tmpDir);
|
|
31
|
+
});
|
|
32
|
+
(0, node_test_1.it)(`Positive: Should return 'true' for current working directory`, async () => {
|
|
33
|
+
const exist = await index_1.pfs.test('.');
|
|
34
|
+
(0, node_assert_1.default)(exist);
|
|
35
|
+
});
|
|
36
|
+
(0, node_test_1.it)(`Positive: For existing file should return 'true'`, async () => {
|
|
37
|
+
const exist = await index_1.pfs.test(node_path_1.default.join(tmpDir, 'tings.txt'));
|
|
38
|
+
(0, node_assert_1.default)(exist);
|
|
39
|
+
});
|
|
40
|
+
(0, node_test_1.it)(`Positive: For existing directory should return 'true'`, async () => {
|
|
41
|
+
const exist = await index_1.pfs.test(node_path_1.default.join(tmpDir, 'digest'));
|
|
42
|
+
(0, node_assert_1.default)(exist);
|
|
43
|
+
});
|
|
44
|
+
(0, node_test_1.it)(`Positive: A non-existent file must return 'false'`, async () => {
|
|
45
|
+
const guid = chance.guid();
|
|
46
|
+
const exist = await index_1.pfs.test(node_path_1.default.join(tmpDir, guid));
|
|
47
|
+
(0, node_assert_1.default)(exist === false);
|
|
48
|
+
});
|
|
49
|
+
(0, node_test_1.it)(`Positive: For existing file should return 'true'`, () => {
|
|
50
|
+
const exist = index_1.pfs.test(node_path_1.default.join(tmpDir, 'tings.txt'), {
|
|
51
|
+
sync: true
|
|
52
|
+
});
|
|
53
|
+
(0, node_assert_1.default)(exist);
|
|
54
|
+
});
|
|
55
|
+
(0, node_test_1.it)(`[sync] Positive: For existing directory should return 'true'`, () => {
|
|
56
|
+
const exist = index_1.pfs.test(node_path_1.default.join(tmpDir, 'digest'), {
|
|
57
|
+
sync: true
|
|
58
|
+
});
|
|
59
|
+
(0, node_assert_1.default)(exist);
|
|
60
|
+
});
|
|
61
|
+
(0, node_test_1.it)(`[sync] Positive: A non-existent file must return 'false'`, () => {
|
|
62
|
+
const guid = chance.guid();
|
|
63
|
+
const exist = index_1.pfs.test(node_path_1.default.join(tmpDir, guid), {
|
|
64
|
+
sync: true
|
|
65
|
+
});
|
|
66
|
+
(0, node_assert_1.default)(exist === false);
|
|
67
|
+
});
|
|
68
|
+
(0, node_test_1.it)('[sync] Positive: Should respect access flag checks', () => {
|
|
69
|
+
node_fs_1.default.chmodSync(node_path_1.default.join(tmpDir, 'tings.txt'), 0o444);
|
|
70
|
+
const writable = index_1.pfs.test(node_path_1.default.join(tmpDir, 'tings.txt'), {
|
|
71
|
+
sync: true,
|
|
72
|
+
flag: 'w'
|
|
73
|
+
});
|
|
74
|
+
(0, node_assert_1.default)(writable === false);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Flag, PoweredFileSystem } from '../powered-file-system';
|
|
2
|
+
/**
|
|
3
|
+
* Writes a file relative to `pwd` and then reapplies the computed permissions explicitly.
|
|
4
|
+
*/
|
|
5
|
+
export declare function write<T extends boolean = false>(this: PoweredFileSystem, src: string, data: Buffer | string, options?: {
|
|
6
|
+
sync?: T;
|
|
7
|
+
encoding?: BufferEncoding | null;
|
|
8
|
+
umask?: number;
|
|
9
|
+
flag?: Flag;
|
|
10
|
+
}): T extends true ? void : Promise<void>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.write = write;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
/**
|
|
10
|
+
* Writes a file relative to `pwd` and then reapplies the computed permissions explicitly.
|
|
11
|
+
*/
|
|
12
|
+
function write(src, data, options) {
|
|
13
|
+
const { sync = false, encoding = 'utf8', umask = 0o000, flag = 'w', } = options ?? {};
|
|
14
|
+
src = node_path_1.default.resolve(this.pwd, src);
|
|
15
|
+
const mode = 0o666 & ~umask;
|
|
16
|
+
if (sync) {
|
|
17
|
+
// Apply chmod explicitly so the final mode is deterministic across runtimes.
|
|
18
|
+
node_fs_1.default.writeFileSync(src, data, { encoding, mode, flag });
|
|
19
|
+
node_fs_1.default.chmodSync(src, mode);
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
node_fs_1.default.writeFile(src, data, { encoding, mode, flag }, (err) => {
|
|
24
|
+
if (err) {
|
|
25
|
+
return reject(err);
|
|
26
|
+
}
|
|
27
|
+
// Align async behavior with the synchronous branch.
|
|
28
|
+
node_fs_1.default.chmod(src, mode, (err) => {
|
|
29
|
+
if (err) {
|
|
30
|
+
return reject(err);
|
|
31
|
+
}
|
|
32
|
+
resolve();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const chance_1 = __importDefault(require("chance"));
|
|
10
|
+
const node_test_1 = require("node:test");
|
|
11
|
+
const index_1 = require("../index");
|
|
12
|
+
const test_utils_1 = require("../test-utils");
|
|
13
|
+
/**
|
|
14
|
+
* Covers file writes, overwrite behavior, and explicit mode handling.
|
|
15
|
+
*/
|
|
16
|
+
const itUnix = process.platform === 'win32' ? node_test_1.it.skip : node_test_1.it;
|
|
17
|
+
(0, node_test_1.describe)('write(src, data[, options])', () => {
|
|
18
|
+
const chance = new chance_1.default();
|
|
19
|
+
let tmpDir = '';
|
|
20
|
+
(0, node_test_1.beforeEach)(() => {
|
|
21
|
+
tmpDir = (0, test_utils_1.createTmpDir)();
|
|
22
|
+
(0, test_utils_1.fmock)({
|
|
23
|
+
[node_path_1.default.join(tmpDir, 'tings.txt')]: {
|
|
24
|
+
type: 'file',
|
|
25
|
+
data: chance.string()
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
(0, node_test_1.afterEach)(() => {
|
|
30
|
+
(0, test_utils_1.restore)(tmpDir);
|
|
31
|
+
});
|
|
32
|
+
(0, node_test_1.it)('Positive: Must write content to file', async () => {
|
|
33
|
+
const payload = chance.paragraph();
|
|
34
|
+
const guid = chance.guid();
|
|
35
|
+
const filePath = node_path_1.default.join(tmpDir, `${guid}.txt`);
|
|
36
|
+
await index_1.pfs.write(filePath, payload);
|
|
37
|
+
const { size } = node_fs_1.default.lstatSync(filePath);
|
|
38
|
+
(0, node_assert_1.default)(payload.length === size);
|
|
39
|
+
});
|
|
40
|
+
(0, node_test_1.it)('Positive: Must rewrite content if file already exists', async () => {
|
|
41
|
+
const payload = chance.paragraph();
|
|
42
|
+
await index_1.pfs.write(node_path_1.default.join(tmpDir, 'tings.txt'), payload);
|
|
43
|
+
const { size } = node_fs_1.default.lstatSync(node_path_1.default.join(tmpDir, 'tings.txt'));
|
|
44
|
+
(0, node_assert_1.default)(payload.length === size);
|
|
45
|
+
});
|
|
46
|
+
(0, node_test_1.it)('Negative: Throw if resource is directory', async () => {
|
|
47
|
+
const payload = chance.paragraph();
|
|
48
|
+
await node_assert_1.default.rejects(async () => {
|
|
49
|
+
await index_1.pfs.write(tmpDir, payload);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
(0, node_test_1.it)(`Negative: Unexpected option 'flag' returns Error`, async () => {
|
|
53
|
+
const payload = chance.paragraph();
|
|
54
|
+
await node_assert_1.default.rejects(async () => {
|
|
55
|
+
await index_1.pfs.write(node_path_1.default.join(tmpDir, 'tings.txt'), payload, {
|
|
56
|
+
flag: 'r'
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
(0, node_test_1.it)('[sync] Positive: Write contents even to a non-existent file', () => {
|
|
61
|
+
const payload = chance.paragraph();
|
|
62
|
+
const guid = chance.guid();
|
|
63
|
+
const filePath = node_path_1.default.join(tmpDir, `${guid}.txt`);
|
|
64
|
+
index_1.pfs.write(filePath, payload, {
|
|
65
|
+
sync: true
|
|
66
|
+
});
|
|
67
|
+
const content = node_fs_1.default.readFileSync(filePath, 'utf8');
|
|
68
|
+
(0, node_assert_1.default)(payload === content);
|
|
69
|
+
});
|
|
70
|
+
(0, node_test_1.it)('[sync] Negative: Throw if resource is directory', () => {
|
|
71
|
+
const payload = chance.paragraph();
|
|
72
|
+
node_assert_1.default.throws(() => {
|
|
73
|
+
index_1.pfs.write(tmpDir, payload, {
|
|
74
|
+
sync: true
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
(0, node_test_1.it)(`[sync] Negative: Unexpected option 'flag' returns Error`, () => {
|
|
79
|
+
const payload = chance.paragraph();
|
|
80
|
+
node_assert_1.default.throws(() => {
|
|
81
|
+
index_1.pfs.write(node_path_1.default.join(tmpDir, 'tings.txt'), payload, {
|
|
82
|
+
sync: true,
|
|
83
|
+
flag: 'r'
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
itUnix('[sync] Positive: Umask should be applied with bit masking', () => {
|
|
88
|
+
const guid = chance.guid();
|
|
89
|
+
const filePath = node_path_1.default.join(tmpDir, `${guid}.txt`);
|
|
90
|
+
index_1.pfs.write(filePath, 'x', {
|
|
91
|
+
sync: true,
|
|
92
|
+
umask: 0o111
|
|
93
|
+
});
|
|
94
|
+
const mode = node_fs_1.default.statSync(filePath).mode & 0o777;
|
|
95
|
+
(0, node_assert_1.default)(mode === 0o666);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { bitmask } from './bitmask';
|
|
3
|
+
/**
|
|
4
|
+
* Public API entrypoint for the path-aware file system wrapper.
|
|
5
|
+
*/
|
|
6
|
+
export type Mode = keyof IConstants;
|
|
7
|
+
export type Flag = Extract<fs.OpenMode, string>;
|
|
8
|
+
export type Stats = fs.Stats;
|
|
9
|
+
export * from './bitmask';
|
|
10
|
+
export interface IConstants {
|
|
11
|
+
e: number;
|
|
12
|
+
r: number;
|
|
13
|
+
w: number;
|
|
14
|
+
x: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Path-aware wrapper around Node's file system APIs.
|
|
18
|
+
*
|
|
19
|
+
* All relative paths are resolved against `pwd`, which makes the instance
|
|
20
|
+
* suitable for sandboxed or virtual working-directory workflows.
|
|
21
|
+
*/
|
|
22
|
+
export declare class PoweredFileSystem {
|
|
23
|
+
readonly pwd: string;
|
|
24
|
+
/**
|
|
25
|
+
* Access mode aliases used by `test()`.
|
|
26
|
+
*/
|
|
27
|
+
readonly constants: IConstants;
|
|
28
|
+
/**
|
|
29
|
+
* Exposes permission mask normalization as a static helper.
|
|
30
|
+
*/
|
|
31
|
+
static bitmask: typeof bitmask;
|
|
32
|
+
/**
|
|
33
|
+
* @param pwd Base directory used to resolve all relative paths.
|
|
34
|
+
*/
|
|
35
|
+
constructor(pwd?: string);
|
|
36
|
+
/**
|
|
37
|
+
* Checks whether the given path is accessible with the requested mode.
|
|
38
|
+
*/
|
|
39
|
+
test<T extends boolean = false>(src: string, options?: {
|
|
40
|
+
sync?: T;
|
|
41
|
+
flag?: Mode;
|
|
42
|
+
}): T extends true ? boolean : Promise<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Returns `lstat` information for a path.
|
|
45
|
+
*/
|
|
46
|
+
stat<T extends boolean = false>(src: string, options?: {
|
|
47
|
+
sync?: T;
|
|
48
|
+
}): T extends true ? Stats : Promise<Stats>;
|
|
49
|
+
/**
|
|
50
|
+
* Applies a mode recursively to a file or directory tree.
|
|
51
|
+
*/
|
|
52
|
+
chmod<T extends boolean = false>(src: string, mode: number, options?: {
|
|
53
|
+
sync?: T;
|
|
54
|
+
}): T extends true ? void : Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Applies ownership recursively to a file or directory tree.
|
|
57
|
+
*/
|
|
58
|
+
chown<T extends boolean = false>(src: string, options?: {
|
|
59
|
+
sync?: T;
|
|
60
|
+
uid?: number;
|
|
61
|
+
gid?: number;
|
|
62
|
+
}): T extends true ? void : Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Creates a symbolic link from `dest` to `src`.
|
|
65
|
+
*/
|
|
66
|
+
symlink<T extends boolean = false>(src: string, dest: string, options?: {
|
|
67
|
+
sync?: T;
|
|
68
|
+
}): T extends true ? void : Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Copies `src` into the destination directory.
|
|
71
|
+
*/
|
|
72
|
+
copy<T extends boolean = false>(src: string, dest: string, options?: {
|
|
73
|
+
sync?: T;
|
|
74
|
+
umask?: number;
|
|
75
|
+
}): T extends true ? void : Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Renames or moves a file system node.
|
|
78
|
+
*/
|
|
79
|
+
rename<T extends boolean = false>(src: string, dest: string, options?: {
|
|
80
|
+
sync?: T;
|
|
81
|
+
}): T extends true ? void : Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* Removes a file system node recursively.
|
|
84
|
+
*/
|
|
85
|
+
remove<T extends boolean = false>(src: string, options?: {
|
|
86
|
+
sync?: T;
|
|
87
|
+
}): T extends true ? void : Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Reads a file relative to the current instance root.
|
|
90
|
+
*/
|
|
91
|
+
read<T extends boolean = false>(src: string, options?: {
|
|
92
|
+
sync?: T;
|
|
93
|
+
encoding?: BufferEncoding | null;
|
|
94
|
+
flag?: Flag;
|
|
95
|
+
}): T extends true ? string | Buffer : Promise<string | Buffer>;
|
|
96
|
+
/**
|
|
97
|
+
* Writes a file and applies the resulting permissions explicitly.
|
|
98
|
+
*/
|
|
99
|
+
write<T extends boolean = false>(src: string, data: Buffer | string, options?: {
|
|
100
|
+
sync?: T;
|
|
101
|
+
encoding?: BufferEncoding | null;
|
|
102
|
+
umask?: number;
|
|
103
|
+
flag?: Flag;
|
|
104
|
+
}): T extends true ? void : Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* @deprecated Use `write(..., { flag: 'a' })` instead.
|
|
107
|
+
*/
|
|
108
|
+
append<T extends boolean = false>(src: string, data: Buffer | string, options?: {
|
|
109
|
+
sync?: T;
|
|
110
|
+
encoding?: BufferEncoding | null;
|
|
111
|
+
umask?: number;
|
|
112
|
+
}): T extends true ? void : Promise<void>;
|
|
113
|
+
/**
|
|
114
|
+
* Lists directory entries relative to the current instance root.
|
|
115
|
+
*/
|
|
116
|
+
readdir<T extends boolean = false>(dir: string, options?: {
|
|
117
|
+
sync?: T;
|
|
118
|
+
encoding?: BufferEncoding | null;
|
|
119
|
+
}): T extends true ? string[] : Promise<string[]>;
|
|
120
|
+
/**
|
|
121
|
+
* Creates a directory tree relative to the current instance root.
|
|
122
|
+
*/
|
|
123
|
+
mkdir<T extends boolean = false>(dir: string, options?: {
|
|
124
|
+
sync?: T;
|
|
125
|
+
umask?: number;
|
|
126
|
+
}): T extends true ? void : Promise<void>;
|
|
127
|
+
}
|