@nickyzj2023/utils 1.0.51 → 1.0.53
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/dist/dom/index.d.ts +1 -0
- package/dist/dom/log.d.ts +26 -0
- package/{src/dom/timeLog.ts → dist/dom/timeLog.d.ts} +8 -10
- package/dist/function/index.d.ts +1 -0
- package/dist/function/loop-until.d.ts +22 -0
- package/dist/function/loopUntil.d.ts +22 -0
- package/dist/hoc/index.d.ts +1 -0
- package/dist/hoc/with-cache.d.ts +42 -0
- package/dist/hoc/withCache.d.ts +42 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +3 -0
- package/dist/is/index.d.ts +4 -0
- package/dist/is/is-falsy.d.ts +9 -0
- package/dist/is/is-nil.d.ts +9 -0
- package/dist/is/is-object.d.ts +8 -0
- package/dist/is/is-primitive.d.ts +9 -0
- package/dist/is/isFalsy.d.ts +9 -0
- package/dist/is/isNil.d.ts +9 -0
- package/dist/is/isObject.d.ts +8 -0
- package/dist/is/isPrimitive.d.ts +9 -0
- package/{src/is/isTruthy.ts → dist/is/isTruthy.d.ts} +8 -10
- package/dist/lru-cache.d.ts +18 -0
- package/dist/network/fetcher.d.ts +56 -0
- package/dist/network/get-real-url.d.ts +2 -0
- package/dist/network/getRealURL.d.ts +2 -0
- package/dist/network/image.d.ts +59 -0
- package/dist/network/index.d.ts +4 -0
- package/dist/network/to.d.ts +9 -0
- package/dist/number/index.d.ts +1 -0
- package/dist/number/random-int.d.ts +7 -0
- package/dist/number/randomInt.d.ts +7 -0
- package/dist/object/index.d.ts +3 -0
- package/dist/object/map-keys.d.ts +17 -0
- package/dist/object/map-values.d.ts +21 -0
- package/dist/object/mapKeys.d.ts +17 -0
- package/dist/object/mapValues.d.ts +21 -0
- package/dist/object/merge-objects.d.ts +12 -0
- package/dist/object/mergeObjects.d.ts +12 -0
- package/dist/string/case.d.ts +32 -0
- package/dist/string/compact.d.ts +22 -0
- package/dist/string/index.d.ts +2 -0
- package/dist/time/debounce.d.ts +20 -0
- package/dist/time/index.d.ts +3 -0
- package/dist/time/sleep.d.ts +7 -0
- package/dist/time/throttle.d.ts +20 -0
- package/docs/assets/navigation.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/functions/camelToSnake.html +1 -1
- package/docs/functions/capitalize.html +1 -1
- package/docs/functions/compactStr.html +1 -1
- package/docs/functions/debounce.html +1 -1
- package/docs/functions/decapitalize.html +1 -1
- package/docs/functions/fetcher.html +1 -1
- package/docs/functions/getRealURL.html +1 -1
- package/docs/functions/imageUrlToBase64.html +1 -1
- package/docs/functions/isFalsy.html +1 -1
- package/docs/functions/isNil.html +1 -1
- package/docs/functions/isObject.html +1 -1
- package/docs/functions/isPrimitive.html +1 -1
- package/docs/functions/{isTruthy.html → log.html} +6 -4
- package/docs/functions/loopUntil.html +1 -1
- package/docs/functions/mapKeys.html +1 -1
- package/docs/functions/mapValues.html +1 -1
- package/docs/functions/mergeObjects.html +1 -1
- package/docs/functions/randomInt.html +1 -1
- package/docs/functions/sleep.html +1 -1
- package/docs/functions/snakeToCamel.html +1 -1
- package/docs/functions/throttle.html +1 -1
- package/docs/functions/to.html +1 -1
- package/docs/functions/withCache.html +3 -3
- package/docs/{functions/timeLog.html → interfaces/LogOptions.html} +11 -4
- package/docs/modules.html +1 -1
- package/docs/types/BunFetchOptions.html +2 -2
- package/docs/types/CamelToSnake.html +1 -1
- package/docs/types/Capitalize.html +1 -1
- package/docs/types/Decapitalize.html +1 -1
- package/docs/types/DeepMapKeys.html +1 -1
- package/docs/types/DeepMapValues.html +1 -1
- package/docs/types/Falsy.html +1 -1
- package/docs/types/ImageCompressionOptions.html +4 -4
- package/docs/types/Primitive.html +1 -1
- package/docs/types/RequestInit.html +1 -1
- package/docs/types/SetTtl.html +1 -1
- package/docs/types/SnakeToCamel.html +1 -1
- package/package.json +1 -1
- package/src/dom/index.ts +1 -1
- package/src/dom/log.ts +62 -0
- package/src/function/index.ts +1 -1
- package/src/hoc/index.ts +1 -1
- package/src/is/index.ts +4 -5
- package/src/network/fetcher.ts +1 -0
- package/src/network/index.ts +4 -4
- package/src/number/index.ts +1 -1
- package/src/object/index.ts +3 -3
- package/src/string/index.ts +2 -12
- package/src/time/index.ts +3 -3
- package/src/lru-cache.ts +0 -50
- /package/src/function/{loopUntil.ts → loop-until.ts} +0 -0
- /package/src/hoc/{withCache.ts → with-cache.ts} +0 -0
- /package/src/is/{isFalsy.ts → is-falsy.ts} +0 -0
- /package/src/is/{isNil.ts → is-nil.ts} +0 -0
- /package/src/is/{isObject.ts → is-object.ts} +0 -0
- /package/src/is/{isPrimitive.ts → is-primitive.ts} +0 -0
- /package/src/network/{getRealURL.ts → get-real-url.ts} +0 -0
- /package/src/number/{randomInt.ts → random-int.ts} +0 -0
- /package/src/object/{mapKeys.ts → map-keys.ts} +0 -0
- /package/src/object/{mapValues.ts → map-values.ts} +0 -0
- /package/src/object/{mergeObjects.ts → merge-objects.ts} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./log";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* log 配置选项
|
|
3
|
+
*/
|
|
4
|
+
export interface LogOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 是否显示时间
|
|
7
|
+
* @default true
|
|
8
|
+
*/
|
|
9
|
+
time?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* 是否显示调用者文件名
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
fileName?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 带额外信息的 console.log
|
|
18
|
+
* @param message - 日志消息
|
|
19
|
+
* @param options - 配置选项
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* log("调试信息"); // "调试信息"
|
|
23
|
+
* log("调试信息", { time: true }); // "[14:30:00] 调试信息"
|
|
24
|
+
* log("调试信息", { time: true, fileName: true }); // "[14:30:00] [index.ts:15] 调试信息"
|
|
25
|
+
*/
|
|
26
|
+
export declare const log: (message: any, options?: LogOptions) => void;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 附带时间的 console.log
|
|
3
|
-
* @param args
|
|
4
|
-
*
|
|
5
|
-
* @example
|
|
6
|
-
* timeLog("Hello", "World"); // 14:30:00 Hello World
|
|
7
|
-
*/
|
|
8
|
-
export const timeLog
|
|
9
|
-
console.log(`${new Date().toLocaleTimeString()}`, ...args);
|
|
10
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* 附带时间的 console.log
|
|
3
|
+
* @param args
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* timeLog("Hello", "World"); // 14:30:00 Hello World
|
|
7
|
+
*/
|
|
8
|
+
export declare const timeLog: (...args: any[]) => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./loop-until";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 循环执行函数,直到符合停止条件
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* // 循环请求大语言模型,直到其不再调用工具
|
|
6
|
+
* loopUntil(
|
|
7
|
+
* async () => {
|
|
8
|
+
* const completion = await chatCompletions();
|
|
9
|
+
* completion.tool_calls?.forEach(chooseAndHandleTool)
|
|
10
|
+
* return completion;
|
|
11
|
+
* },
|
|
12
|
+
* {
|
|
13
|
+
* shouldStop: (completion) => !completion.tool_calls,
|
|
14
|
+
* },
|
|
15
|
+
* ),
|
|
16
|
+
*/
|
|
17
|
+
export declare const loopUntil: <T>(fn: (count: number) => Promise<T>, options?: {
|
|
18
|
+
/** 最大循环次数,默认 5 次 */
|
|
19
|
+
maxRetries?: number;
|
|
20
|
+
/** 停止循环条件,默认立即停止 */
|
|
21
|
+
shouldStop?: (result: T) => boolean;
|
|
22
|
+
}) => Promise<T>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 循环执行函数,直到符合停止条件
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* // 循环请求大语言模型,直到其不再调用工具
|
|
6
|
+
* loopUntil(
|
|
7
|
+
* async () => {
|
|
8
|
+
* const completion = await chatCompletions();
|
|
9
|
+
* completion.tool_calls?.forEach(chooseAndHandleTool)
|
|
10
|
+
* return completion;
|
|
11
|
+
* },
|
|
12
|
+
* {
|
|
13
|
+
* shouldStop: (completion) => !completion.tool_calls,
|
|
14
|
+
* },
|
|
15
|
+
* ),
|
|
16
|
+
*/
|
|
17
|
+
export declare const loopUntil: <T>(fn: (count: number) => Promise<T>, options?: {
|
|
18
|
+
/** 最大循环次数,默认 5 次 */
|
|
19
|
+
maxRetries?: number;
|
|
20
|
+
/** 停止循环条件,默认立即停止 */
|
|
21
|
+
shouldStop?: (result: T) => boolean;
|
|
22
|
+
}) => Promise<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./with-cache";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type SetTtl = (seconds: number) => void;
|
|
2
|
+
/**
|
|
3
|
+
* 创建一个带缓存的高阶函数
|
|
4
|
+
*
|
|
5
|
+
* @template Args 被包装函数的参数类型数组
|
|
6
|
+
* @template Result 被包装函数的返回类型
|
|
7
|
+
*
|
|
8
|
+
* @param fn 需要被缓存的函数,参数里附带的 setTtl 方法用于根据具体情况改写过期时间
|
|
9
|
+
* @param ttlSeconds 以秒为单位的过期时间,-1 表示永不过期,默认 -1,会被回调函数里的 setTtl() 覆盖
|
|
10
|
+
*
|
|
11
|
+
* @returns 返回包装后的函数,以及缓存相关的额外方法
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // 异步函数示例
|
|
15
|
+
* const fetchData = withCache(async function (url: string) {
|
|
16
|
+
* const data = await fetch(url).then((res) => res.json());
|
|
17
|
+
* this.setTtl(data.expiresIn); // 根据实际情况调整过期时间
|
|
18
|
+
* return data;
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* await fetchData(urlA);
|
|
22
|
+
* await fetchData(urlA); // 使用缓存结果
|
|
23
|
+
* await fetchData(urlB);
|
|
24
|
+
* await fetchData(urlB); // 使用缓存结果
|
|
25
|
+
*
|
|
26
|
+
* fetchData.clear(); // 清除缓存
|
|
27
|
+
* await fetchData(urlA); // 重新请求
|
|
28
|
+
* await fetchData(urlB); // 重新请求
|
|
29
|
+
*
|
|
30
|
+
* // 缓存过期前
|
|
31
|
+
* await sleep();
|
|
32
|
+
* fetchData.updateTtl(180); // 更新 ttl 并为所有未过期的缓存续期
|
|
33
|
+
* await fetchData(urlA); // 使用缓存结果
|
|
34
|
+
* await fetchData(urlB); // 使用缓存结果
|
|
35
|
+
*/
|
|
36
|
+
export declare const withCache: <Args extends any[], Result>(fn: (this: {
|
|
37
|
+
setTtl: SetTtl;
|
|
38
|
+
}, ...args: Args) => Result, ttlSeconds?: number) => {
|
|
39
|
+
(...args: Args): Result;
|
|
40
|
+
clear(): void;
|
|
41
|
+
updateTtl(seconds: number): void;
|
|
42
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type SetTtl = (seconds: number) => void;
|
|
2
|
+
/**
|
|
3
|
+
* 创建一个带缓存的高阶函数
|
|
4
|
+
*
|
|
5
|
+
* @template Args 被包装函数的参数类型数组
|
|
6
|
+
* @template Result 被包装函数的返回类型
|
|
7
|
+
*
|
|
8
|
+
* @param fn 需要被缓存的函数,参数里附带的 setTtl 方法用于根据具体情况改写过期时间
|
|
9
|
+
* @param ttlSeconds 以秒为单位的过期时间,-1 表示永不过期,默认 -1,会被回调函数里的 setTtl() 覆盖
|
|
10
|
+
*
|
|
11
|
+
* @returns 返回包装后的函数,以及缓存相关的额外方法
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // 异步函数示例
|
|
15
|
+
* const fetchData = withCache(async function (url: string) {
|
|
16
|
+
* const data = await fetch(url).then((res) => res.json());
|
|
17
|
+
* this.setTtl(data.expiresIn); // 根据实际情况调整过期时间
|
|
18
|
+
* return data;
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* await fetchData(urlA);
|
|
22
|
+
* await fetchData(urlA); // 使用缓存结果
|
|
23
|
+
* await fetchData(urlB);
|
|
24
|
+
* await fetchData(urlB); // 使用缓存结果
|
|
25
|
+
*
|
|
26
|
+
* fetchData.clear(); // 清除缓存
|
|
27
|
+
* await fetchData(urlA); // 重新请求
|
|
28
|
+
* await fetchData(urlB); // 重新请求
|
|
29
|
+
*
|
|
30
|
+
* // 缓存过期前
|
|
31
|
+
* await sleep();
|
|
32
|
+
* fetchData.updateTtl(180); // 更新 ttl 并为所有未过期的缓存续期
|
|
33
|
+
* await fetchData(urlA); // 使用缓存结果
|
|
34
|
+
* await fetchData(urlB); // 使用缓存结果
|
|
35
|
+
*/
|
|
36
|
+
export declare const withCache: <Args extends any[], Result>(fn: (this: {
|
|
37
|
+
setTtl: SetTtl;
|
|
38
|
+
}, ...args: Args) => Result, ttlSeconds?: number) => {
|
|
39
|
+
(...args: Args): Result;
|
|
40
|
+
clear(): void;
|
|
41
|
+
updateTtl(seconds: number): void;
|
|
42
|
+
};
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var O=(x,Y)=>{let{time:T=!0,fileName:G=!0}=Y??{},M=[];if(T)M.push(`[${new Date().toLocaleTimeString()}]`);if(G){let{stack:E}=Error(),P=E?.split(`
|
|
3
|
+
`)[2]?.trim()?.match(/at\s+(.*):(\d+)/);if(P?.[1]){let Q=P[1].split(/[/\\]/).pop();M.push(`[${Q}:${P[2]}]`)}}M.push(x),console.log(...M)};var N=async(x,Y)=>{let{maxRetries:T=5,shouldStop:G=()=>!0}=Y??{};for(let M=0;M<T;M++){let E=await x(M);if(G(E))return E}throw Error(`\u8D85\u8FC7\u4E86\u6700\u5927\u5FAA\u73AF\u6B21\u6570\uFF08${T}\uFF09\u4E14\u672A\u6EE1\u8DB3\u505C\u6B62\u6267\u884C\u6761\u4EF6`)};var k=(x,Y=-1)=>{let T=new Map,G=(...M)=>{let E=JSON.stringify(M),J=Date.now(),P=T.get(E);if(P&&J<P.expiresAt)return P.value;let Q=Y===-1?1/0:J+Y*1000,$={setTtl:(_)=>{Q=J+_*1000}},X=x.apply($,M);if(X instanceof Promise){let _=X.then((z)=>{return T.set(E,{value:z,expiresAt:Q}),z});return T.set(E,{value:_,expiresAt:Q}),_}return T.set(E,{value:X,expiresAt:Q}),X};return G.clear=()=>T.clear(),G.updateTtl=(M)=>{Y=M;let E=Date.now(),J=E+M*1000;for(let[P,Q]of T.entries())if(Q.expiresAt>E)Q.expiresAt=J,T.set(P,Q)},G};var j=(x)=>{return!x};var I=(x)=>{return x===null||x===void 0};var Z=(x)=>{return x?.constructor===Object};var C=(x)=>{return x===null||x===void 0||typeof x!=="object"&&typeof x!=="function"};var F=(x,Y)=>{if(Array.isArray(x))return x.map((T)=>F(T,Y));if(Z(x))return Object.keys(x).reduce((G,M)=>{let E=Y(M),J=x[M];return G[E]=F(J,Y),G},{});return x};var d=(x,Y,T)=>{let{filter:G}=T??{};if(Array.isArray(x)){let M=x.map((E,J)=>{if(Z(E))return d(E,Y,T);return Y(E,J)});if(G)return M.filter((E,J)=>G(E,J));return M}if(Z(x))return Object.keys(x).reduce((E,J)=>{let P=x[J],Q;if(Z(P)||Array.isArray(P))Q=d(P,Y,T);else Q=Y(P,J);if(!G||G(Q,J))E[J]=Q;return E},{});return x};var H=(x,Y)=>{let T={...x};for(let G of Object.keys(Y)){let M=T[G],E=Y[G];if(C(M)&&C(E)){T[G]=E;continue}if(Array.isArray(M)&&Array.isArray(E)){T[G]=M.concat(E);continue}if(Z(M)&&Z(E)){T[G]=H(M,E);continue}T[G]=E}return T};var Px=(x="",Y={})=>{let T=async(G,M={})=>{let E=new URL(x?`${x}${G}`:G),{params:J,parser:P,...Q}=H(Y,M);if(Z(J))Object.entries(J).forEach(([_,z])=>{if(I(z))return;E.searchParams.append(_,z.toString())});if(Z(Q.body)||Array.isArray(Q.body))Q.body=JSON.stringify(Q.body),Q.headers={...Q.headers,"Content-Type":"application/json"};let $=await fetch(E,Q);if(!$.ok){if($.headers.get("Content-Type")?.startsWith("application/json"))throw await $.json();throw Error($.statusText)}return await(P?.($)??$.json())};return{get:(G,M)=>T(G,{...M,method:"GET"}),post:(G,M,E)=>T(G,{...E,method:"POST",body:M}),put:(G,M,E)=>T(G,{...E,method:"PUT",body:M}),delete:(G,M)=>T(G,{...M,method:"DELETE"})}};var S=async(x)=>{try{return[null,await x]}catch(Y){return[Y,void 0]}};var _x=async(x)=>{let[Y,T]=await S(fetch(x,{method:"HEAD",redirect:"manual"}));if(Y)return x;return T.headers.get("location")||x};var D=(x)=>{let Y=new Uint8Array(x),T="";for(let G=0;G<Y.byteLength;G++)T+=String.fromCharCode(Y[G]);return btoa(T)},R=async()=>{try{let Y=await Function("modulePath","return import(modulePath)")("sharp");return Y.default||Y}catch{return null}},f=async(x,Y,T,G)=>{let M=Buffer.from(Y),E=x(M);if(T==="image/jpeg")E=E.jpeg({quality:Math.round(G*100)});else if(T==="image/png"){let P=Math.round((1-G)*9);E=E.png({compressionLevel:P})}let J=await E.toBuffer();return`data:${T};base64,${J.toString("base64")}`},zx=async(x,Y={})=>{let{quality:T=0.92,compressor:G,fetcher:M=fetch}=Y;if(!x.startsWith("http"))throw Error("\u56FE\u7247\u5730\u5740\u5FC5\u987B\u4EE5http\u6216https\u5F00\u5934");let E=await M(x);if(!E.ok)throw Error(`\u83B7\u53D6\u56FE\u7247\u5931\u8D25: ${E.statusText}`);let J=E.headers.get("Content-Type")||"image/jpeg",P=await E.arrayBuffer();if(J!=="image/jpeg"&&J!=="image/png"){let X=D(P);return`data:${J};base64,${X}`}if(G)return await G(P,J,T);if(typeof OffscreenCanvas<"u"){let X=null;try{let _=new Blob([P],{type:J});X=await createImageBitmap(_);let z=new OffscreenCanvas(X.width,X.height),W=z.getContext("2d");if(!W)throw Error("\u65E0\u6CD5\u83B7\u53D6 OffscreenCanvas context");W.drawImage(X,0,0),X.close(),X=null;let q=await(await z.convertToBlob({type:J,quality:T})).arrayBuffer(),K=D(q);return`data:${J};base64,${K}`}catch{X?.close();let _=D(P);return`data:${J};base64,${_}`}}let Q=await R();if(Q)try{return await f(Q,P,J,T)}catch{let X=D(P);return`data:${J};base64,${X}`}let $=D(P);return`data:${J};base64,${$}`};var dx=(x,Y)=>{return Math.floor(Math.random()*(Y-x+1))+x};var Rx=(x)=>{return x.replace(/_([a-zA-Z])/g,(Y,T)=>T.toUpperCase())},fx=(x)=>{return x.replace(/([A-Z])/g,(Y,T)=>`_${T.toLowerCase()}`)},Ax=(x)=>{return x.charAt(0).toUpperCase()+x.slice(1)},Ox=(x)=>{return x.charAt(0).toLowerCase()+x.slice(1)};var Ux=(x="",Y)=>{if(!x)return"";let{maxLength:T=1/0,disableNewLineReplace:G=!1,disableWhitespaceCollapse:M=!1,omission:E="..."}=Y??{},J=x;if(!G)J=J.replace(/\r?\n/g,"\\n");else J=J.replace(/\r?\n/g," ");if(!M)J=J.replace(/\s+/g," ");if(J=J.trim(),T>0&&J.length>T)return J.slice(0,T)+E;return J};var hx=(x,Y=300)=>{let T=null;return(...G)=>{if(T)clearTimeout(T);T=setTimeout(()=>{x(...G)},Y)}};var gx=async(x=150)=>{return new Promise((Y)=>{setTimeout(Y,x)})};var mx=(x,Y=300)=>{let T=null;return function(...G){if(!T)T=setTimeout(()=>{T=null,x.apply(this,G)},Y)}};export{k as withCache,S as to,mx as throttle,Rx as snakeToCamel,gx as sleep,dx as randomInt,H as mergeObjects,d as mapValues,F as mapKeys,N as loopUntil,O as log,C as isPrimitive,Z as isObject,I as isNil,j as isFalsy,zx as imageUrlToBase64,_x as getRealURL,Px as fetcher,Ox as decapitalize,hx as debounce,Ux as compactStr,Ax as capitalize,fx as camelToSnake};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type Primitive = number | string | boolean | symbol | bigint | undefined | null;
|
|
2
|
+
/**
|
|
3
|
+
* 检测传入的值是否为**原始值**(number、string、boolean、symbol、bigint、undefined、null)
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* isPrimitive(1); // true
|
|
7
|
+
* isPrimitive([]); // false
|
|
8
|
+
*/
|
|
9
|
+
export declare const isPrimitive: (value: any) => value is Primitive;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type Primitive = number | string | boolean | symbol | bigint | undefined | null;
|
|
2
|
+
/**
|
|
3
|
+
* 检测传入的值是否为**原始值**(number、string、boolean、symbol、bigint、undefined、null)
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* isPrimitive(1); // true
|
|
7
|
+
* isPrimitive([]); // false
|
|
8
|
+
*/
|
|
9
|
+
export declare const isPrimitive: (value: any) => value is Primitive;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 检测传入的值是否为**真值**
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* isTruthy(1); // true
|
|
6
|
-
* isTruthy(""); // false
|
|
7
|
-
*/
|
|
8
|
-
export const isTruthy
|
|
9
|
-
return !!value;
|
|
10
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* 检测传入的值是否为**真值**
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* isTruthy(1); // true
|
|
6
|
+
* isTruthy(""); // false
|
|
7
|
+
*/
|
|
8
|
+
export declare const isTruthy: (value: any) => value is any;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 简易 LRU 缓存
|
|
3
|
+
* @example
|
|
4
|
+
* const cache = new LRUCache<string, number>(2);
|
|
5
|
+
* cache.set("a", 1);
|
|
6
|
+
* cache.set("b", 2);
|
|
7
|
+
* cache.set("c", 3); // 缓存已满,a 被淘汰
|
|
8
|
+
* cache.get("a"); // undefined
|
|
9
|
+
*/
|
|
10
|
+
declare class LRUCache<K, V> {
|
|
11
|
+
private cache;
|
|
12
|
+
private maxSize;
|
|
13
|
+
constructor(maxSize?: number);
|
|
14
|
+
get(key: K): V | undefined;
|
|
15
|
+
set(key: K, value: V): void;
|
|
16
|
+
has(key: K): boolean;
|
|
17
|
+
}
|
|
18
|
+
export default LRUCache;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export type BunFetchOptions = {
|
|
2
|
+
/** 代理服务器配置(仅 Bun 支持) */
|
|
3
|
+
proxy?: string;
|
|
4
|
+
};
|
|
5
|
+
export type RequestInit = globalThis.RequestInit & BunFetchOptions & {
|
|
6
|
+
params?: Record<string, any>;
|
|
7
|
+
parser?: (response: Response) => Promise<any>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* 基于 Fetch API 的请求客户端
|
|
11
|
+
* @param baseURL 接口前缀
|
|
12
|
+
* @param baseOptions 客户端级别的请求体,后续调用时传递相同参数会覆盖上去
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* 特性:
|
|
16
|
+
* - 合并实例、调用时的相同请求体
|
|
17
|
+
* - 在 params 里传递对象,自动转换为 queryString
|
|
18
|
+
* - 在 body 里传递对象,自动 JSON.stringify
|
|
19
|
+
* - 可选择使用 to() 转换请求结果为 [Error, Response]
|
|
20
|
+
* - 可选择使用 withCache() 缓存请求结果
|
|
21
|
+
* - 支持 proxy 选项(仅在 Bun 环境有效)
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
*
|
|
25
|
+
* // 用法1:直接发送请求
|
|
26
|
+
* const res = await fetcher().get<Blog>("https://nickyzj.run:3030/blogs/hello-world");
|
|
27
|
+
*
|
|
28
|
+
* // 用法2:创建实例
|
|
29
|
+
* const api = fetcher("https://nickyzj.run:3030", { headers: { Authorization: "Bearer token" } });
|
|
30
|
+
* const res = await api.get<Blog>("/blogs/hello-world", { headers: {...}, params: { page: 1 } }); // 与实例相同的 headers 会覆盖上去,params 会转成 ?page=1 跟到 url 后面
|
|
31
|
+
*
|
|
32
|
+
* // 用法3:使用代理(仅 Bun 环境)
|
|
33
|
+
* const api = fetcher("https://api.example.com", {
|
|
34
|
+
* proxy: "http://127.0.0.1:7890"
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* // 安全处理请求结果
|
|
38
|
+
* const [error, data] = await to(api.get<Blog>("/blogs/hello-world"));
|
|
39
|
+
* if (error) {
|
|
40
|
+
* console.error(error);
|
|
41
|
+
* return;
|
|
42
|
+
* }
|
|
43
|
+
* console.log(data);
|
|
44
|
+
*
|
|
45
|
+
* // 缓存请求结果
|
|
46
|
+
* const getBlogs = withCache(api.get);
|
|
47
|
+
* await getBlogs("/blogs");
|
|
48
|
+
* await sleep();
|
|
49
|
+
* await getBlogs("/blogs"); // 不发请求,使用缓存
|
|
50
|
+
*/
|
|
51
|
+
export declare const fetcher: (baseURL?: string, baseOptions?: RequestInit) => {
|
|
52
|
+
get: <T>(url: string, options?: Omit<RequestInit, "method">) => Promise<T>;
|
|
53
|
+
post: <T>(url: string, body: any, options?: Omit<RequestInit, "method" | "body">) => Promise<T>;
|
|
54
|
+
put: <T>(url: string, body: any, options?: Omit<RequestInit, "method" | "body">) => Promise<T>;
|
|
55
|
+
delete: <T>(url: string, options?: Omit<RequestInit, "method" | "body">) => Promise<T>;
|
|
56
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 图片压缩选项
|
|
3
|
+
*/
|
|
4
|
+
export type ImageCompressionOptions = {
|
|
5
|
+
/** 压缩比率,默认 0.92 */
|
|
6
|
+
quality?: number;
|
|
7
|
+
/**
|
|
8
|
+
* 自定义压缩函数,用于覆盖默认压缩行为
|
|
9
|
+
* @param arrayBuffer 图片的 ArrayBuffer 数据
|
|
10
|
+
* @param mime 图片的 MIME 类型
|
|
11
|
+
* @param quality 压缩质量
|
|
12
|
+
* @returns 压缩后的 base64 字符串
|
|
13
|
+
*/
|
|
14
|
+
compressor?: (arrayBuffer: ArrayBuffer, mime: string, quality: number) => Promise<string> | string;
|
|
15
|
+
/**
|
|
16
|
+
* 自定义 fetch 函数,用于使用自己封装的请求库读取图片
|
|
17
|
+
* 必须返回符合 Web 标准的 Response 对象
|
|
18
|
+
* @param url 图片地址
|
|
19
|
+
* @returns Promise<Response>
|
|
20
|
+
*/
|
|
21
|
+
fetcher?: (url: string) => Promise<Response>;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 图片地址转 base64 数据
|
|
25
|
+
*
|
|
26
|
+
* @param imageUrl 图片地址
|
|
27
|
+
* @param options 可选配置
|
|
28
|
+
* @param options.quality 压缩比率,默认 0.92
|
|
29
|
+
* @param options.compressor 自定义压缩函数,用于覆盖默认压缩行为
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* // 基本用法(浏览器自动使用 Canvas 压缩,Node.js/Bun 自动检测并使用 sharp)
|
|
33
|
+
* imageUrlToBase64("https://example.com/image.jpg");
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // 使用自定义 fetch 函数(如 axios 封装)
|
|
37
|
+
* imageUrlToBase64("https://example.com/image.jpg", {
|
|
38
|
+
* fetcher: async (url) => {
|
|
39
|
+
* // 使用 axios 或其他请求库,但必须返回 Response 对象
|
|
40
|
+
* const response = await axios.get(url, { responseType: 'arraybuffer' });
|
|
41
|
+
* return new Response(response.data, {
|
|
42
|
+
* status: response.status,
|
|
43
|
+
* statusText: response.statusText,
|
|
44
|
+
* headers: response.headers
|
|
45
|
+
* });
|
|
46
|
+
* }
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // 使用自定义压缩函数覆盖默认行为
|
|
51
|
+
* imageUrlToBase64("https://example.com/image.jpg", {
|
|
52
|
+
* quality: 0.8,
|
|
53
|
+
* compressor: async (buffer, mime, quality) => {
|
|
54
|
+
* // 自定义压缩逻辑
|
|
55
|
+
* return `data:${mime};base64,...`;
|
|
56
|
+
* }
|
|
57
|
+
* });
|
|
58
|
+
*/
|
|
59
|
+
export declare const imageUrlToBase64: (imageUrl: string, options?: ImageCompressionOptions) => Promise<string>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Go 语言风格的异步处理方式
|
|
3
|
+
* @param promise 一个能被 await 的异步函数
|
|
4
|
+
* @returns 如果成功,返回 [null, 异步函数结果],否则返回 [Error, undefined]
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const [error, response] = await to(fetcher().get<Blog>("/blogs/hello-world"));
|
|
8
|
+
*/
|
|
9
|
+
export declare const to: <T, E = Error>(promise: Promise<T>) => Promise<[null, T] | [E, undefined]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./random-int";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type DeepMapKeys<T> = T extends Array<infer U> ? Array<DeepMapKeys<U>> : T extends object ? {
|
|
2
|
+
[key: string]: DeepMapKeys<T[keyof T]>;
|
|
3
|
+
} : T;
|
|
4
|
+
/**
|
|
5
|
+
* 递归处理对象里的 key
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* 无法完整推导出类型,只能做到有递归,key 全为 string,value 为同层级的所有类型的联合
|
|
9
|
+
*
|
|
10
|
+
* @template T 要转换的对象
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const obj = { a: { b: 1 } };
|
|
14
|
+
* const result = mapKeys(obj, (key) => key.toUpperCase());
|
|
15
|
+
* console.log(result); // { A: { B: 1 } }
|
|
16
|
+
*/
|
|
17
|
+
export declare const mapKeys: <T>(obj: T, getNewKey: (key: string) => string) => DeepMapKeys<T>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type DeepMapValues<T, R> = T extends Array<infer U> ? Array<DeepMapValues<U, R>> : T extends object ? {
|
|
2
|
+
[K in keyof T]: T[K] extends object ? DeepMapValues<T[K], R> : R;
|
|
3
|
+
} : R;
|
|
4
|
+
/**
|
|
5
|
+
* 递归处理对象里的 value
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* 无法完整推导出类型,所有 value 最终都会变为 any
|
|
9
|
+
*
|
|
10
|
+
* @template T 要转换的对象
|
|
11
|
+
* @template R 转换后的值类型,为 any,无法进一步推导
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const obj = { a: 1, b: { c: 2 } };
|
|
15
|
+
* const result = mapValues(obj, (value, key) => isPrimitive(value) ? value + 1 : value);
|
|
16
|
+
* console.log(result); // { a: 2, b: { c: 3 } }
|
|
17
|
+
*/
|
|
18
|
+
export declare const mapValues: <T, R = any>(obj: T, getNewValue: (value: any, key: string | number) => R, options?: {
|
|
19
|
+
/** 过滤函数,返回 true 表示保留该字段 */
|
|
20
|
+
filter?: (value: any, key: string | number) => boolean;
|
|
21
|
+
}) => DeepMapValues<T, R>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type DeepMapKeys<T> = T extends Array<infer U> ? Array<DeepMapKeys<U>> : T extends object ? {
|
|
2
|
+
[key: string]: DeepMapKeys<T[keyof T]>;
|
|
3
|
+
} : T;
|
|
4
|
+
/**
|
|
5
|
+
* 递归处理对象里的 key
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* 无法完整推导出类型,只能做到有递归,key 全为 string,value 为同层级的所有类型的联合
|
|
9
|
+
*
|
|
10
|
+
* @template T 要转换的对象
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const obj = { a: { b: 1 } };
|
|
14
|
+
* const result = mapKeys(obj, (key) => key.toUpperCase());
|
|
15
|
+
* console.log(result); // { A: { B: 1 } }
|
|
16
|
+
*/
|
|
17
|
+
export declare const mapKeys: <T>(obj: T, getNewKey: (key: string) => string) => DeepMapKeys<T>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type DeepMapValues<T, R> = T extends Array<infer U> ? Array<DeepMapValues<U, R>> : T extends object ? {
|
|
2
|
+
[K in keyof T]: T[K] extends object ? DeepMapValues<T[K], R> : R;
|
|
3
|
+
} : R;
|
|
4
|
+
/**
|
|
5
|
+
* 递归处理对象里的 value
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* 无法完整推导出类型,所有 value 最终都会变为 any
|
|
9
|
+
*
|
|
10
|
+
* @template T 要转换的对象
|
|
11
|
+
* @template R 转换后的值类型,为 any,无法进一步推导
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* const obj = { a: 1, b: { c: 2 } };
|
|
15
|
+
* const result = mapValues(obj, (value, key) => isPrimitive(value) ? value + 1 : value);
|
|
16
|
+
* console.log(result); // { a: 2, b: { c: 3 } }
|
|
17
|
+
*/
|
|
18
|
+
export declare const mapValues: <T, R = any>(obj: T, getNewValue: (value: any, key: string | number) => R, options?: {
|
|
19
|
+
/** 过滤函数,返回 true 表示保留该字段 */
|
|
20
|
+
filter?: (value: any, key: string | number) => boolean;
|
|
21
|
+
}) => DeepMapValues<T, R>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 深度合并两个对象,规则如下:
|
|
3
|
+
* 1. 原始值覆盖:如果两个值都是原始类型,则用后者覆盖;
|
|
4
|
+
* 2. 数组拼接:如果两个值都是数组,则拼接为大数组;
|
|
5
|
+
* 3. 对象递归合并:如果两个值都是对象,则进行递归深度合并;
|
|
6
|
+
*
|
|
7
|
+
* @template T 第一个对象
|
|
8
|
+
* @template U 第二个对象
|
|
9
|
+
* @param {T} obj1 要合并的第一个对象,相同字段会被 obj2 覆盖
|
|
10
|
+
* @param {U} obj2 要合并的第二个对象
|
|
11
|
+
*/
|
|
12
|
+
export declare const mergeObjects: <T extends Record<string, any>, U extends Record<string, any>>(obj1: T, obj2: U) => T & U;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 深度合并两个对象,规则如下:
|
|
3
|
+
* 1. 原始值覆盖:如果两个值都是原始类型,则用后者覆盖;
|
|
4
|
+
* 2. 数组拼接:如果两个值都是数组,则拼接为大数组;
|
|
5
|
+
* 3. 对象递归合并:如果两个值都是对象,则进行递归深度合并;
|
|
6
|
+
*
|
|
7
|
+
* @template T 第一个对象
|
|
8
|
+
* @template U 第二个对象
|
|
9
|
+
* @param {T} obj1 要合并的第一个对象,相同字段会被 obj2 覆盖
|
|
10
|
+
* @param {U} obj2 要合并的第二个对象
|
|
11
|
+
*/
|
|
12
|
+
export declare const mergeObjects: <T extends Record<string, any>, U extends Record<string, any>>(obj1: T, obj2: U) => T & U;
|