Skip to content

Commit c530520

Browse files
sapphi-redmarco-ippolito
authored andcommittedApr 29, 2024·
fs: add stacktrace to fs/promises
Sync functions in fs throwed an error with a stacktrace which is helpful for debugging. But functions in fs/promises throwed an error without a stacktrace. This commit adds stacktraces by calling Error.captureStacktrace and re-throwing the error. Refs: #34817 PR-URL: #49849 Backport-PR-URL: #51127 Fixes: #50160 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Zeyu "Alex" Yang <himself65@outlook.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
1 parent d5ac979 commit c530520

File tree

4 files changed

+248
-90
lines changed

4 files changed

+248
-90
lines changed
 

‎lib/internal/fs/promises.js

+240-87
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const {
44
ArrayPrototypePush,
55
ArrayPrototypePop,
66
Error,
7+
ErrorCaptureStackTrace,
78
MathMax,
89
MathMin,
910
NumberIsSafeInteger,
@@ -138,6 +139,15 @@ function lazyFsStreams() {
138139

139140
const lazyRimRaf = getLazy(() => require('internal/fs/rimraf').rimrafPromises);
140141

142+
// By the time the C++ land creates an error for a promise rejection (likely from a
143+
// libuv callback), there is already no JS frames on the stack. So we need to
144+
// wait until V8 resumes execution back to JS land before we have enough information
145+
// to re-capture the stack trace.
146+
function handleErrorFromBinding(error) {
147+
ErrorCaptureStackTrace(error, handleErrorFromBinding);
148+
return PromiseReject(error);
149+
}
150+
141151
class FileHandle extends EventEmitterMixin(JSTransferable) {
142152
/**
143153
* @param {InternalFSBinding.FileHandle | undefined} filehandle
@@ -502,7 +512,11 @@ async function readFileHandle(filehandle, options) {
502512

503513
checkAborted(signal);
504514

505-
const statFields = await binding.fstat(filehandle.fd, false, kUsePromises);
515+
const statFields = await PromisePrototypeThen(
516+
binding.fstat(filehandle.fd, false, kUsePromises),
517+
undefined,
518+
handleErrorFromBinding,
519+
);
506520

507521
checkAborted(signal);
508522

@@ -533,8 +547,11 @@ async function readFileHandle(filehandle, options) {
533547
length = MathMin(size - totalRead, kReadFileBufferLength);
534548
}
535549

536-
const bytesRead = (await binding.read(filehandle.fd, buffer, offset,
537-
length, -1, kUsePromises)) ?? 0;
550+
const bytesRead = (await PromisePrototypeThen(
551+
binding.read(filehandle.fd, buffer, offset, length, -1, kUsePromises),
552+
undefined,
553+
handleErrorFromBinding,
554+
)) ?? 0;
538555
totalRead += bytesRead;
539556

540557
if (bytesRead === 0 ||
@@ -582,8 +599,11 @@ async function access(path, mode = F_OK) {
582599
path = getValidatedPath(path);
583600

584601
mode = getValidMode(mode, 'access');
585-
return binding.access(pathModule.toNamespacedPath(path), mode,
586-
kUsePromises);
602+
return await PromisePrototypeThen(
603+
binding.access(pathModule.toNamespacedPath(path), mode, kUsePromises),
604+
undefined,
605+
handleErrorFromBinding,
606+
);
587607
}
588608

589609
async function cp(src, dest, options) {
@@ -597,10 +617,14 @@ async function copyFile(src, dest, mode) {
597617
src = getValidatedPath(src, 'src');
598618
dest = getValidatedPath(dest, 'dest');
599619
mode = getValidMode(mode, 'copyFile');
600-
return binding.copyFile(pathModule.toNamespacedPath(src),
601-
pathModule.toNamespacedPath(dest),
602-
mode,
603-
kUsePromises);
620+
return await PromisePrototypeThen(
621+
binding.copyFile(pathModule.toNamespacedPath(src),
622+
pathModule.toNamespacedPath(dest),
623+
mode,
624+
kUsePromises),
625+
undefined,
626+
handleErrorFromBinding,
627+
);
604628
}
605629

606630
// Note that unlike fs.open() which uses numeric file descriptors,
@@ -609,9 +633,12 @@ async function open(path, flags, mode) {
609633
path = getValidatedPath(path);
610634
const flagsNumber = stringToFlags(flags);
611635
mode = parseFileMode(mode, 'mode', 0o666);
612-
return new FileHandle(
613-
await binding.openFileHandle(pathModule.toNamespacedPath(path),
614-
flagsNumber, mode, kUsePromises));
636+
return new FileHandle(await PromisePrototypeThen(
637+
binding.openFileHandle(pathModule.toNamespacedPath(path),
638+
flagsNumber, mode, kUsePromises),
639+
undefined,
640+
handleErrorFromBinding,
641+
));
615642
}
616643

617644
async function read(handle, bufferOrParams, offset, length, position) {
@@ -661,8 +688,11 @@ async function read(handle, bufferOrParams, offset, length, position) {
661688
if (!NumberIsSafeInteger(position))
662689
position = -1;
663690

664-
const bytesRead = (await binding.read(handle.fd, buffer, offset, length,
665-
position, kUsePromises)) || 0;
691+
const bytesRead = (await PromisePrototypeThen(
692+
binding.read(handle.fd, buffer, offset, length, position, kUsePromises),
693+
undefined,
694+
handleErrorFromBinding,
695+
)) || 0;
666696

667697
return { __proto__: null, bytesRead, buffer };
668698
}
@@ -673,8 +703,11 @@ async function readv(handle, buffers, position) {
673703
if (typeof position !== 'number')
674704
position = null;
675705

676-
const bytesRead = (await binding.readBuffers(handle.fd, buffers, position,
677-
kUsePromises)) || 0;
706+
const bytesRead = (await PromisePrototypeThen(
707+
binding.readBuffers(handle.fd, buffers, position, kUsePromises),
708+
undefined,
709+
handleErrorFromBinding,
710+
)) || 0;
678711
return { __proto__: null, bytesRead, buffers };
679712
}
680713

@@ -703,15 +736,22 @@ async function write(handle, buffer, offsetOrOptions, length, position) {
703736
position = null;
704737
validateOffsetLengthWrite(offset, length, buffer.byteLength);
705738
const bytesWritten =
706-
(await binding.writeBuffer(handle.fd, buffer, offset,
707-
length, position, kUsePromises)) || 0;
739+
(await PromisePrototypeThen(
740+
binding.writeBuffer(handle.fd, buffer, offset,
741+
length, position, kUsePromises),
742+
undefined,
743+
handleErrorFromBinding,
744+
)) || 0;
708745
return { __proto__: null, bytesWritten, buffer };
709746
}
710747

711748
validateStringAfterArrayBufferView(buffer, 'buffer');
712749
validateEncoding(buffer, length);
713-
const bytesWritten = (await binding.writeString(handle.fd, buffer, offset,
714-
length, kUsePromises)) || 0;
750+
const bytesWritten = (await PromisePrototypeThen(
751+
binding.writeString(handle.fd, buffer, offset, length, kUsePromises),
752+
undefined,
753+
handleErrorFromBinding,
754+
)) || 0;
715755
return { __proto__: null, bytesWritten, buffer };
716756
}
717757

@@ -725,17 +765,24 @@ async function writev(handle, buffers, position) {
725765
return { __proto__: null, bytesWritten: 0, buffers };
726766
}
727767

728-
const bytesWritten = (await binding.writeBuffers(handle.fd, buffers, position,
729-
kUsePromises)) || 0;
768+
const bytesWritten = (await PromisePrototypeThen(
769+
binding.writeBuffers(handle.fd, buffers, position, kUsePromises),
770+
undefined,
771+
handleErrorFromBinding,
772+
)) || 0;
730773
return { __proto__: null, bytesWritten, buffers };
731774
}
732775

733776
async function rename(oldPath, newPath) {
734777
oldPath = getValidatedPath(oldPath, 'oldPath');
735778
newPath = getValidatedPath(newPath, 'newPath');
736-
return binding.rename(pathModule.toNamespacedPath(oldPath),
737-
pathModule.toNamespacedPath(newPath),
738-
kUsePromises);
779+
return await PromisePrototypeThen(
780+
binding.rename(pathModule.toNamespacedPath(oldPath),
781+
pathModule.toNamespacedPath(newPath),
782+
kUsePromises),
783+
undefined,
784+
handleErrorFromBinding,
785+
);
739786
}
740787

741788
async function truncate(path, len = 0) {
@@ -746,7 +793,11 @@ async function truncate(path, len = 0) {
746793
async function ftruncate(handle, len = 0) {
747794
validateInteger(len, 'len');
748795
len = MathMax(0, len);
749-
return binding.ftruncate(handle.fd, len, kUsePromises);
796+
return await PromisePrototypeThen(
797+
binding.ftruncate(handle.fd, len, kUsePromises),
798+
undefined,
799+
handleErrorFromBinding,
800+
);
750801
}
751802

752803
async function rm(path, options) {
@@ -767,15 +818,27 @@ async function rmdir(path, options) {
767818
}
768819
}
769820

770-
return binding.rmdir(path, kUsePromises);
821+
return await PromisePrototypeThen(
822+
binding.rmdir(path, kUsePromises),
823+
undefined,
824+
handleErrorFromBinding,
825+
);
771826
}
772827

773828
async function fdatasync(handle) {
774-
return binding.fdatasync(handle.fd, kUsePromises);
829+
return await PromisePrototypeThen(
830+
binding.fdatasync(handle.fd, kUsePromises),
831+
undefined,
832+
handleErrorFromBinding,
833+
);
775834
}
776835

777836
async function fsync(handle) {
778-
return binding.fsync(handle.fd, kUsePromises);
837+
return await PromisePrototypeThen(
838+
binding.fsync(handle.fd, kUsePromises),
839+
undefined,
840+
handleErrorFromBinding,
841+
);
779842
}
780843

781844
async function mkdir(path, options) {
@@ -789,21 +852,29 @@ async function mkdir(path, options) {
789852
path = getValidatedPath(path);
790853
validateBoolean(recursive, 'options.recursive');
791854

792-
return binding.mkdir(pathModule.toNamespacedPath(path),
793-
parseFileMode(mode, 'mode', 0o777), recursive,
794-
kUsePromises);
855+
return await PromisePrototypeThen(
856+
binding.mkdir(pathModule.toNamespacedPath(path),
857+
parseFileMode(mode, 'mode', 0o777), recursive,
858+
kUsePromises),
859+
undefined,
860+
handleErrorFromBinding,
861+
);
795862
}
796863

797864
async function readdirRecursive(originalPath, options) {
798865
const result = [];
799866
const queue = [
800867
[
801868
originalPath,
802-
await binding.readdir(
803-
pathModule.toNamespacedPath(originalPath),
804-
options.encoding,
805-
!!options.withFileTypes,
806-
kUsePromises,
869+
await PromisePrototypeThen(
870+
binding.readdir(
871+
pathModule.toNamespacedPath(originalPath),
872+
options.encoding,
873+
!!options.withFileTypes,
874+
kUsePromises,
875+
),
876+
undefined,
877+
handleErrorFromBinding,
807878
),
808879
],
809880
];
@@ -819,11 +890,15 @@ async function readdirRecursive(originalPath, options) {
819890
const direntPath = pathModule.join(path, dirent.name);
820891
ArrayPrototypePush(queue, [
821892
direntPath,
822-
await binding.readdir(
823-
direntPath,
824-
options.encoding,
825-
true,
826-
kUsePromises,
893+
await PromisePrototypeThen(
894+
binding.readdir(
895+
direntPath,
896+
options.encoding,
897+
true,
898+
kUsePromises,
899+
),
900+
undefined,
901+
handleErrorFromBinding,
827902
),
828903
]);
829904
}
@@ -842,11 +917,15 @@ async function readdirRecursive(originalPath, options) {
842917
if (stat === 1) {
843918
ArrayPrototypePush(queue, [
844919
direntPath,
845-
await binding.readdir(
846-
pathModule.toNamespacedPath(direntPath),
847-
options.encoding,
848-
false,
849-
kUsePromises,
920+
await PromisePrototypeThen(
921+
binding.readdir(
922+
pathModule.toNamespacedPath(direntPath),
923+
options.encoding,
924+
false,
925+
kUsePromises,
926+
),
927+
undefined,
928+
handleErrorFromBinding,
850929
),
851930
]);
852931
}
@@ -863,11 +942,15 @@ async function readdir(path, options) {
863942
if (options.recursive) {
864943
return readdirRecursive(path, options);
865944
}
866-
const result = await binding.readdir(
867-
pathModule.toNamespacedPath(path),
868-
options.encoding,
869-
!!options.withFileTypes,
870-
kUsePromises,
945+
const result = await PromisePrototypeThen(
946+
binding.readdir(
947+
pathModule.toNamespacedPath(path),
948+
options.encoding,
949+
!!options.withFileTypes,
950+
kUsePromises,
951+
),
952+
undefined,
953+
handleErrorFromBinding,
871954
);
872955
return options.withFileTypes ?
873956
getDirectoryEntriesPromise(path, result) :
@@ -877,8 +960,12 @@ async function readdir(path, options) {
877960
async function readlink(path, options) {
878961
options = getOptions(options);
879962
path = getValidatedPath(path, 'oldPath');
880-
return binding.readlink(pathModule.toNamespacedPath(path),
881-
options.encoding, kUsePromises);
963+
return await PromisePrototypeThen(
964+
binding.readlink(pathModule.toNamespacedPath(path),
965+
options.encoding, kUsePromises),
966+
undefined,
967+
handleErrorFromBinding,
968+
);
882969
}
883970

884971
async function symlink(target, path, type_) {
@@ -903,60 +990,96 @@ async function symlink(target, path, type_) {
903990

904991
target = getValidatedPath(target, 'target');
905992
path = getValidatedPath(path);
906-
return binding.symlink(preprocessSymlinkDestination(target, type, path),
907-
pathModule.toNamespacedPath(path),
908-
stringToSymlinkType(type),
909-
kUsePromises);
993+
return await PromisePrototypeThen(
994+
binding.symlink(preprocessSymlinkDestination(target, type, path),
995+
pathModule.toNamespacedPath(path),
996+
stringToSymlinkType(type),
997+
kUsePromises),
998+
undefined,
999+
handleErrorFromBinding,
1000+
);
9101001
}
9111002

9121003
async function fstat(handle, options = { bigint: false }) {
913-
const result = await binding.fstat(handle.fd, options.bigint, kUsePromises);
1004+
const result = await PromisePrototypeThen(
1005+
binding.fstat(handle.fd, options.bigint, kUsePromises),
1006+
undefined,
1007+
handleErrorFromBinding,
1008+
);
9141009
return getStatsFromBinding(result);
9151010
}
9161011

9171012
async function lstat(path, options = { bigint: false }) {
9181013
path = getValidatedPath(path);
919-
const result = await binding.lstat(pathModule.toNamespacedPath(path),
920-
options.bigint, kUsePromises);
1014+
const result = await PromisePrototypeThen(
1015+
binding.lstat(pathModule.toNamespacedPath(path),
1016+
options.bigint, kUsePromises),
1017+
undefined,
1018+
handleErrorFromBinding,
1019+
);
9211020
return getStatsFromBinding(result);
9221021
}
9231022

9241023
async function stat(path, options = { bigint: false }) {
9251024
path = getValidatedPath(path);
926-
const result = await binding.stat(pathModule.toNamespacedPath(path),
927-
options.bigint, kUsePromises);
1025+
const result = await PromisePrototypeThen(
1026+
binding.stat(pathModule.toNamespacedPath(path),
1027+
options.bigint, kUsePromises),
1028+
undefined,
1029+
handleErrorFromBinding,
1030+
);
9281031
return getStatsFromBinding(result);
9291032
}
9301033

9311034
async function statfs(path, options = { bigint: false }) {
9321035
path = getValidatedPath(path);
933-
const result = await binding.statfs(pathModule.toNamespacedPath(path),
934-
options.bigint, kUsePromises);
1036+
const result = await PromisePrototypeThen(
1037+
binding.statfs(pathModule.toNamespacedPath(path),
1038+
options.bigint, kUsePromises),
1039+
undefined,
1040+
handleErrorFromBinding,
1041+
);
9351042
return getStatFsFromBinding(result);
9361043
}
9371044

9381045
async function link(existingPath, newPath) {
9391046
existingPath = getValidatedPath(existingPath, 'existingPath');
9401047
newPath = getValidatedPath(newPath, 'newPath');
941-
return binding.link(pathModule.toNamespacedPath(existingPath),
942-
pathModule.toNamespacedPath(newPath),
943-
kUsePromises);
1048+
return await PromisePrototypeThen(
1049+
binding.link(pathModule.toNamespacedPath(existingPath),
1050+
pathModule.toNamespacedPath(newPath),
1051+
kUsePromises),
1052+
undefined,
1053+
handleErrorFromBinding,
1054+
);
9441055
}
9451056

9461057
async function unlink(path) {
9471058
path = getValidatedPath(path);
948-
return binding.unlink(pathModule.toNamespacedPath(path), kUsePromises);
1059+
return await PromisePrototypeThen(
1060+
binding.unlink(pathModule.toNamespacedPath(path), kUsePromises),
1061+
undefined,
1062+
handleErrorFromBinding,
1063+
);
9491064
}
9501065

9511066
async function fchmod(handle, mode) {
9521067
mode = parseFileMode(mode, 'mode');
953-
return binding.fchmod(handle.fd, mode, kUsePromises);
1068+
return await PromisePrototypeThen(
1069+
binding.fchmod(handle.fd, mode, kUsePromises),
1070+
undefined,
1071+
handleErrorFromBinding,
1072+
);
9541073
}
9551074

9561075
async function chmod(path, mode) {
9571076
path = getValidatedPath(path);
9581077
mode = parseFileMode(mode, 'mode');
959-
return binding.chmod(pathModule.toNamespacedPath(path), mode, kUsePromises);
1078+
return await PromisePrototypeThen(
1079+
binding.chmod(pathModule.toNamespacedPath(path), mode, kUsePromises),
1080+
undefined,
1081+
handleErrorFromBinding,
1082+
);
9601083
}
9611084

9621085
async function lchmod(path, mode) {
@@ -971,50 +1094,76 @@ async function lchown(path, uid, gid) {
9711094
path = getValidatedPath(path);
9721095
validateInteger(uid, 'uid', -1, kMaxUserId);
9731096
validateInteger(gid, 'gid', -1, kMaxUserId);
974-
return binding.lchown(pathModule.toNamespacedPath(path),
975-
uid, gid, kUsePromises);
1097+
return await PromisePrototypeThen(
1098+
binding.lchown(pathModule.toNamespacedPath(path), uid, gid, kUsePromises),
1099+
undefined,
1100+
handleErrorFromBinding,
1101+
);
9761102
}
9771103

9781104
async function fchown(handle, uid, gid) {
9791105
validateInteger(uid, 'uid', -1, kMaxUserId);
9801106
validateInteger(gid, 'gid', -1, kMaxUserId);
981-
return binding.fchown(handle.fd, uid, gid, kUsePromises);
1107+
return await PromisePrototypeThen(
1108+
binding.fchown(handle.fd, uid, gid, kUsePromises),
1109+
undefined,
1110+
handleErrorFromBinding,
1111+
);
9821112
}
9831113

9841114
async function chown(path, uid, gid) {
9851115
path = getValidatedPath(path);
9861116
validateInteger(uid, 'uid', -1, kMaxUserId);
9871117
validateInteger(gid, 'gid', -1, kMaxUserId);
988-
return binding.chown(pathModule.toNamespacedPath(path),
989-
uid, gid, kUsePromises);
1118+
return await PromisePrototypeThen(
1119+
binding.chown(pathModule.toNamespacedPath(path), uid, gid, kUsePromises),
1120+
undefined,
1121+
handleErrorFromBinding,
1122+
);
9901123
}
9911124

9921125
async function utimes(path, atime, mtime) {
9931126
path = getValidatedPath(path);
994-
return binding.utimes(pathModule.toNamespacedPath(path),
995-
toUnixTimestamp(atime),
996-
toUnixTimestamp(mtime),
997-
kUsePromises);
1127+
return await PromisePrototypeThen(
1128+
binding.utimes(pathModule.toNamespacedPath(path),
1129+
toUnixTimestamp(atime),
1130+
toUnixTimestamp(mtime),
1131+
kUsePromises),
1132+
undefined,
1133+
handleErrorFromBinding,
1134+
);
9981135
}
9991136

10001137
async function futimes(handle, atime, mtime) {
10011138
atime = toUnixTimestamp(atime, 'atime');
10021139
mtime = toUnixTimestamp(mtime, 'mtime');
1003-
return binding.futimes(handle.fd, atime, mtime, kUsePromises);
1140+
return await PromisePrototypeThen(
1141+
binding.futimes(handle.fd, atime, mtime, kUsePromises),
1142+
undefined,
1143+
handleErrorFromBinding,
1144+
);
10041145
}
10051146

10061147
async function lutimes(path, atime, mtime) {
10071148
path = getValidatedPath(path);
1008-
return binding.lutimes(pathModule.toNamespacedPath(path),
1009-
toUnixTimestamp(atime),
1010-
toUnixTimestamp(mtime),
1011-
kUsePromises);
1149+
return await PromisePrototypeThen(
1150+
binding.lutimes(pathModule.toNamespacedPath(path),
1151+
toUnixTimestamp(atime),
1152+
toUnixTimestamp(mtime),
1153+
kUsePromises),
1154+
undefined,
1155+
handleErrorFromBinding,
1156+
);
10121157
}
10131158

10141159
async function realpath(path, options) {
10151160
options = getOptions(options);
10161161
path = getValidatedPath(path);
1017-
return binding.realpath(pathModule.toNamespacedPath(path), options.encoding, kUsePromises);
1162+
return await PromisePrototypeThen(
1163+
binding.realpath(pathModule.toNamespacedPath(path), options.encoding, kUsePromises),
1164+
undefined,
1165+
handleErrorFromBinding,
1166+
);
10181167
}
10191168

10201169
async function mkdtemp(prefix, options) {
@@ -1030,7 +1179,11 @@ async function mkdtemp(prefix, options) {
10301179
path = Buffer.concat([prefix, Buffer.from('XXXXXX')]);
10311180
}
10321181

1033-
return binding.mkdtemp(path, options.encoding, kUsePromises);
1182+
return await PromisePrototypeThen(
1183+
binding.mkdtemp(path, options.encoding, kUsePromises),
1184+
undefined,
1185+
handleErrorFromBinding,
1186+
);
10341187
}
10351188

10361189
async function writeFile(path, data, options) {

‎test/parallel/test-fs-access.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,13 @@ fs.promises.access(readOnlyFile, fs.constants.R_OK)
9595
assert.strictEqual(err.code, 'ENOENT');
9696
assert.strictEqual(err.path, doesNotExist);
9797
};
98+
const expectedErrorPromise = (err) => {
99+
expectedError(err);
100+
assert.match(err.stack, /at async Object\.access/);
101+
};
98102
fs.access(doesNotExist, common.mustCall(expectedError));
99103
fs.promises.access(doesNotExist)
100-
.then(common.mustNotCall(), common.mustCall(expectedError))
104+
.then(common.mustNotCall(), common.mustCall(expectedErrorPromise))
101105
.catch(throwNextTick);
102106
}
103107

‎test/parallel/test-fs-promises-readfile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ async function validateWrongSignalParam() {
7272
async function validateZeroByteLiar() {
7373
const originalFStat = fsBinding.fstat;
7474
fsBinding.fstat = common.mustCall(
75-
() => (/* stat fields */ [0, 1, 2, 3, 4, 5, 6, 7, 0 /* size */])
75+
async () => (/* stat fields */ [0, 1, 2, 3, 4, 5, 6, 7, 0 /* size */])
7676
);
7777
const readBuffer = await readFile(fn);
7878
assert.strictEqual(readBuffer.toString(), largeBuffer.toString());

‎test/parallel/test-fs-promises.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ assert.strictEqual(
5757
{
5858
code: 'ENOENT',
5959
name: 'Error',
60-
message: /^ENOENT: no such file or directory, access/
60+
message: /^ENOENT: no such file or directory, access/,
61+
stack: /at async Function\.rejects/
6162
}
6263
).then(common.mustCall());
6364

0 commit comments

Comments
 (0)
Please sign in to comment.