@kazura/node-util
Version:
268 lines (260 loc) • 7.4 kB
JavaScript
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 };