@secret-momo/utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # utils
2
+ 个人常用的工具库集合
@@ -0,0 +1,15 @@
1
+ export declare function dateTimeToNow(fmt?: string): string;
2
+ export declare function dateToNow(fmt?: string): string;
3
+ /**
4
+ * 获取昨天日期
5
+ * @param fmt 日期格式
6
+ * @returns 昨天日期(YYYY-MM-DD)
7
+ */
8
+ export declare function yesterdayDate(fmt?: string): string;
9
+ /**
10
+ * 将时间戳转换为日期
11
+ * @param timestamp 时间戳(毫秒)
12
+ * @returns 日期(YYYY-MM-DD)
13
+ */
14
+ export declare function timestampToDate(timestamp: number, fmt?: string): string;
15
+ //# sourceMappingURL=date-time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-time.d.ts","sourceRoot":"","sources":["../src/date-time.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,GAAG,SAAwB,GAAG,MAAM,CAEjE;AAED,wBAAgB,SAAS,CAAC,GAAG,SAAe,GAAG,MAAM,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,SAAe,GAAG,MAAM,CAExD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,SAAe,GAAG,MAAM,CAE7E"}
@@ -0,0 +1,16 @@
1
+ import dayjs from 'dayjs';
2
+
3
+ function dateTimeToNow(fmt = "YYYY-MM-DD HH:mm:ss") {
4
+ return dayjs().format(fmt);
5
+ }
6
+ function dateToNow(fmt = "YYYY-MM-DD") {
7
+ return dayjs().format(fmt);
8
+ }
9
+ function yesterdayDate(fmt = "YYYY-MM-DD") {
10
+ return dayjs().subtract(1, "day").format(fmt);
11
+ }
12
+ function timestampToDate(timestamp, fmt = "YYYY-MM-DD") {
13
+ return dayjs(timestamp).format(fmt);
14
+ }
15
+
16
+ export { dateTimeToNow, dateToNow, timestampToDate, yesterdayDate };
package/lib/env.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function getNodeName(): string;
2
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAgDA,wBAAgB,WAAW,IAAI,MAAM,CAIpC"}
package/lib/env.js ADDED
@@ -0,0 +1,39 @@
1
+ import { execSync } from 'child_process';
2
+
3
+ let macHardwareInfo = null;
4
+ function getMacHardwareInfo() {
5
+ if (macHardwareInfo) {
6
+ return macHardwareInfo;
7
+ }
8
+ try {
9
+ const output = execSync("system_profiler SPHardwareDataType", {
10
+ encoding: "utf-8"
11
+ });
12
+ const getValue = (key) => {
13
+ const regex = new RegExp(`${key}:\\s+(.+)`);
14
+ const match = output.match(regex);
15
+ return match?.[1]?.trim() ?? null;
16
+ };
17
+ macHardwareInfo = {
18
+ modelName: getValue("Model Name"),
19
+ modelIdentifier: getValue("Model Identifier"),
20
+ chip: getValue("Chip") || getValue("Processor Name"),
21
+ // 兼容 Intel
22
+ raw: output
23
+ };
24
+ return macHardwareInfo;
25
+ } catch (_e) {
26
+ return {
27
+ modelName: null,
28
+ modelIdentifier: null,
29
+ chip: null,
30
+ raw: ""
31
+ };
32
+ }
33
+ }
34
+ function getNodeName() {
35
+ const { modelName, chip } = getMacHardwareInfo();
36
+ return `${modelName}(${chip})`;
37
+ }
38
+
39
+ export { getNodeName };
package/lib/fetch.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ export declare const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
2
+ /**
3
+ * 对 `globalThis.fetch` 的轻量封装:
4
+ * - 最多重试 `MAX_RETRIES` 次(默认 5 次),总尝试次数为 `MAX_RETRIES + 1`。
5
+ * - 仅在可重试异常(如超时、连接重置、网络错误)时重试。
6
+ * - 重试间隔采用指数退避:`2^attempt` 秒,即 `1s, 2s, 4s, 8s, 16s`。
7
+ * - 若请求信号已中止(`init.signal.aborted`),则不会继续重试并直接抛出错误。
8
+ */
9
+ export declare function fetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
10
+ export declare function fetchText(input: string | URL | Request, init?: RequestInit): Promise<string>;
11
+ export declare function fetchHtml(input: string | URL | Request, init?: RequestInit): Promise<string>;
12
+ export declare function fetchJson(input: string | URL | Request, init?: RequestInit): Promise<unknown>;
13
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../src/fetch.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,SAAS,oHAC6F,CAAC;AAqEpH;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkChG;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,OAAO,CAAC,CAgBlB"}
package/lib/fetch.js ADDED
@@ -0,0 +1,118 @@
1
+ import { warn } from './log.js';
2
+ import { sleep } from './sleep.js';
3
+
4
+ const MAX_RETRIES = 5;
5
+ const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
6
+ function originFromUrl(url) {
7
+ try {
8
+ const parsed = new URL(url);
9
+ return parsed.origin;
10
+ } catch {
11
+ return url;
12
+ }
13
+ }
14
+ function refererFromUrl(url) {
15
+ return originFromUrl(url) + "/";
16
+ }
17
+ function formatErrorSummary(error) {
18
+ const err = error;
19
+ const name = err?.name ?? "Error";
20
+ const message = err?.message ?? String(error);
21
+ const code = err?.cause?.code ? ` [code=${String(err.cause.code)}]` : "";
22
+ return `${name}: ${message}${code}`;
23
+ }
24
+ function isRetryableError(error, signal) {
25
+ if (signal?.aborted) {
26
+ return false;
27
+ }
28
+ const err = error;
29
+ const message = (err?.message ?? String(error)).toLowerCase();
30
+ const code = String(err?.cause?.code ?? "").toLowerCase();
31
+ const isNetworkLikeMessage = [
32
+ "network",
33
+ "socket",
34
+ "econnreset",
35
+ "econnrefused",
36
+ "etimedout",
37
+ "timeout",
38
+ "failed to fetch",
39
+ "unable to connect",
40
+ // TLS/证书错误(常见于本地代理 Clash / 系统证书轮换后 session ticket 失效)
41
+ "certificate",
42
+ "unknown cert",
43
+ "ssl",
44
+ "tls"
45
+ ].some((m) => message.includes(m));
46
+ const isNetworkLikeCode = ["econnreset", "econnrefused", "etimedout"].some((m) => code === m);
47
+ return err?.name === "TimeoutError" || err?.name === "AbortError" || isNetworkLikeMessage || isNetworkLikeCode;
48
+ }
49
+ async function fetch(input, init) {
50
+ const originalSignal = init?.signal ?? void 0;
51
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
52
+ try {
53
+ init = {
54
+ ...init,
55
+ headers: {
56
+ ...init?.headers,
57
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
58
+ Referer: refererFromUrl(input.toString()),
59
+ "User-Agent": userAgent
60
+ }
61
+ };
62
+ return await globalThis.fetch(input, init);
63
+ } catch (error) {
64
+ const shouldRetry = attempt < MAX_RETRIES && isRetryableError(error, originalSignal);
65
+ if (!shouldRetry) {
66
+ throw error;
67
+ }
68
+ const totalAttempts = MAX_RETRIES + 1;
69
+ const currentAttempt = attempt + 1;
70
+ const seconds = Math.pow(2, attempt);
71
+ const remainingRetries = MAX_RETRIES - attempt - 1;
72
+ warn(
73
+ `\u8BF7\u6C42\u5931\u8D25\uFF0C\u7B2C ${currentAttempt}/${totalAttempts} \u6B21\u5C1D\u8BD5\uFF0C${seconds}s \u540E\u91CD\u8BD5\uFF08\u5269\u4F59 ${remainingRetries} \u6B21\uFF09\uFF1A${input}\uFF1B\u9519\u8BEF\uFF1A${formatErrorSummary(error)}`
74
+ );
75
+ await sleep(1e3 * seconds);
76
+ }
77
+ }
78
+ throw new Error("Unexpected fetch retry failure");
79
+ }
80
+ async function fetchText(input, init) {
81
+ return (await fetch(input, init)).text();
82
+ }
83
+ async function fetchHtml(input, init) {
84
+ init = {
85
+ ...init,
86
+ headers: {
87
+ ...init?.headers,
88
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
89
+ "Cache-Control": "no-cache",
90
+ "Content-Type": "text/html",
91
+ Pragma: "no-cache",
92
+ "Upgrade-Insecure-Requests": "1",
93
+ "Sec-Fetch-Site": "none",
94
+ "Sec-Fetch-Mode": "navigate",
95
+ "Sec-Fetch-User": "?1",
96
+ "Sec-Fetch-Dest": "document"
97
+ }
98
+ };
99
+ return await fetchText(input, init);
100
+ }
101
+ async function fetchJson(input, init) {
102
+ init = {
103
+ ...init,
104
+ headers: {
105
+ ...init?.headers,
106
+ Accept: "application/json, text/plain, */*",
107
+ Connection: "keep-alive",
108
+ "Content-Type": "application/json",
109
+ Origin: originFromUrl(input.toString()),
110
+ "Sec-Fetch-Site": "same-site",
111
+ "Sec-Fetch-Mode": "cors",
112
+ "Sec-Fetch-Dest": "empty"
113
+ }
114
+ };
115
+ return (await fetch(input, init)).json();
116
+ }
117
+
118
+ export { fetch, fetchHtml, fetchJson, fetchText, userAgent };
package/lib/index.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export * from './date-time';
2
+ export * from './env';
3
+ export * from './fetch';
4
+ export * from './log';
5
+ export * from './num';
6
+ export * from './sleep';
7
+ export * from './uuid';
8
+ export * from './url';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC"}
package/lib/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { dateTimeToNow, dateToNow, timestampToDate, yesterdayDate } from './date-time.js';
2
+ export { getNodeName } from './env.js';
3
+ export { fetch, fetchHtml, fetchJson, fetchText, userAgent } from './fetch.js';
4
+ export { error, info, log, success, warn } from './log.js';
5
+ export { fixed2, fixed4, toPercentage } from './num.js';
6
+ export { sleep, sleep_random } from './sleep.js';
7
+ export { genId, uuidv4 } from './uuid.js';
8
+ export { getRedirectUrl, removeQueryParams } from './url.js';
9
+ export { default as consola } from 'consola';
package/lib/log.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import consola from 'consola';
2
+ export { consola };
3
+ export declare function log(message: string): void;
4
+ export declare function warn(message: string): void;
5
+ export declare function error(e: unknown): void;
6
+ export declare function success(message: string): void;
7
+ export declare function info(message: string): void;
8
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,QAElC;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,QAEnC;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,OAAO,QAE/B;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,QAEtC;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,QAEnC"}
package/lib/log.js ADDED
@@ -0,0 +1,20 @@
1
+ import consola from 'consola';
2
+ export { default as consola } from 'consola';
3
+
4
+ function log(message) {
5
+ consola.log(message);
6
+ }
7
+ function warn(message) {
8
+ consola.warn(message);
9
+ }
10
+ function error(e) {
11
+ consola.error(e);
12
+ }
13
+ function success(message) {
14
+ consola.success(message);
15
+ }
16
+ function info(message) {
17
+ consola.info(message);
18
+ }
19
+
20
+ export { error, info, log, success, warn };
package/lib/num.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export declare function fixed2(n: number): number;
2
+ export declare function fixed4(n: number): number;
3
+ /**
4
+ * 将数字转为百分比字符串,如:12.34%
5
+ *
6
+ * @param n 数字,如:0.1234
7
+ * @returns 百分比字符串,如:12.34%
8
+ */
9
+ export declare function toPercentage(n?: number): string;
10
+ //# sourceMappingURL=num.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"num.d.ts","sourceRoot":"","sources":["../src/num.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAExC;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAExC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI/C"}
package/lib/num.js ADDED
@@ -0,0 +1,12 @@
1
+ function fixed2(n) {
2
+ return Number(n.toFixed(2));
3
+ }
4
+ function fixed4(n) {
5
+ return Number(n.toFixed(4));
6
+ }
7
+ function toPercentage(n) {
8
+ if (n === void 0) return "-";
9
+ return `${fixed2(n * 100)}%`;
10
+ }
11
+
12
+ export { fixed2, fixed4, toPercentage };
package/lib/sleep.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function sleep(ms: number): Promise<unknown>;
2
+ export declare function sleep_random(): Promise<unknown>;
3
+ //# sourceMappingURL=sleep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../src/sleep.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,oBAE/B;AAED,wBAAgB,YAAY,qBAE3B"}
package/lib/sleep.js ADDED
@@ -0,0 +1,8 @@
1
+ function sleep(ms) {
2
+ return new Promise((resolve) => setTimeout(resolve, ms));
3
+ }
4
+ function sleep_random() {
5
+ return sleep(Math.floor(Math.random() * 500) + 750);
6
+ }
7
+
8
+ export { sleep, sleep_random };
package/lib/url.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 移除 url 中的 query 参数
3
+ */
4
+ export declare function removeQueryParams(url: string): string;
5
+ export declare function getRedirectUrl(url: string): Promise<string>;
6
+ //# sourceMappingURL=url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAajE"}
package/lib/url.js ADDED
@@ -0,0 +1,18 @@
1
+ import { fetch, userAgent } from './fetch.js';
2
+
3
+ function removeQueryParams(url) {
4
+ return url.split("?")[0] ?? url;
5
+ }
6
+ async function getRedirectUrl(url) {
7
+ const res = await fetch(url, {
8
+ method: "GET",
9
+ redirect: "manual",
10
+ headers: { "User-Agent": userAgent }
11
+ });
12
+ if (res.status >= 300 && res.status < 400) {
13
+ return res.headers.get("location") || url;
14
+ }
15
+ return url;
16
+ }
17
+
18
+ export { getRedirectUrl, removeQueryParams };
package/lib/uuid.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare function genId(len?: number): string;
2
+ export declare function uuidv4(): `${string}-${string}-${string}-${string}-${string}`;
3
+ //# sourceMappingURL=uuid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,GAAG,SAAK,UAW7B;AAED,wBAAgB,MAAM,wDAErB"}
package/lib/uuid.js ADDED
@@ -0,0 +1,14 @@
1
+ function genId(len = 10) {
2
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
3
+ let result = "";
4
+ const bytes = crypto.getRandomValues(new Uint8Array(len));
5
+ for (let i = 0; i < len; i++) {
6
+ result += chars[bytes[i] % chars.length];
7
+ }
8
+ return result;
9
+ }
10
+ function uuidv4() {
11
+ return crypto.randomUUID();
12
+ }
13
+
14
+ export { genId, uuidv4 };
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@secret-momo/utils",
3
+ "version": "1.0.0",
4
+ "module": "src/index.ts",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "files": [
10
+ "lib"
11
+ ],
12
+ "lint-staged": {
13
+ "*.{ts,tsx,js,jsx}": [
14
+ "bunx eslint --fix",
15
+ "bunx prettier --write"
16
+ ],
17
+ "*.{json,md}": [
18
+ "bunx prettier --write"
19
+ ]
20
+ },
21
+ "scripts": {
22
+ "prepublishOnly": "bun run build",
23
+ "build": "rm -rf lib && rollup -c && tsc -p tsconfig.json"
24
+ },
25
+ "devDependencies": {
26
+ "@eslint/config-helpers": "0.6.0",
27
+ "@eslint/js": "10.0.1",
28
+ "@types/bun": "1.3.14",
29
+ "eslint": "10.4.1",
30
+ "eslint-plugin-import": "2.32.0",
31
+ "eslint-plugin-react": "7.37.5",
32
+ "eslint-plugin-react-hooks": "7.1.1",
33
+ "eslint-plugin-unused-imports": "4.4.1",
34
+ "husky": "9.1.7",
35
+ "jiti": "2.7.0",
36
+ "lint-staged": "16.4.0",
37
+ "prettier": "3.8.3",
38
+ "rollup": "4.61.1",
39
+ "rollup-plugin-esbuild": "6.2.1",
40
+ "typescript": "6.0.3",
41
+ "typescript-eslint": "8.60.1"
42
+ },
43
+ "dependencies": {
44
+ "consola": "3.4.2",
45
+ "dayjs": "1.11.21"
46
+ }
47
+ }