UNPKG

@kazura/node-util

Version:
268 lines (260 loc) 7.4 kB
import path from 'node:path'; import fs from 'node:fs'; import { createHash } from 'node:crypto'; import { DdddOcr, MODEL_TYPE, CHARSET_RANGE } from 'ddddocr-node'; import { cd, $ } from 'zx'; import { generateConfig, generateSecret, Totp as Totp$1, generateUrl } from 'time2fa'; import os from 'os'; import { Worker as Worker$1, workerData, parentPort } from 'worker_threads'; import { v4 } from 'uuid'; class Ocr extends DdddOcr { static MODEL_TYPE = MODEL_TYPE; static CHARSET_RANGE = CHARSET_RANGE; static create(logSeverityLevel) { return new Ocr(logSeverityLevel); } } class GitUtil { constructor(repositoryPath) { this.repositoryPath = repositoryPath; } changeDirectory() { cd(this.repositoryPath); } async getBranchName() { this.changeDirectory(); const branch = await $`git rev-parse --abbrev-ref HEAD`; return branch.toString().trim(); } async getShortCommitId() { this.changeDirectory(); const commitId = await $`git rev-parse --short HEAD`; return commitId.toString().trim(); } async getCommitId() { this.changeDirectory(); const commitId = await $`git rev-parse HEAD`; return commitId.toString().trim(); } async getUserName() { this.changeDirectory(); const username = await $`git config user.name`; return username.toString().trim(); } async getUserEmail() { this.changeDirectory(); const email = await $`git config user.email`; return email.toString().trim(); } async add(file) { this.changeDirectory(); await $`git add ${file}`; } async addAll() { this.changeDirectory(); await $`git add .`; } async commit(message) { this.changeDirectory(); await $`git commit -m "${message}"`; } async commitAll(message) { this.changeDirectory(); await $`git commit -a -m ${message}`; } async submodulesUpdateInit() { this.changeDirectory(); await $`git submodule update --init`; } async pullRebaseOrigin() { this.changeDirectory(); await $`git pull --progress -v --rebase "origin"`; } async pullNoRebaseOrigin() { this.changeDirectory(); await $`git pull --progress -v --no-rebase "origin"`; } async checkout(branch) { this.changeDirectory(); await $`git checkout ${branch}`; } async checkoutOrigin(branch) { this.changeDirectory(); await $`git checkout -b ${branch} --track origin/${branch}`; } async pushOrigin(localBranch, remoteBranch) { this.changeDirectory(); remoteBranch = remoteBranch ?? localBranch; await $`git push --progress -v "origin" ${localBranch}:${remoteBranch}`; } } class TotpBase { config; constructor(config) { this.config = generateConfig(config); } generateSecret() { return generateSecret(this.config.secretSize); } generatePasscodes(secret) { const backupCodes = Totp$1.generatePasscodes({ secret }, this.config); return backupCodes[0]; } validate(passcode, secret) { return Totp$1.validate({ passcode, secret }, this.config); } generateUrl(issuer, user, secret) { return generateUrl({ issuer, user, secret }, this.config); } } const Totp = new TotpBase(); class NodeWorker extends Worker$1 { WORKER_ID = 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.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: 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 = workerData; constructor() { parentPort.on("message", async (args) => { const result = await this.doWork(...args); parentPort.postMessage(result); }); } } function lookupFile(dir, formats, options) { for (const format of formats) { const fullPath = path.join(dir, format); if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) { const result = options?.pathOnly ? fullPath : fs.readFileSync(fullPath, "utf-8"); if (!options?.predicate || options.predicate(result)) { return result; } } } const parentDir = path.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.dirname(filename); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(filename, content); } function md5(text) { return createHash("md5").update(text, "utf-8").digest("hex"); } function getHash(text) { return createHash("sha256").update(text, "utf-8").digest("hex").substring(0, 8); } export { GitUtil, Ocr, TaskWorker, Totp, TotpBase, Worker, getHash, getPkgJson, getPkgName, lookupFile, md5, writeFile };