mktemp
Version:
create temporary files and directories
145 lines (144 loc) • 3.86 kB
JavaScript
import { generateUniqueName, getOutcomeCount } from "./unique_name.mjs";
import fs from "node:fs";
//#region src/creation.ts
/** permission of 0600 -r-------- */
const _r________ = 384;
/** permission of 0700 -rw------- */
const _rw_______ = 448;
/**
* getOutcomeCount("X") * 1 → 1 - (35/36)^(36*1) = 0.63728996689
* ≈ 63%
* 63% chance of success, 37% chance of failure
*
* getOutcomeCount("X") * 3 → 1 - (35/36)^(36*3) = 0.9522823874
* ≈ 95%
* 95% chance of success, 5% chance of failure
*/
const RETRY_MULTIPLIER = 3;
/**
* check mode is fs.Mode
*
* @param mode - target value
* @returns true if mode is fs.Mode
*/
function isMode(mode) {
return typeof mode === "number" || typeof mode === "string";
}
/**
* check error is NodeJS.ErrnoException
*
* @param error - target value
* @returns true if error is NodeJS.ErrnoException
*/
function isErrnoException(error) {
if (typeof error === "object" && error !== null) return "code" in error;
return false;
}
/**
* try create unique name file or directory
*/
function tryCreate({ callback, isDir, mode, retryCount, template }) {
const path = generateUniqueName(template);
const fn = (err, fd) => {
if (err) {
if (err.code === "EEXIST") if (retryCount > 0) setImmediate(tryCreate, {
callback,
isDir,
mode,
retryCount: retryCount - 1,
template
});
else callback(/* @__PURE__ */ new RangeError("over max retry count"), null);
else callback(err, null);
return;
}
if (fd) fs.close(fd, (err) => {
callback(err, path);
});
else callback(null, path);
};
if (isDir) fs.mkdir(path, mode, fn);
else fs.open(path, "ax+", mode, fn);
}
function createFile(template, mode = _r________, callback) {
if (typeof mode === "function") {
callback = mode;
mode = _r________;
}
if (typeof callback !== "function") return new Promise((resolve, reject) => {
if (isMode(mode)) createFile(template, mode, (err, path) => {
if (err) reject(err);
else resolve(path);
});
else reject(/* @__PURE__ */ new TypeError(`mode must be a fs.Mode: ${mode}`));
});
tryCreate({
callback,
isDir: false,
mode,
retryCount: getOutcomeCount(template) * RETRY_MULTIPLIER,
template
});
}
function createFileSync(template, mode = _r________) {
let path;
let isExist;
let retryCount = getOutcomeCount(template);
do {
isExist = false;
path = generateUniqueName(template);
let fd = null;
try {
fd = fs.openSync(path, "ax+", mode);
} catch (err) {
if (isErrnoException(err) && err.code === "EEXIST") if (retryCount > 0) isExist = true;
else throw new RangeError("over max retry count");
else throw err;
} finally {
if (fd !== null) fs.closeSync(fd);
}
retryCount -= 1;
} while (isExist);
return path;
}
function createDir(template, mode = _rw_______, callback) {
if (typeof mode === "function") {
callback = mode;
mode = _rw_______;
}
if (typeof callback !== "function") return new Promise((resolve, reject) => {
if (isMode(mode)) createDir(template, mode, (err, path) => {
if (err) reject(err);
else resolve(path);
});
else reject(/* @__PURE__ */ new TypeError(`mode must be a fs.Mode: ${mode}`));
});
tryCreate({
callback,
isDir: true,
mode,
retryCount: getOutcomeCount(template) * RETRY_MULTIPLIER,
template
});
}
function createDirSync(template, mode = _rw_______) {
let path;
let isExist;
let retryCount = getOutcomeCount(template);
do {
isExist = false;
path = generateUniqueName(template);
try {
fs.mkdirSync(path, mode);
} catch (err) {
if (isErrnoException(err) && err.code === "EEXIST") if (retryCount > 0) isExist = true;
else throw new RangeError("over max retry count");
else throw err;
}
retryCount -= 1;
} while (isExist);
return path;
}
//#endregion
export { createDir, createDirSync, createFile, createFileSync };
//# sourceMappingURL=creation.mjs.map