@kazura/node-util
Version:
287 lines (276 loc) • 8.14 kB
JavaScript
;
const path = require('node:path');
const fs = require('node:fs');
const node_crypto = require('node:crypto');
const ddddocrNode = require('ddddocr-node');
const zx = require('zx');
const time2fa = require('time2fa');
const os = require('os');
const worker_threads = require('worker_threads');
const uuid = require('uuid');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const path__default = /*#__PURE__*/_interopDefaultCompat(path);
const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
const os__default = /*#__PURE__*/_interopDefaultCompat(os);
class Ocr extends ddddocrNode.DdddOcr {
static MODEL_TYPE = ddddocrNode.MODEL_TYPE;
static CHARSET_RANGE = ddddocrNode.CHARSET_RANGE;
static create(logSeverityLevel) {
return new Ocr(logSeverityLevel);
}
}
class GitUtil {
constructor(repositoryPath) {
this.repositoryPath = repositoryPath;
}
changeDirectory() {
zx.cd(this.repositoryPath);
}
async getBranchName() {
this.changeDirectory();
const branch = await zx.$`git rev-parse --abbrev-ref HEAD`;
return branch.toString().trim();
}
async getShortCommitId() {
this.changeDirectory();
const commitId = await zx.$`git rev-parse --short HEAD`;
return commitId.toString().trim();
}
async getCommitId() {
this.changeDirectory();
const commitId = await zx.$`git rev-parse HEAD`;
return commitId.toString().trim();
}
async getUserName() {
this.changeDirectory();
const username = await zx.$`git config user.name`;
return username.toString().trim();
}
async getUserEmail() {
this.changeDirectory();
const email = await zx.$`git config user.email`;
return email.toString().trim();
}
async add(file) {
this.changeDirectory();
await zx.$`git add ${file}`;
}
async addAll() {
this.changeDirectory();
await zx.$`git add .`;
}
async commit(message) {
this.changeDirectory();
await zx.$`git commit -m "${message}"`;
}
async commitAll(message) {
this.changeDirectory();
await zx.$`git commit -a -m ${message}`;
}
async submodulesUpdateInit() {
this.changeDirectory();
await zx.$`git submodule update --init`;
}
async pullRebaseOrigin() {
this.changeDirectory();
await zx.$`git pull --progress -v --rebase "origin"`;
}
async pullNoRebaseOrigin() {
this.changeDirectory();
await zx.$`git pull --progress -v --no-rebase "origin"`;
}
async checkout(branch) {
this.changeDirectory();
await zx.$`git checkout ${branch}`;
}
async checkoutOrigin(branch) {
this.changeDirectory();
await zx.$`git checkout -b ${branch} --track origin/${branch}`;
}
async pushOrigin(localBranch, remoteBranch) {
this.changeDirectory();
remoteBranch = remoteBranch ?? localBranch;
await zx.$`git push --progress -v "origin" ${localBranch}:${remoteBranch}`;
}
}
class TotpBase {
config;
constructor(config) {
this.config = time2fa.generateConfig(config);
}
generateSecret() {
return time2fa.generateSecret(this.config.secretSize);
}
generatePasscodes(secret) {
const backupCodes = time2fa.Totp.generatePasscodes({ secret }, this.config);
return backupCodes[0];
}
validate(passcode, secret) {
return time2fa.Totp.validate({ passcode, secret }, this.config);
}
generateUrl(issuer, user, secret) {
return time2fa.generateUrl({ issuer, user, secret }, this.config);
}
}
const Totp = new TotpBase();
class NodeWorker extends worker_threads.Worker {
WORKER_ID = uuid.v4();
currentResolve = null;
currentReject = null;
constructor(filename, options) {
super(filename, options);
}
}
class Worker {
constructor(filename, options = {}) {
this.filename = filename;
this.options = options;
this.options.max = this.options.max ?? Math.max(1, os__default.cpus().length - 1);
if (this.options.max < 1) {
throw new Error("options.max must be greater than 0");
}
this.options.workerData = Object.assign({}, this.options.workerData, {
WORKER_ID: uuid.v4()
// Worker 标识
});
}
/**
* 工作线程池
*/
pool = [];
/**
* 空闲的线程
*/
idlePool = [];
/**
* 等待的任务
*/
queue = [];
async run(...args) {
const worker = await this.getAvailableWorker();
return new Promise((resolve, reject) => {
worker.currentResolve = resolve;
worker.currentReject = reject;
worker.postMessage(args);
});
}
/**
* 停止所有工作线程
*/
stop() {
this.pool.forEach((w) => w.unref());
this.queue.forEach(
([_, reject]) => reject(new Error("Main worker pool stopped before a worker was available."))
);
this.pool = [];
this.idlePool = [];
this.queue = [];
}
/**
* 获取可用的工作线程
* @returns
*/
async getAvailableWorker() {
if (this.idlePool.length) {
return this.idlePool.shift();
}
if (this.pool.length < this.options.max) {
const worker = new NodeWorker(this.filename, this.options);
worker.on("message", (res) => {
worker.currentResolve && worker.currentResolve(res);
worker.currentResolve = null;
this.assignDoneWorker(worker);
});
worker.on("error", (err) => {
worker.currentReject && worker.currentReject(err);
worker.currentReject = null;
});
worker.on("exit", (code) => {
const i = this.pool.indexOf(worker);
if (i > -1) this.pool.splice(i, 1);
if (code !== 0 && worker.currentReject) {
worker.currentReject(new Error(`Worker stopped with non-0 exit code ${code}`));
worker.currentReject = null;
}
});
this.pool.push(worker);
return worker;
}
let resolve;
let reject;
const onWorkerAvailablePromise = new Promise((r, rj) => {
resolve = r;
reject = rj;
});
this.queue.push([resolve, reject]);
return onWorkerAvailablePromise;
}
/**
* 分配完成的工作线程
* @param worker
* @returns
*/
assignDoneWorker(worker) {
if (this.queue.length) {
const [resolve] = this.queue.shift();
resolve(worker);
return;
}
this.idlePool.push(worker);
}
}
class TaskWorker {
workerData = worker_threads.workerData;
constructor() {
worker_threads.parentPort.on("message", async (args) => {
const result = await this.doWork(...args);
worker_threads.parentPort.postMessage(result);
});
}
}
function lookupFile(dir, formats, options) {
for (const format of formats) {
const fullPath = path__default.join(dir, format);
if (fs__default.existsSync(fullPath) && fs__default.statSync(fullPath).isFile()) {
const result = options?.pathOnly ? fullPath : fs__default.readFileSync(fullPath, "utf-8");
if (!options?.predicate || options.predicate(result)) {
return result;
}
}
}
const parentDir = path__default.dirname(dir);
if (parentDir !== dir && (!options?.rootDir || parentDir.startsWith(options?.rootDir))) {
return lookupFile(parentDir, formats, options);
}
}
function getPkgJson(root) {
return JSON.parse(lookupFile(root, ["package.json"]) || `{}`);
}
function getPkgName(name) {
return name?.startsWith("@") ? name.split("/")[1] : name;
}
function writeFile(filename, content) {
const dir = path__default.dirname(filename);
if (!fs__default.existsSync(dir)) {
fs__default.mkdirSync(dir, { recursive: true });
}
fs__default.writeFileSync(filename, content);
}
function md5(text) {
return node_crypto.createHash("md5").update(text, "utf-8").digest("hex");
}
function getHash(text) {
return node_crypto.createHash("sha256").update(text, "utf-8").digest("hex").substring(0, 8);
}
exports.GitUtil = GitUtil;
exports.Ocr = Ocr;
exports.TaskWorker = TaskWorker;
exports.Totp = Totp;
exports.TotpBase = TotpBase;
exports.Worker = Worker;
exports.getHash = getHash;
exports.getPkgJson = getPkgJson;
exports.getPkgName = getPkgName;
exports.lookupFile = lookupFile;
exports.md5 = md5;
exports.writeFile = writeFile;