@cloudcome/utils-core 1.6.0 → 1.8.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.
Files changed (52) hide show
  1. package/dist/async.cjs +2 -2
  2. package/dist/async.cjs.map +1 -1
  3. package/dist/async.mjs +1 -1
  4. package/dist/async.mjs.map +1 -1
  5. package/dist/dict.cjs +56 -0
  6. package/dist/dict.cjs.map +1 -1
  7. package/dist/dict.d.ts +67 -0
  8. package/dist/dict.mjs +57 -1
  9. package/dist/dict.mjs.map +1 -1
  10. package/dist/{fn.cjs → function.cjs} +1 -1
  11. package/dist/function.cjs.map +1 -0
  12. package/dist/{fn.mjs → function.mjs} +1 -1
  13. package/dist/function.mjs.map +1 -0
  14. package/dist/index.cjs +1 -1
  15. package/dist/index.mjs +1 -1
  16. package/dist/object/process.d.ts +15 -0
  17. package/dist/object.cjs +6 -0
  18. package/dist/object.cjs.map +1 -1
  19. package/dist/object.mjs +6 -0
  20. package/dist/object.mjs.map +1 -1
  21. package/dist/{qs.cjs → query.cjs} +1 -1
  22. package/dist/query.cjs.map +1 -0
  23. package/dist/{qs.mjs → query.mjs} +1 -1
  24. package/dist/query.mjs.map +1 -0
  25. package/dist/try/{tryCallback.d.ts → callback.d.ts} +1 -1
  26. package/dist/try/function.d.ts +5 -0
  27. package/dist/try/main.d.ts +7 -0
  28. package/dist/try/types.d.ts +1 -1
  29. package/dist/try.cjs +18 -18
  30. package/dist/try.cjs.map +1 -1
  31. package/dist/try.d.ts +2 -1
  32. package/dist/try.mjs +19 -19
  33. package/dist/try.mjs.map +1 -1
  34. package/dist/types.d.ts +8 -0
  35. package/package.json +13 -21
  36. package/dist/enum.cjs +0 -58
  37. package/dist/enum.cjs.map +0 -1
  38. package/dist/enum.d.ts +0 -68
  39. package/dist/enum.mjs +0 -58
  40. package/dist/enum.mjs.map +0 -1
  41. package/dist/fn.cjs.map +0 -1
  42. package/dist/fn.mjs.map +0 -1
  43. package/dist/qs.cjs.map +0 -1
  44. package/dist/qs.mjs.map +0 -1
  45. package/dist/try/const.d.ts +0 -2
  46. package/dist/try/index.d.ts +0 -7
  47. package/dist/try/tryFlatten.d.ts +0 -6
  48. package/dist/try/tryFunction.d.ts +0 -3
  49. /package/dist/{fn.d.ts → function.d.ts} +0 -0
  50. /package/dist/{qs.d.ts → query.d.ts} +0 -0
  51. /package/dist/try/{callbackCurry.d.ts → curry.d.ts} +0 -0
  52. /package/dist/try/{tryPromise.d.ts → promise.d.ts} +0 -0
package/dist/async.cjs CHANGED
@@ -17,7 +17,7 @@ var __privateWrapper = (obj, member, setter, getter) => ({
17
17
  });
18
18
  var _tasks, _length, _AsyncQueue_instances, add_fn, addAndRun_fn, _startResolved, _startRejected, _startResults, _startPwr, _startLength, _running, run_fn, _stopResolved, _stopRejected, _stopLength, _stopResults, _stopPwr;
19
19
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
20
- const fn = require("./fn.cjs");
20
+ const _function = require("./function.cjs");
21
21
  class AsyncQueue {
22
22
  /**
23
23
  * 创建一个异步任务队列
@@ -209,7 +209,7 @@ function asyncShared(af, options) {
209
209
  var _a;
210
210
  (_a = options == null ? void 0 : options.onTrigger) == null ? void 0 : _a.call(options, ...inputs);
211
211
  const p = _sharedAf("trigger", ...inputs);
212
- p.catch(fn.fnNoop);
212
+ p.catch(_function.fnNoop);
213
213
  return p;
214
214
  };
215
215
  }
@@ -1 +1 @@
1
- {"version":3,"file":"async.cjs","sources":["../src/async.ts"],"sourcesContent":["import { fnNoop } from './fn';\nimport type { AnyArray, AnyAsyncFunction } from './types';\n\n/**\n * 表示异步任务的类型\n * @template T - 任务返回值的类型\n */\ntype AsyncTask<T> = {\n /** 任务索引 */\n idx: number;\n /** 异步任务函数 */\n afn: () => Promise<T>;\n /** Promise 的解析器对象 */\n pwr?: PromiseWithResolvers<T>;\n};\n\n/**\n * 异步任务队列的配置选项\n */\nexport type AsyncQueueOptions = {\n /**\n * 并发限制数,0 表示无限制\n * @default 0\n */\n limit?: number;\n};\n\n/**\n * 异步任务队列,用于管理和控制异步任务的执行\n * @template T - 任务返回值的类型\n */\nexport class AsyncQueue<T> {\n #tasks: AsyncTask<T>[] = [];\n #length = 0;\n\n /**\n * 创建一个异步任务队列\n * @param asyncFns - 要执行的异步函数数组\n * @param options - 队列配置选项\n */\n constructor(\n asyncFns: Array<() => Promise<T>>,\n readonly options?: AsyncQueueOptions,\n ) {\n asyncFns.forEach((afn, idx) => {\n this.#add('push', afn);\n });\n }\n\n get length() {\n return this.#length;\n }\n\n get limit() {\n return this.options?.limit || 0;\n }\n\n #add(method: 'unshift' | 'push', afn: () => Promise<T>, pwr?: PromiseWithResolvers<T>) {\n this.#tasks[method]({\n idx: this.#length++,\n afn: afn,\n pwr: pwr,\n });\n }\n\n #addAndRun(method: 'unshift' | 'push', afn: () => Promise<T>) {\n // 明确终止了\n if (this.#stopLength >= 0) {\n throw new Error('异步队列已被终止,无法添加新的任务');\n }\n\n const pwr = Promise.withResolvers<T>();\n this.#add(method, afn, pwr);\n\n if (this.#startPwr && this.#running === 0) {\n this.#run();\n }\n\n return pwr.promise;\n }\n\n async push(afn: () => Promise<T>) {\n return this.#addAndRun('push', afn);\n }\n\n async unshift(afn: () => Promise<T>) {\n return this.#addAndRun('unshift', afn);\n }\n\n #startResolved = 0;\n #startRejected = 0;\n get startSettled() {\n return this.#startResolved === this.#startLength || this.#startRejected > 0;\n }\n\n #startResults: T[] = [];\n #startPwr: PromiseWithResolvers<T[]> | null = null;\n #startLength = 0;\n\n /**\n * 启动队列中的任务执行\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async start(): Promise<T[]> {\n if (this.#startPwr) return this.#startPwr.promise;\n\n // 固化启动时长度,便于判断 start 异步结果\n this.#startLength = this.#length;\n this.#startPwr = Promise.withResolvers<T[]>();\n\n if (this.#startLength === 0) {\n this.#startPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#startPwr.promise;\n }\n\n #running = 0;\n #run() {\n while (this.limit === 0 || this.#running < this.limit) {\n const task = this.#tasks.shift();\n\n // 无任务可执行\n if (!task) break;\n\n this.#running++;\n\n task\n .afn()\n .then((result) => {\n this.#running--;\n task.pwr?.resolve(result);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startResults[task.idx] = result;\n this.#startResolved++;\n }\n\n // 所有启动任务都已执行完毕\n if (this.#startResolved === this.#startLength) {\n this.#startPwr?.resolve(this.#startResults);\n }\n\n this.#stopResults[task.idx] = result;\n this.#stopResolved++;\n\n // 所有停止任务都已执行完毕\n if (this.#stopResolved === this.#stopLength) {\n this.#stopPwr?.resolve(this.#stopResults);\n } else {\n this.#run();\n }\n })\n .catch((reason) => {\n this.#running--;\n task.pwr?.reject(reason);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startRejected++;\n this.#startPwr?.reject(reason);\n }\n\n // 属于停止任务\n if (this.#stopLength > 0) {\n this.#stopRejected++;\n this.#stopPwr?.reject(reason);\n }\n });\n }\n }\n #stopResolved = 0;\n #stopRejected = 0;\n get stopSettled() {\n return this.#stopResolved === this.#stopLength || this.#stopRejected > 0;\n }\n\n #stopLength = -1;\n #stopResults: T[] = [];\n\n #stopPwr?: PromiseWithResolvers<T[]> | null = null;\n\n /**\n * 终止队列中的任务执行,终止队列后不再可以追加异步任务\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async stop() {\n if (this.#stopPwr) {\n return this.#stopPwr.promise;\n }\n\n this.#stopLength = this.#length;\n this.#stopPwr = Promise.withResolvers<T[]>();\n\n if (this.#stopLength === 0) {\n this.#stopPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#stopPwr.promise;\n }\n}\n\n/**\n * 使用给定的并发限制执行异步函数\n *\n * 此函数的目的是控制一组异步函数的并发执行数量,通过创建一个AsyncQueue实例来管理这些异步函数的执行\n * 它确保在任何给定时间只有最多`limit`数量的异步函数被执行,以避免潜在的性能问题或资源竞争\n *\n * @param asyncFns 一个包含异步函数的数组,每个异步函数都不需要参数,并返回一个Promise\n * @param limit 并发限制的数量,表示同时执行的异步函数的最大数量,0 表示不限制\n * @returns 返回一个Promise,当所有异步函数都执行完毕后,该Promise将被解析\n */\nexport function asyncLimit<T>(asyncFns: Array<() => Promise<T>>, limit: number) {\n const aq = new AsyncQueue<T>(asyncFns, { limit });\n return aq.start();\n}\n\n/**\n * 异步共享函数的配置选项\n */\nexport type AsyncSharedOptions<I extends AnyArray, O> = {\n /**\n * 是否在调用结束后再执行(只在运行期间有再次调用时才会生效)\n * @type {boolean}\n * @default false\n * @example\n * const sharedFn = asyncShared(fetchData, { trailing: true });\n * // 如果在 fetchData 执行期间多次调用 sharedFn,则会在 fetchData 结束后再次执行\n */\n trailing?: boolean;\n\n /**\n * 缓存结果的最大有效期(毫秒)\n * @type {number}\n * @example\n * const sharedFn = asyncShared(fetchData, { maxAge: 1000 });\n * // 在 1 秒内调用 sharedFn 会直接返回缓存结果\n */\n maxAge?: number;\n\n /**\n * 在调用共享函数时触发的回调函数\n * @param inputs - 传递给共享函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onTrigger: (...args) => console.log('Calling with:', args)\n * };\n */\n onTrigger?: (...inputs: I) => unknown;\n\n /**\n * 在执行异步函数时触发的回调函数\n * @param args - 传递给异步函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onExecute: (...args) => console.log('Executing with:', args)\n * };\n */\n onExecute?: (...args: I) => unknown;\n\n /**\n * 在异步函数成功执行后触发的回调函数\n * @param output - 异步函数的返回结果\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onSuccess: (result) => console.log('Success:', result)\n * };\n */\n onSuccess?: (output: O) => unknown;\n\n /**\n * 在异步函数执行失败时触发的回调函数\n * @param error - 异步函数抛出的错误\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onError: (error) => console.error('Error:', error)\n * };\n */\n onError?: (error: unknown) => unknown;\n\n /**\n * 在异步函数执行完成(无论成功或失败)时触发的回调函数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onFinally: () => console.log('Execution completed')\n * };\n */\n onFinally?: () => unknown;\n};\n\n/**\n * 创建一个共享执行结果的异步函数\n * @template F - 异步函数类型\n * @param {F} af - 要共享的异步函数\n * @param {AsyncSharedOptions} [options] - 配置选项\n * @returns {F} 返回一个新的异步函数,该函数会共享执行结果\n * @example\n * const fetchData = async (id) => {\n * // 模拟异步操作\n * return await fetch(`/api/data/${id}`);\n * };\n *\n * const sharedFetch = asyncShared(fetchData, { maxAge: 1000 });\n *\n * // 多次调用会共享同一个请求\n * const result1 = await sharedFetch(1);\n * const result2 = await sharedFetch(1); // 上次请求完成后 1000ms 内直接返回缓存结果\n */\nexport function asyncShared<I extends AnyArray, O>(\n af: (...inputs: I) => Promise<O>,\n options?: AsyncSharedOptions<I, O>,\n) {\n let executedPromise: Promise<O> | undefined;\n let executing = false;\n let executingInputs: I | undefined;\n let executedTime = 0;\n\n const _sharedAf = async (from: 'trigger' | 'trailing', ...inputs: I) => {\n executingInputs = inputs;\n\n // 如果正在运行,则复用运行结果\n if (executing && executedPromise) {\n return executedPromise;\n }\n\n // 如果已运行结束空闲时,判断是否在等待时间内\n if (executedPromise && Date.now() - executedTime < (options?.maxAge || 0)) {\n return executedPromise;\n }\n\n // 否则直接执行\n executing = true;\n options?.onExecute?.(...executingInputs);\n executedPromise = af(...executingInputs);\n executingInputs = undefined;\n executedPromise\n .then((res) => {\n options?.onSuccess?.(res);\n })\n .catch((err) => {\n options?.onError?.(err);\n })\n .finally(() => {\n executing = false;\n executedTime = Date.now();\n options?.onFinally?.();\n\n // 执行期间多次调用,则重新执行\n if (executingInputs && options?.trailing) {\n _sharedAf('trailing', ...executingInputs);\n }\n });\n\n return executedPromise;\n };\n\n return function sharedAf(...inputs: I): Promise<O> {\n options?.onTrigger?.(...inputs);\n const p = _sharedAf('trigger', ...inputs);\n // 必须捕获错误,否则单测错误边界时会抛错\n p.catch(fnNoop);\n return p;\n };\n}\n\n// const af1 = asyncShared(async () => {\n// return 1;\n// });\n// const n = await af1();\n\n// const af2 = asyncShared(async (a: number) => {\n// return a + 1;\n// });\n// const n2 = await af2(2);\n"],"names":["_a","fnNoop"],"mappings":"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"}
1
+ {"version":3,"file":"async.cjs","sources":["../src/async.ts"],"sourcesContent":["import { fnNoop } from './function';\nimport type { AnyArray, AnyAsyncFunction } from './types';\n\n/**\n * 表示异步任务的类型\n * @template T - 任务返回值的类型\n */\ntype AsyncTask<T> = {\n /** 任务索引 */\n idx: number;\n /** 异步任务函数 */\n afn: () => Promise<T>;\n /** Promise 的解析器对象 */\n pwr?: PromiseWithResolvers<T>;\n};\n\n/**\n * 异步任务队列的配置选项\n */\nexport type AsyncQueueOptions = {\n /**\n * 并发限制数,0 表示无限制\n * @default 0\n */\n limit?: number;\n};\n\n/**\n * 异步任务队列,用于管理和控制异步任务的执行\n * @template T - 任务返回值的类型\n */\nexport class AsyncQueue<T> {\n #tasks: AsyncTask<T>[] = [];\n #length = 0;\n\n /**\n * 创建一个异步任务队列\n * @param asyncFns - 要执行的异步函数数组\n * @param options - 队列配置选项\n */\n constructor(\n asyncFns: Array<() => Promise<T>>,\n readonly options?: AsyncQueueOptions,\n ) {\n asyncFns.forEach((afn, idx) => {\n this.#add('push', afn);\n });\n }\n\n get length() {\n return this.#length;\n }\n\n get limit() {\n return this.options?.limit || 0;\n }\n\n #add(method: 'unshift' | 'push', afn: () => Promise<T>, pwr?: PromiseWithResolvers<T>) {\n this.#tasks[method]({\n idx: this.#length++,\n afn: afn,\n pwr: pwr,\n });\n }\n\n #addAndRun(method: 'unshift' | 'push', afn: () => Promise<T>) {\n // 明确终止了\n if (this.#stopLength >= 0) {\n throw new Error('异步队列已被终止,无法添加新的任务');\n }\n\n const pwr = Promise.withResolvers<T>();\n this.#add(method, afn, pwr);\n\n if (this.#startPwr && this.#running === 0) {\n this.#run();\n }\n\n return pwr.promise;\n }\n\n async push(afn: () => Promise<T>) {\n return this.#addAndRun('push', afn);\n }\n\n async unshift(afn: () => Promise<T>) {\n return this.#addAndRun('unshift', afn);\n }\n\n #startResolved = 0;\n #startRejected = 0;\n get startSettled() {\n return this.#startResolved === this.#startLength || this.#startRejected > 0;\n }\n\n #startResults: T[] = [];\n #startPwr: PromiseWithResolvers<T[]> | null = null;\n #startLength = 0;\n\n /**\n * 启动队列中的任务执行\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async start(): Promise<T[]> {\n if (this.#startPwr) return this.#startPwr.promise;\n\n // 固化启动时长度,便于判断 start 异步结果\n this.#startLength = this.#length;\n this.#startPwr = Promise.withResolvers<T[]>();\n\n if (this.#startLength === 0) {\n this.#startPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#startPwr.promise;\n }\n\n #running = 0;\n #run() {\n while (this.limit === 0 || this.#running < this.limit) {\n const task = this.#tasks.shift();\n\n // 无任务可执行\n if (!task) break;\n\n this.#running++;\n\n task\n .afn()\n .then((result) => {\n this.#running--;\n task.pwr?.resolve(result);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startResults[task.idx] = result;\n this.#startResolved++;\n }\n\n // 所有启动任务都已执行完毕\n if (this.#startResolved === this.#startLength) {\n this.#startPwr?.resolve(this.#startResults);\n }\n\n this.#stopResults[task.idx] = result;\n this.#stopResolved++;\n\n // 所有停止任务都已执行完毕\n if (this.#stopResolved === this.#stopLength) {\n this.#stopPwr?.resolve(this.#stopResults);\n } else {\n this.#run();\n }\n })\n .catch((reason) => {\n this.#running--;\n task.pwr?.reject(reason);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startRejected++;\n this.#startPwr?.reject(reason);\n }\n\n // 属于停止任务\n if (this.#stopLength > 0) {\n this.#stopRejected++;\n this.#stopPwr?.reject(reason);\n }\n });\n }\n }\n #stopResolved = 0;\n #stopRejected = 0;\n get stopSettled() {\n return this.#stopResolved === this.#stopLength || this.#stopRejected > 0;\n }\n\n #stopLength = -1;\n #stopResults: T[] = [];\n\n #stopPwr?: PromiseWithResolvers<T[]> | null = null;\n\n /**\n * 终止队列中的任务执行,终止队列后不再可以追加异步任务\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async stop() {\n if (this.#stopPwr) {\n return this.#stopPwr.promise;\n }\n\n this.#stopLength = this.#length;\n this.#stopPwr = Promise.withResolvers<T[]>();\n\n if (this.#stopLength === 0) {\n this.#stopPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#stopPwr.promise;\n }\n}\n\n/**\n * 使用给定的并发限制执行异步函数\n *\n * 此函数的目的是控制一组异步函数的并发执行数量,通过创建一个AsyncQueue实例来管理这些异步函数的执行\n * 它确保在任何给定时间只有最多`limit`数量的异步函数被执行,以避免潜在的性能问题或资源竞争\n *\n * @param asyncFns 一个包含异步函数的数组,每个异步函数都不需要参数,并返回一个Promise\n * @param limit 并发限制的数量,表示同时执行的异步函数的最大数量,0 表示不限制\n * @returns 返回一个Promise,当所有异步函数都执行完毕后,该Promise将被解析\n */\nexport function asyncLimit<T>(asyncFns: Array<() => Promise<T>>, limit: number) {\n const aq = new AsyncQueue<T>(asyncFns, { limit });\n return aq.start();\n}\n\n/**\n * 异步共享函数的配置选项\n */\nexport type AsyncSharedOptions<I extends AnyArray, O> = {\n /**\n * 是否在调用结束后再执行(只在运行期间有再次调用时才会生效)\n * @type {boolean}\n * @default false\n * @example\n * const sharedFn = asyncShared(fetchData, { trailing: true });\n * // 如果在 fetchData 执行期间多次调用 sharedFn,则会在 fetchData 结束后再次执行\n */\n trailing?: boolean;\n\n /**\n * 缓存结果的最大有效期(毫秒)\n * @type {number}\n * @example\n * const sharedFn = asyncShared(fetchData, { maxAge: 1000 });\n * // 在 1 秒内调用 sharedFn 会直接返回缓存结果\n */\n maxAge?: number;\n\n /**\n * 在调用共享函数时触发的回调函数\n * @param inputs - 传递给共享函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onTrigger: (...args) => console.log('Calling with:', args)\n * };\n */\n onTrigger?: (...inputs: I) => unknown;\n\n /**\n * 在执行异步函数时触发的回调函数\n * @param args - 传递给异步函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onExecute: (...args) => console.log('Executing with:', args)\n * };\n */\n onExecute?: (...args: I) => unknown;\n\n /**\n * 在异步函数成功执行后触发的回调函数\n * @param output - 异步函数的返回结果\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onSuccess: (result) => console.log('Success:', result)\n * };\n */\n onSuccess?: (output: O) => unknown;\n\n /**\n * 在异步函数执行失败时触发的回调函数\n * @param error - 异步函数抛出的错误\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onError: (error) => console.error('Error:', error)\n * };\n */\n onError?: (error: unknown) => unknown;\n\n /**\n * 在异步函数执行完成(无论成功或失败)时触发的回调函数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onFinally: () => console.log('Execution completed')\n * };\n */\n onFinally?: () => unknown;\n};\n\n/**\n * 创建一个共享执行结果的异步函数\n * @template F - 异步函数类型\n * @param {F} af - 要共享的异步函数\n * @param {AsyncSharedOptions} [options] - 配置选项\n * @returns {F} 返回一个新的异步函数,该函数会共享执行结果\n * @example\n * const fetchData = async (id) => {\n * // 模拟异步操作\n * return await fetch(`/api/data/${id}`);\n * };\n *\n * const sharedFetch = asyncShared(fetchData, { maxAge: 1000 });\n *\n * // 多次调用会共享同一个请求\n * const result1 = await sharedFetch(1);\n * const result2 = await sharedFetch(1); // 上次请求完成后 1000ms 内直接返回缓存结果\n */\nexport function asyncShared<I extends AnyArray, O>(\n af: (...inputs: I) => Promise<O>,\n options?: AsyncSharedOptions<I, O>,\n) {\n let executedPromise: Promise<O> | undefined;\n let executing = false;\n let executingInputs: I | undefined;\n let executedTime = 0;\n\n const _sharedAf = async (from: 'trigger' | 'trailing', ...inputs: I) => {\n executingInputs = inputs;\n\n // 如果正在运行,则复用运行结果\n if (executing && executedPromise) {\n return executedPromise;\n }\n\n // 如果已运行结束空闲时,判断是否在等待时间内\n if (executedPromise && Date.now() - executedTime < (options?.maxAge || 0)) {\n return executedPromise;\n }\n\n // 否则直接执行\n executing = true;\n options?.onExecute?.(...executingInputs);\n executedPromise = af(...executingInputs);\n executingInputs = undefined;\n executedPromise\n .then((res) => {\n options?.onSuccess?.(res);\n })\n .catch((err) => {\n options?.onError?.(err);\n })\n .finally(() => {\n executing = false;\n executedTime = Date.now();\n options?.onFinally?.();\n\n // 执行期间多次调用,则重新执行\n if (executingInputs && options?.trailing) {\n _sharedAf('trailing', ...executingInputs);\n }\n });\n\n return executedPromise;\n };\n\n return function sharedAf(...inputs: I): Promise<O> {\n options?.onTrigger?.(...inputs);\n const p = _sharedAf('trigger', ...inputs);\n // 必须捕获错误,否则单测错误边界时会抛错\n p.catch(fnNoop);\n return p;\n };\n}\n\n// const af1 = asyncShared(async () => {\n// return 1;\n// });\n// const n = await af1();\n\n// const af2 = asyncShared(async (a: number) => {\n// return a + 1;\n// });\n// const n2 = await af2(2);\n"],"names":["_a","fnNoop"],"mappings":"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"}
package/dist/async.mjs CHANGED
@@ -15,7 +15,7 @@ var __privateWrapper = (obj, member, setter, getter) => ({
15
15
  }
16
16
  });
17
17
  var _tasks, _length, _AsyncQueue_instances, add_fn, addAndRun_fn, _startResolved, _startRejected, _startResults, _startPwr, _startLength, _running, run_fn, _stopResolved, _stopRejected, _stopLength, _stopResults, _stopPwr;
18
- import { fnNoop } from "./fn.mjs";
18
+ import { fnNoop } from "./function.mjs";
19
19
  class AsyncQueue {
20
20
  /**
21
21
  * 创建一个异步任务队列
@@ -1 +1 @@
1
- {"version":3,"file":"async.mjs","sources":["../src/async.ts"],"sourcesContent":["import { fnNoop } from './fn';\nimport type { AnyArray, AnyAsyncFunction } from './types';\n\n/**\n * 表示异步任务的类型\n * @template T - 任务返回值的类型\n */\ntype AsyncTask<T> = {\n /** 任务索引 */\n idx: number;\n /** 异步任务函数 */\n afn: () => Promise<T>;\n /** Promise 的解析器对象 */\n pwr?: PromiseWithResolvers<T>;\n};\n\n/**\n * 异步任务队列的配置选项\n */\nexport type AsyncQueueOptions = {\n /**\n * 并发限制数,0 表示无限制\n * @default 0\n */\n limit?: number;\n};\n\n/**\n * 异步任务队列,用于管理和控制异步任务的执行\n * @template T - 任务返回值的类型\n */\nexport class AsyncQueue<T> {\n #tasks: AsyncTask<T>[] = [];\n #length = 0;\n\n /**\n * 创建一个异步任务队列\n * @param asyncFns - 要执行的异步函数数组\n * @param options - 队列配置选项\n */\n constructor(\n asyncFns: Array<() => Promise<T>>,\n readonly options?: AsyncQueueOptions,\n ) {\n asyncFns.forEach((afn, idx) => {\n this.#add('push', afn);\n });\n }\n\n get length() {\n return this.#length;\n }\n\n get limit() {\n return this.options?.limit || 0;\n }\n\n #add(method: 'unshift' | 'push', afn: () => Promise<T>, pwr?: PromiseWithResolvers<T>) {\n this.#tasks[method]({\n idx: this.#length++,\n afn: afn,\n pwr: pwr,\n });\n }\n\n #addAndRun(method: 'unshift' | 'push', afn: () => Promise<T>) {\n // 明确终止了\n if (this.#stopLength >= 0) {\n throw new Error('异步队列已被终止,无法添加新的任务');\n }\n\n const pwr = Promise.withResolvers<T>();\n this.#add(method, afn, pwr);\n\n if (this.#startPwr && this.#running === 0) {\n this.#run();\n }\n\n return pwr.promise;\n }\n\n async push(afn: () => Promise<T>) {\n return this.#addAndRun('push', afn);\n }\n\n async unshift(afn: () => Promise<T>) {\n return this.#addAndRun('unshift', afn);\n }\n\n #startResolved = 0;\n #startRejected = 0;\n get startSettled() {\n return this.#startResolved === this.#startLength || this.#startRejected > 0;\n }\n\n #startResults: T[] = [];\n #startPwr: PromiseWithResolvers<T[]> | null = null;\n #startLength = 0;\n\n /**\n * 启动队列中的任务执行\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async start(): Promise<T[]> {\n if (this.#startPwr) return this.#startPwr.promise;\n\n // 固化启动时长度,便于判断 start 异步结果\n this.#startLength = this.#length;\n this.#startPwr = Promise.withResolvers<T[]>();\n\n if (this.#startLength === 0) {\n this.#startPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#startPwr.promise;\n }\n\n #running = 0;\n #run() {\n while (this.limit === 0 || this.#running < this.limit) {\n const task = this.#tasks.shift();\n\n // 无任务可执行\n if (!task) break;\n\n this.#running++;\n\n task\n .afn()\n .then((result) => {\n this.#running--;\n task.pwr?.resolve(result);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startResults[task.idx] = result;\n this.#startResolved++;\n }\n\n // 所有启动任务都已执行完毕\n if (this.#startResolved === this.#startLength) {\n this.#startPwr?.resolve(this.#startResults);\n }\n\n this.#stopResults[task.idx] = result;\n this.#stopResolved++;\n\n // 所有停止任务都已执行完毕\n if (this.#stopResolved === this.#stopLength) {\n this.#stopPwr?.resolve(this.#stopResults);\n } else {\n this.#run();\n }\n })\n .catch((reason) => {\n this.#running--;\n task.pwr?.reject(reason);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startRejected++;\n this.#startPwr?.reject(reason);\n }\n\n // 属于停止任务\n if (this.#stopLength > 0) {\n this.#stopRejected++;\n this.#stopPwr?.reject(reason);\n }\n });\n }\n }\n #stopResolved = 0;\n #stopRejected = 0;\n get stopSettled() {\n return this.#stopResolved === this.#stopLength || this.#stopRejected > 0;\n }\n\n #stopLength = -1;\n #stopResults: T[] = [];\n\n #stopPwr?: PromiseWithResolvers<T[]> | null = null;\n\n /**\n * 终止队列中的任务执行,终止队列后不再可以追加异步任务\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async stop() {\n if (this.#stopPwr) {\n return this.#stopPwr.promise;\n }\n\n this.#stopLength = this.#length;\n this.#stopPwr = Promise.withResolvers<T[]>();\n\n if (this.#stopLength === 0) {\n this.#stopPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#stopPwr.promise;\n }\n}\n\n/**\n * 使用给定的并发限制执行异步函数\n *\n * 此函数的目的是控制一组异步函数的并发执行数量,通过创建一个AsyncQueue实例来管理这些异步函数的执行\n * 它确保在任何给定时间只有最多`limit`数量的异步函数被执行,以避免潜在的性能问题或资源竞争\n *\n * @param asyncFns 一个包含异步函数的数组,每个异步函数都不需要参数,并返回一个Promise\n * @param limit 并发限制的数量,表示同时执行的异步函数的最大数量,0 表示不限制\n * @returns 返回一个Promise,当所有异步函数都执行完毕后,该Promise将被解析\n */\nexport function asyncLimit<T>(asyncFns: Array<() => Promise<T>>, limit: number) {\n const aq = new AsyncQueue<T>(asyncFns, { limit });\n return aq.start();\n}\n\n/**\n * 异步共享函数的配置选项\n */\nexport type AsyncSharedOptions<I extends AnyArray, O> = {\n /**\n * 是否在调用结束后再执行(只在运行期间有再次调用时才会生效)\n * @type {boolean}\n * @default false\n * @example\n * const sharedFn = asyncShared(fetchData, { trailing: true });\n * // 如果在 fetchData 执行期间多次调用 sharedFn,则会在 fetchData 结束后再次执行\n */\n trailing?: boolean;\n\n /**\n * 缓存结果的最大有效期(毫秒)\n * @type {number}\n * @example\n * const sharedFn = asyncShared(fetchData, { maxAge: 1000 });\n * // 在 1 秒内调用 sharedFn 会直接返回缓存结果\n */\n maxAge?: number;\n\n /**\n * 在调用共享函数时触发的回调函数\n * @param inputs - 传递给共享函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onTrigger: (...args) => console.log('Calling with:', args)\n * };\n */\n onTrigger?: (...inputs: I) => unknown;\n\n /**\n * 在执行异步函数时触发的回调函数\n * @param args - 传递给异步函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onExecute: (...args) => console.log('Executing with:', args)\n * };\n */\n onExecute?: (...args: I) => unknown;\n\n /**\n * 在异步函数成功执行后触发的回调函数\n * @param output - 异步函数的返回结果\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onSuccess: (result) => console.log('Success:', result)\n * };\n */\n onSuccess?: (output: O) => unknown;\n\n /**\n * 在异步函数执行失败时触发的回调函数\n * @param error - 异步函数抛出的错误\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onError: (error) => console.error('Error:', error)\n * };\n */\n onError?: (error: unknown) => unknown;\n\n /**\n * 在异步函数执行完成(无论成功或失败)时触发的回调函数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onFinally: () => console.log('Execution completed')\n * };\n */\n onFinally?: () => unknown;\n};\n\n/**\n * 创建一个共享执行结果的异步函数\n * @template F - 异步函数类型\n * @param {F} af - 要共享的异步函数\n * @param {AsyncSharedOptions} [options] - 配置选项\n * @returns {F} 返回一个新的异步函数,该函数会共享执行结果\n * @example\n * const fetchData = async (id) => {\n * // 模拟异步操作\n * return await fetch(`/api/data/${id}`);\n * };\n *\n * const sharedFetch = asyncShared(fetchData, { maxAge: 1000 });\n *\n * // 多次调用会共享同一个请求\n * const result1 = await sharedFetch(1);\n * const result2 = await sharedFetch(1); // 上次请求完成后 1000ms 内直接返回缓存结果\n */\nexport function asyncShared<I extends AnyArray, O>(\n af: (...inputs: I) => Promise<O>,\n options?: AsyncSharedOptions<I, O>,\n) {\n let executedPromise: Promise<O> | undefined;\n let executing = false;\n let executingInputs: I | undefined;\n let executedTime = 0;\n\n const _sharedAf = async (from: 'trigger' | 'trailing', ...inputs: I) => {\n executingInputs = inputs;\n\n // 如果正在运行,则复用运行结果\n if (executing && executedPromise) {\n return executedPromise;\n }\n\n // 如果已运行结束空闲时,判断是否在等待时间内\n if (executedPromise && Date.now() - executedTime < (options?.maxAge || 0)) {\n return executedPromise;\n }\n\n // 否则直接执行\n executing = true;\n options?.onExecute?.(...executingInputs);\n executedPromise = af(...executingInputs);\n executingInputs = undefined;\n executedPromise\n .then((res) => {\n options?.onSuccess?.(res);\n })\n .catch((err) => {\n options?.onError?.(err);\n })\n .finally(() => {\n executing = false;\n executedTime = Date.now();\n options?.onFinally?.();\n\n // 执行期间多次调用,则重新执行\n if (executingInputs && options?.trailing) {\n _sharedAf('trailing', ...executingInputs);\n }\n });\n\n return executedPromise;\n };\n\n return function sharedAf(...inputs: I): Promise<O> {\n options?.onTrigger?.(...inputs);\n const p = _sharedAf('trigger', ...inputs);\n // 必须捕获错误,否则单测错误边界时会抛错\n p.catch(fnNoop);\n return p;\n };\n}\n\n// const af1 = asyncShared(async () => {\n// return 1;\n// });\n// const n = await af1();\n\n// const af2 = asyncShared(async (a: number) => {\n// return a + 1;\n// });\n// const n2 = await af2(2);\n"],"names":["_a"],"mappings":"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"}
1
+ {"version":3,"file":"async.mjs","sources":["../src/async.ts"],"sourcesContent":["import { fnNoop } from './function';\nimport type { AnyArray, AnyAsyncFunction } from './types';\n\n/**\n * 表示异步任务的类型\n * @template T - 任务返回值的类型\n */\ntype AsyncTask<T> = {\n /** 任务索引 */\n idx: number;\n /** 异步任务函数 */\n afn: () => Promise<T>;\n /** Promise 的解析器对象 */\n pwr?: PromiseWithResolvers<T>;\n};\n\n/**\n * 异步任务队列的配置选项\n */\nexport type AsyncQueueOptions = {\n /**\n * 并发限制数,0 表示无限制\n * @default 0\n */\n limit?: number;\n};\n\n/**\n * 异步任务队列,用于管理和控制异步任务的执行\n * @template T - 任务返回值的类型\n */\nexport class AsyncQueue<T> {\n #tasks: AsyncTask<T>[] = [];\n #length = 0;\n\n /**\n * 创建一个异步任务队列\n * @param asyncFns - 要执行的异步函数数组\n * @param options - 队列配置选项\n */\n constructor(\n asyncFns: Array<() => Promise<T>>,\n readonly options?: AsyncQueueOptions,\n ) {\n asyncFns.forEach((afn, idx) => {\n this.#add('push', afn);\n });\n }\n\n get length() {\n return this.#length;\n }\n\n get limit() {\n return this.options?.limit || 0;\n }\n\n #add(method: 'unshift' | 'push', afn: () => Promise<T>, pwr?: PromiseWithResolvers<T>) {\n this.#tasks[method]({\n idx: this.#length++,\n afn: afn,\n pwr: pwr,\n });\n }\n\n #addAndRun(method: 'unshift' | 'push', afn: () => Promise<T>) {\n // 明确终止了\n if (this.#stopLength >= 0) {\n throw new Error('异步队列已被终止,无法添加新的任务');\n }\n\n const pwr = Promise.withResolvers<T>();\n this.#add(method, afn, pwr);\n\n if (this.#startPwr && this.#running === 0) {\n this.#run();\n }\n\n return pwr.promise;\n }\n\n async push(afn: () => Promise<T>) {\n return this.#addAndRun('push', afn);\n }\n\n async unshift(afn: () => Promise<T>) {\n return this.#addAndRun('unshift', afn);\n }\n\n #startResolved = 0;\n #startRejected = 0;\n get startSettled() {\n return this.#startResolved === this.#startLength || this.#startRejected > 0;\n }\n\n #startResults: T[] = [];\n #startPwr: PromiseWithResolvers<T[]> | null = null;\n #startLength = 0;\n\n /**\n * 启动队列中的任务执行\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async start(): Promise<T[]> {\n if (this.#startPwr) return this.#startPwr.promise;\n\n // 固化启动时长度,便于判断 start 异步结果\n this.#startLength = this.#length;\n this.#startPwr = Promise.withResolvers<T[]>();\n\n if (this.#startLength === 0) {\n this.#startPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#startPwr.promise;\n }\n\n #running = 0;\n #run() {\n while (this.limit === 0 || this.#running < this.limit) {\n const task = this.#tasks.shift();\n\n // 无任务可执行\n if (!task) break;\n\n this.#running++;\n\n task\n .afn()\n .then((result) => {\n this.#running--;\n task.pwr?.resolve(result);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startResults[task.idx] = result;\n this.#startResolved++;\n }\n\n // 所有启动任务都已执行完毕\n if (this.#startResolved === this.#startLength) {\n this.#startPwr?.resolve(this.#startResults);\n }\n\n this.#stopResults[task.idx] = result;\n this.#stopResolved++;\n\n // 所有停止任务都已执行完毕\n if (this.#stopResolved === this.#stopLength) {\n this.#stopPwr?.resolve(this.#stopResults);\n } else {\n this.#run();\n }\n })\n .catch((reason) => {\n this.#running--;\n task.pwr?.reject(reason);\n\n // 属于启动任务\n if (task.idx < this.#startLength) {\n this.#startRejected++;\n this.#startPwr?.reject(reason);\n }\n\n // 属于停止任务\n if (this.#stopLength > 0) {\n this.#stopRejected++;\n this.#stopPwr?.reject(reason);\n }\n });\n }\n }\n #stopResolved = 0;\n #stopRejected = 0;\n get stopSettled() {\n return this.#stopResolved === this.#stopLength || this.#stopRejected > 0;\n }\n\n #stopLength = -1;\n #stopResults: T[] = [];\n\n #stopPwr?: PromiseWithResolvers<T[]> | null = null;\n\n /**\n * 终止队列中的任务执行,终止队列后不再可以追加异步任务\n * @returns 返回一个 Promise,在所有启动任务完成后解析为结果数组\n */\n async stop() {\n if (this.#stopPwr) {\n return this.#stopPwr.promise;\n }\n\n this.#stopLength = this.#length;\n this.#stopPwr = Promise.withResolvers<T[]>();\n\n if (this.#stopLength === 0) {\n this.#stopPwr.resolve([]);\n } else {\n this.#run();\n }\n\n return this.#stopPwr.promise;\n }\n}\n\n/**\n * 使用给定的并发限制执行异步函数\n *\n * 此函数的目的是控制一组异步函数的并发执行数量,通过创建一个AsyncQueue实例来管理这些异步函数的执行\n * 它确保在任何给定时间只有最多`limit`数量的异步函数被执行,以避免潜在的性能问题或资源竞争\n *\n * @param asyncFns 一个包含异步函数的数组,每个异步函数都不需要参数,并返回一个Promise\n * @param limit 并发限制的数量,表示同时执行的异步函数的最大数量,0 表示不限制\n * @returns 返回一个Promise,当所有异步函数都执行完毕后,该Promise将被解析\n */\nexport function asyncLimit<T>(asyncFns: Array<() => Promise<T>>, limit: number) {\n const aq = new AsyncQueue<T>(asyncFns, { limit });\n return aq.start();\n}\n\n/**\n * 异步共享函数的配置选项\n */\nexport type AsyncSharedOptions<I extends AnyArray, O> = {\n /**\n * 是否在调用结束后再执行(只在运行期间有再次调用时才会生效)\n * @type {boolean}\n * @default false\n * @example\n * const sharedFn = asyncShared(fetchData, { trailing: true });\n * // 如果在 fetchData 执行期间多次调用 sharedFn,则会在 fetchData 结束后再次执行\n */\n trailing?: boolean;\n\n /**\n * 缓存结果的最大有效期(毫秒)\n * @type {number}\n * @example\n * const sharedFn = asyncShared(fetchData, { maxAge: 1000 });\n * // 在 1 秒内调用 sharedFn 会直接返回缓存结果\n */\n maxAge?: number;\n\n /**\n * 在调用共享函数时触发的回调函数\n * @param inputs - 传递给共享函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onTrigger: (...args) => console.log('Calling with:', args)\n * };\n */\n onTrigger?: (...inputs: I) => unknown;\n\n /**\n * 在执行异步函数时触发的回调函数\n * @param args - 传递给异步函数的参数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onExecute: (...args) => console.log('Executing with:', args)\n * };\n */\n onExecute?: (...args: I) => unknown;\n\n /**\n * 在异步函数成功执行后触发的回调函数\n * @param output - 异步函数的返回结果\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onSuccess: (result) => console.log('Success:', result)\n * };\n */\n onSuccess?: (output: O) => unknown;\n\n /**\n * 在异步函数执行失败时触发的回调函数\n * @param error - 异步函数抛出的错误\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onError: (error) => console.error('Error:', error)\n * };\n */\n onError?: (error: unknown) => unknown;\n\n /**\n * 在异步函数执行完成(无论成功或失败)时触发的回调函数\n * @example\n * const options: AsyncSharedOptions<typeof fetchData> = {\n * onFinally: () => console.log('Execution completed')\n * };\n */\n onFinally?: () => unknown;\n};\n\n/**\n * 创建一个共享执行结果的异步函数\n * @template F - 异步函数类型\n * @param {F} af - 要共享的异步函数\n * @param {AsyncSharedOptions} [options] - 配置选项\n * @returns {F} 返回一个新的异步函数,该函数会共享执行结果\n * @example\n * const fetchData = async (id) => {\n * // 模拟异步操作\n * return await fetch(`/api/data/${id}`);\n * };\n *\n * const sharedFetch = asyncShared(fetchData, { maxAge: 1000 });\n *\n * // 多次调用会共享同一个请求\n * const result1 = await sharedFetch(1);\n * const result2 = await sharedFetch(1); // 上次请求完成后 1000ms 内直接返回缓存结果\n */\nexport function asyncShared<I extends AnyArray, O>(\n af: (...inputs: I) => Promise<O>,\n options?: AsyncSharedOptions<I, O>,\n) {\n let executedPromise: Promise<O> | undefined;\n let executing = false;\n let executingInputs: I | undefined;\n let executedTime = 0;\n\n const _sharedAf = async (from: 'trigger' | 'trailing', ...inputs: I) => {\n executingInputs = inputs;\n\n // 如果正在运行,则复用运行结果\n if (executing && executedPromise) {\n return executedPromise;\n }\n\n // 如果已运行结束空闲时,判断是否在等待时间内\n if (executedPromise && Date.now() - executedTime < (options?.maxAge || 0)) {\n return executedPromise;\n }\n\n // 否则直接执行\n executing = true;\n options?.onExecute?.(...executingInputs);\n executedPromise = af(...executingInputs);\n executingInputs = undefined;\n executedPromise\n .then((res) => {\n options?.onSuccess?.(res);\n })\n .catch((err) => {\n options?.onError?.(err);\n })\n .finally(() => {\n executing = false;\n executedTime = Date.now();\n options?.onFinally?.();\n\n // 执行期间多次调用,则重新执行\n if (executingInputs && options?.trailing) {\n _sharedAf('trailing', ...executingInputs);\n }\n });\n\n return executedPromise;\n };\n\n return function sharedAf(...inputs: I): Promise<O> {\n options?.onTrigger?.(...inputs);\n const p = _sharedAf('trigger', ...inputs);\n // 必须捕获错误,否则单测错误边界时会抛错\n p.catch(fnNoop);\n return p;\n };\n}\n\n// const af1 = asyncShared(async () => {\n// return 1;\n// });\n// const n = await af1();\n\n// const af2 = asyncShared(async (a: number) => {\n// return a + 1;\n// });\n// const n2 = await af2(2);\n"],"names":["_a"],"mappings":"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"}
package/dist/dict.cjs CHANGED
@@ -1,2 +1,58 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function _defineDict(definition) {
4
+ const keys = Object.keys(definition);
5
+ return {
6
+ ...[...Object.entries(definition)].reduce((acc, [key, dfn]) => {
7
+ if (key[0].toUpperCase() !== key[0]) {
8
+ throw new Error(`错误:枚举键名 ${key} 必须以大写字母开头`);
9
+ }
10
+ if (key.startsWith("$")) {
11
+ throw new Error(`错误:枚举键名 ${key} 不能以 $ 符号开头`);
12
+ }
13
+ acc[key] = dfn.value;
14
+ acc[`$${key}`] = { key, ...dfn };
15
+ return acc;
16
+ }, {}),
17
+ definition,
18
+ descriptions: keys.map((key) => ({
19
+ key,
20
+ ...definition[key]
21
+ })),
22
+ keys,
23
+ length: keys.length,
24
+ values: keys.map((key) => definition[key].value),
25
+ kvRecord: keys.reduce((acc, key) => {
26
+ acc[key] = definition[key].value;
27
+ return acc;
28
+ }, {}),
29
+ vkRecord: keys.reduce((acc, key) => {
30
+ acc[definition[key].value] = key;
31
+ return acc;
32
+ }, {}),
33
+ toKeyRecord(prop) {
34
+ return keys.reduce((acc, key) => {
35
+ acc[key] = definition[key][prop];
36
+ return acc;
37
+ }, {});
38
+ },
39
+ toValRecord(prop) {
40
+ return keys.reduce(
41
+ (acc, key) => {
42
+ acc[definition[key].value] = definition[key][prop];
43
+ return acc;
44
+ },
45
+ {}
46
+ );
47
+ }
48
+ };
49
+ }
50
+ function declareDict() {
51
+ return {
52
+ define(definition) {
53
+ return _defineDict(definition);
54
+ }
55
+ };
56
+ }
57
+ exports.declareDict = declareDict;
2
58
  //# sourceMappingURL=dict.cjs.map
package/dist/dict.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dict.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"dict.cjs","sources":["../src/dict.ts"],"sourcesContent":["import type { AnyObject, MergeIntersection, UnionToIntersection, UnionToTuple } from './types';\n\nexport type DictKey = string;\nexport type DictValue = number | string;\nexport type DictMetaAppend = {\n key?: string;\n value?: string | number;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n [key: string]: any;\n};\nexport type DictMeta<A extends DictMetaAppend> = A & {\n value: DictValue;\n};\nexport type DictDescription<A extends DictMetaAppend> = Record<DictKey, DictMeta<A>>;\n\nconst dictError = Symbol('dictKeyError');\n\ntype _StartWithDollarSign<T extends string> = T extends `$${infer R}` ? R : never;\n\ntype _CheckDefinition<O extends AnyObject> = {\n // [K in keyof O & string]: K extends Capitalize<K> ? O[K] : `错误:枚举键名 ${K} 必须大写字母开头`;\n [K in keyof O & string]: K extends Capitalize<K>\n ? K extends `$${infer R}`\n ? O[K] & { [dictError]: `错误:枚举键名 ${K} 不能以 $ 符号开头` }\n : O[K]\n : O[K] & { [dictError]: `错误:枚举键名 ${K} 必须以大写字母开头` };\n};\n\ntype _ToOriginDefProp<T extends AnyObject> = {\n [K in keyof T as `$${K & string}`]: MergeIntersection<T[K] & { readonly key: K }>;\n};\n\ntype _KVRecord<T> = T extends Record<string, AnyObject>\n ? {\n readonly [K in keyof T]: T[K]['value'];\n }\n : never;\n\ntype _VKRecord<T> = T extends Record<string, AnyObject>\n ? MergeIntersection<\n UnionToIntersection<\n {\n [K in keyof T]: {\n [P in keyof T[K] as P extends 'value' ? T[K][P] & (string | number) : never]: K;\n };\n }[keyof T]\n >\n >\n : never;\n\nexport type DictExpose<A extends DictMetaAppend, E extends DictDescription<A>> = _ToOriginDefProp<E> &\n _KVRecord<E> & {\n readonly definition: E;\n readonly descriptions: MergeIntersection<A & { key: keyof E }>[];\n readonly keys: UnionToTuple<keyof E>;\n readonly length: UnionToTuple<keyof E>['length'];\n readonly values: UnionToTuple<E[keyof E]['value']>;\n readonly kvRecord: _KVRecord<E>;\n readonly vkRecord: _VKRecord<E>;\n toKeyRecord: <P extends keyof A>(prop: P) => Record<keyof E, A[P]>;\n toValRecord: <P extends keyof A>(prop: P) => Record<E[keyof E]['value'], A[P]>;\n };\n\n/**\n * 定义一个枚举类型,如果需要类型提示,需要使用 declareDict 函数定义枚举。\n * @template A - 枚举元数据附加类型,扩展自 DictMetaAppend\n * @template E - 枚举描述类型,键为枚举键名,值为枚举元数据\n * @param {_CheckDefinition<E>} definition - 枚举定义对象\n * @returns {DictExpose<A, E>} 返回枚举的完整暴露对象\n * @example\n * ```typescript\n * const Status = defineDict({\n * Pending: { value: 0, label: '待处理' },\n * Approved: { value: 1, label: '已批准' }\n * });\n * ```\n *\n * @property {E} definition - 原始枚举定义对象\n * @property {Array<MergeIntersection<A & { key: keyof E }>>} descriptions - 枚举项的完整描述数组\n * @property {UnionToTuple<keyof E>} keys - 枚举键名的元组\n * @property {\"UnionToTuple<keyof E>[\\\"length\\\"]\"} length - 枚举项的数量\n * @property {UnionToTuple<E[keyof E][\"value\"]>} values - 枚举值的元组\n * @property {Record<keyof E, E[keyof E][\"value\"]>} kvRecord - 键到值的映射记录\n * @property {Record<E[keyof E][\"value\"], E[keyof E]>} vkRecord - 值到键的映射记录\n * @property {function} toKeyRecord - 根据属性名创建键到属性值的映射记录\n * @property {function} toValueRecord - 根据属性名创建值到属性值的映射记录\n */\nfunction _defineDict<A extends DictMetaAppend, const E extends DictDescription<A>>(\n definition: _CheckDefinition<E>,\n): DictExpose<A, E> {\n const keys = Object.keys(definition);\n\n return {\n ...[...Object.entries(definition)].reduce((acc, [key, dfn]) => {\n if (key[0].toUpperCase() !== key[0]) {\n throw new Error(`错误:枚举键名 ${key} 必须以大写字母开头`);\n }\n\n if (key.startsWith('$')) {\n throw new Error(`错误:枚举键名 ${key} 不能以 $ 符号开头`);\n }\n\n // @ts-ignore\n acc[key] = dfn.value;\n // @ts-ignore\n acc[`$${key}`] = { key, ...dfn };\n return acc;\n }, {}),\n definition,\n descriptions: keys.map((key) => ({\n key,\n ...definition[key],\n })),\n keys,\n length: keys.length,\n values: keys.map((key) => definition[key as keyof E].value),\n kvRecord: keys.reduce((acc, key) => {\n // @ts-ignore\n acc[key] = definition[key].value;\n return acc;\n }, {}),\n vkRecord: keys.reduce((acc, key) => {\n // @ts-ignore\n acc[definition[key].value] = key;\n return acc;\n }, {}),\n toKeyRecord<P extends keyof A>(prop: P) {\n return keys.reduce((acc, key) => {\n // @ts-ignore\n acc[key] = definition[key][prop];\n return acc;\n }, {});\n },\n toValRecord<P extends keyof A>(prop: P) {\n return keys.reduce(\n (acc, key) => {\n // @ts-ignore\n acc[definition[key].value] = definition[key][prop];\n return acc;\n },\n {} as Record<E[keyof E]['value'], A[P]>,\n );\n },\n } as unknown as DictExpose<A, E>;\n}\n\n/**\n * 声明一个枚举工厂函数\n * @template A - 枚举元数据附加类型,扩展自 DictMetaAppend\n * @returns {Object} 返回包含 define 方法的对象\n *\n * @property {function} define - 定义枚举的函数\n * @template E - 枚举描述类型\n * @param {_CheckDefinition<E>} definition - 枚举定义对象\n * @returns {DictExpose<A, E>} 返回枚举的完整暴露对象\n * @example\n * ```typescript\n * const createStatusDict = declareDict<{ label: string }>();\n * const Status = createStatusDict.define({\n * Pending: { value: 0, label: '待处理' },\n * Approved: { value: 1, label: '已批准' }\n * });\n * ```\n */\nexport function declareDict<A extends DictMetaAppend>() {\n return {\n define<const E extends DictDescription<A>>(definition: _CheckDefinition<E>): DictExpose<A, E> {\n return _defineDict(definition);\n },\n };\n}\n"],"names":[],"mappings":";;AAuFA,SAAS,YACP,YACkB;AACZ,QAAA,OAAO,OAAO,KAAK,UAAU;AAE5B,SAAA;AAAA,IACL,GAAG,CAAC,GAAG,OAAO,QAAQ,UAAU,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM;AAC7D,UAAI,IAAI,CAAC,EAAE,kBAAkB,IAAI,CAAC,GAAG;AACnC,cAAM,IAAI,MAAM,WAAW,GAAG,YAAY;AAAA,MAAA;AAGxC,UAAA,IAAI,WAAW,GAAG,GAAG;AACvB,cAAM,IAAI,MAAM,WAAW,GAAG,aAAa;AAAA,MAAA;AAIzC,UAAA,GAAG,IAAI,IAAI;AAEf,UAAI,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI;AACxB,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,IACL;AAAA,IACA,cAAc,KAAK,IAAI,CAAC,SAAS;AAAA,MAC/B;AAAA,MACA,GAAG,WAAW,GAAG;AAAA,IAAA,EACjB;AAAA,IACF;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK,IAAI,CAAC,QAAQ,WAAW,GAAc,EAAE,KAAK;AAAA,IAC1D,UAAU,KAAK,OAAO,CAAC,KAAK,QAAQ;AAElC,UAAI,GAAG,IAAI,WAAW,GAAG,EAAE;AACpB,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,IACL,UAAU,KAAK,OAAO,CAAC,KAAK,QAAQ;AAElC,UAAI,WAAW,GAAG,EAAE,KAAK,IAAI;AACtB,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,IACL,YAA+B,MAAS;AACtC,aAAO,KAAK,OAAO,CAAC,KAAK,QAAQ;AAE/B,YAAI,GAAG,IAAI,WAAW,GAAG,EAAE,IAAI;AACxB,eAAA;AAAA,MACT,GAAG,EAAE;AAAA,IACP;AAAA,IACA,YAA+B,MAAS;AACtC,aAAO,KAAK;AAAA,QACV,CAAC,KAAK,QAAQ;AAER,cAAA,WAAW,GAAG,EAAE,KAAK,IAAI,WAAW,GAAG,EAAE,IAAI;AAC1C,iBAAA;AAAA,QACT;AAAA,QACA,CAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;AACF;AAoBO,SAAS,cAAwC;AAC/C,SAAA;AAAA,IACL,OAA2C,YAAmD;AAC5F,aAAO,YAAY,UAAU;AAAA,IAAA;AAAA,EAEjC;AACF;;"}
package/dist/dict.d.ts CHANGED
@@ -1 +1,68 @@
1
+ import { AnyObject, MergeIntersection, UnionToIntersection, UnionToTuple } from './types';
2
+ export type DictKey = string;
3
+ export type DictValue = number | string;
4
+ export type DictMetaAppend = {
5
+ key?: string;
6
+ value?: string | number;
7
+ [key: string]: any;
8
+ };
9
+ export type DictMeta<A extends DictMetaAppend> = A & {
10
+ value: DictValue;
11
+ };
12
+ export type DictDescription<A extends DictMetaAppend> = Record<DictKey, DictMeta<A>>;
13
+ declare const dictError: unique symbol;
14
+ type _CheckDefinition<O extends AnyObject> = {
15
+ [K in keyof O & string]: K extends Capitalize<K> ? K extends `$${infer R}` ? O[K] & {
16
+ [dictError]: `错误:枚举键名 ${K} 不能以 $ 符号开头`;
17
+ } : O[K] : O[K] & {
18
+ [dictError]: `错误:枚举键名 ${K} 必须以大写字母开头`;
19
+ };
20
+ };
21
+ type _ToOriginDefProp<T extends AnyObject> = {
22
+ [K in keyof T as `$${K & string}`]: MergeIntersection<T[K] & {
23
+ readonly key: K;
24
+ }>;
25
+ };
26
+ type _KVRecord<T> = T extends Record<string, AnyObject> ? {
27
+ readonly [K in keyof T]: T[K]['value'];
28
+ } : never;
29
+ type _VKRecord<T> = T extends Record<string, AnyObject> ? MergeIntersection<UnionToIntersection<{
30
+ [K in keyof T]: {
31
+ [P in keyof T[K] as P extends 'value' ? T[K][P] & (string | number) : never]: K;
32
+ };
33
+ }[keyof T]>> : never;
34
+ export type DictExpose<A extends DictMetaAppend, E extends DictDescription<A>> = _ToOriginDefProp<E> & _KVRecord<E> & {
35
+ readonly definition: E;
36
+ readonly descriptions: MergeIntersection<A & {
37
+ key: keyof E;
38
+ }>[];
39
+ readonly keys: UnionToTuple<keyof E>;
40
+ readonly length: UnionToTuple<keyof E>['length'];
41
+ readonly values: UnionToTuple<E[keyof E]['value']>;
42
+ readonly kvRecord: _KVRecord<E>;
43
+ readonly vkRecord: _VKRecord<E>;
44
+ toKeyRecord: <P extends keyof A>(prop: P) => Record<keyof E, A[P]>;
45
+ toValRecord: <P extends keyof A>(prop: P) => Record<E[keyof E]['value'], A[P]>;
46
+ };
47
+ /**
48
+ * 声明一个枚举工厂函数
49
+ * @template A - 枚举元数据附加类型,扩展自 DictMetaAppend
50
+ * @returns {Object} 返回包含 define 方法的对象
51
+ *
52
+ * @property {function} define - 定义枚举的函数
53
+ * @template E - 枚举描述类型
54
+ * @param {_CheckDefinition<E>} definition - 枚举定义对象
55
+ * @returns {DictExpose<A, E>} 返回枚举的完整暴露对象
56
+ * @example
57
+ * ```typescript
58
+ * const createStatusDict = declareDict<{ label: string }>();
59
+ * const Status = createStatusDict.define({
60
+ * Pending: { value: 0, label: '待处理' },
61
+ * Approved: { value: 1, label: '已批准' }
62
+ * });
63
+ * ```
64
+ */
65
+ export declare function declareDict<A extends DictMetaAppend>(): {
66
+ define<const E extends DictDescription<A>>(definition: _CheckDefinition<E>): DictExpose<A, E>;
67
+ };
1
68
  export {};
package/dist/dict.mjs CHANGED
@@ -1,2 +1,58 @@
1
-
1
+ function _defineDict(definition) {
2
+ const keys = Object.keys(definition);
3
+ return {
4
+ ...[...Object.entries(definition)].reduce((acc, [key, dfn]) => {
5
+ if (key[0].toUpperCase() !== key[0]) {
6
+ throw new Error(`错误:枚举键名 ${key} 必须以大写字母开头`);
7
+ }
8
+ if (key.startsWith("$")) {
9
+ throw new Error(`错误:枚举键名 ${key} 不能以 $ 符号开头`);
10
+ }
11
+ acc[key] = dfn.value;
12
+ acc[`$${key}`] = { key, ...dfn };
13
+ return acc;
14
+ }, {}),
15
+ definition,
16
+ descriptions: keys.map((key) => ({
17
+ key,
18
+ ...definition[key]
19
+ })),
20
+ keys,
21
+ length: keys.length,
22
+ values: keys.map((key) => definition[key].value),
23
+ kvRecord: keys.reduce((acc, key) => {
24
+ acc[key] = definition[key].value;
25
+ return acc;
26
+ }, {}),
27
+ vkRecord: keys.reduce((acc, key) => {
28
+ acc[definition[key].value] = key;
29
+ return acc;
30
+ }, {}),
31
+ toKeyRecord(prop) {
32
+ return keys.reduce((acc, key) => {
33
+ acc[key] = definition[key][prop];
34
+ return acc;
35
+ }, {});
36
+ },
37
+ toValRecord(prop) {
38
+ return keys.reduce(
39
+ (acc, key) => {
40
+ acc[definition[key].value] = definition[key][prop];
41
+ return acc;
42
+ },
43
+ {}
44
+ );
45
+ }
46
+ };
47
+ }
48
+ function declareDict() {
49
+ return {
50
+ define(definition) {
51
+ return _defineDict(definition);
52
+ }
53
+ };
54
+ }
55
+ export {
56
+ declareDict
57
+ };
2
58
  //# sourceMappingURL=dict.mjs.map
package/dist/dict.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dict.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"dict.mjs","sources":["../src/dict.ts"],"sourcesContent":["import type { AnyObject, MergeIntersection, UnionToIntersection, UnionToTuple } from './types';\n\nexport type DictKey = string;\nexport type DictValue = number | string;\nexport type DictMetaAppend = {\n key?: string;\n value?: string | number;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n [key: string]: any;\n};\nexport type DictMeta<A extends DictMetaAppend> = A & {\n value: DictValue;\n};\nexport type DictDescription<A extends DictMetaAppend> = Record<DictKey, DictMeta<A>>;\n\nconst dictError = Symbol('dictKeyError');\n\ntype _StartWithDollarSign<T extends string> = T extends `$${infer R}` ? R : never;\n\ntype _CheckDefinition<O extends AnyObject> = {\n // [K in keyof O & string]: K extends Capitalize<K> ? O[K] : `错误:枚举键名 ${K} 必须大写字母开头`;\n [K in keyof O & string]: K extends Capitalize<K>\n ? K extends `$${infer R}`\n ? O[K] & { [dictError]: `错误:枚举键名 ${K} 不能以 $ 符号开头` }\n : O[K]\n : O[K] & { [dictError]: `错误:枚举键名 ${K} 必须以大写字母开头` };\n};\n\ntype _ToOriginDefProp<T extends AnyObject> = {\n [K in keyof T as `$${K & string}`]: MergeIntersection<T[K] & { readonly key: K }>;\n};\n\ntype _KVRecord<T> = T extends Record<string, AnyObject>\n ? {\n readonly [K in keyof T]: T[K]['value'];\n }\n : never;\n\ntype _VKRecord<T> = T extends Record<string, AnyObject>\n ? MergeIntersection<\n UnionToIntersection<\n {\n [K in keyof T]: {\n [P in keyof T[K] as P extends 'value' ? T[K][P] & (string | number) : never]: K;\n };\n }[keyof T]\n >\n >\n : never;\n\nexport type DictExpose<A extends DictMetaAppend, E extends DictDescription<A>> = _ToOriginDefProp<E> &\n _KVRecord<E> & {\n readonly definition: E;\n readonly descriptions: MergeIntersection<A & { key: keyof E }>[];\n readonly keys: UnionToTuple<keyof E>;\n readonly length: UnionToTuple<keyof E>['length'];\n readonly values: UnionToTuple<E[keyof E]['value']>;\n readonly kvRecord: _KVRecord<E>;\n readonly vkRecord: _VKRecord<E>;\n toKeyRecord: <P extends keyof A>(prop: P) => Record<keyof E, A[P]>;\n toValRecord: <P extends keyof A>(prop: P) => Record<E[keyof E]['value'], A[P]>;\n };\n\n/**\n * 定义一个枚举类型,如果需要类型提示,需要使用 declareDict 函数定义枚举。\n * @template A - 枚举元数据附加类型,扩展自 DictMetaAppend\n * @template E - 枚举描述类型,键为枚举键名,值为枚举元数据\n * @param {_CheckDefinition<E>} definition - 枚举定义对象\n * @returns {DictExpose<A, E>} 返回枚举的完整暴露对象\n * @example\n * ```typescript\n * const Status = defineDict({\n * Pending: { value: 0, label: '待处理' },\n * Approved: { value: 1, label: '已批准' }\n * });\n * ```\n *\n * @property {E} definition - 原始枚举定义对象\n * @property {Array<MergeIntersection<A & { key: keyof E }>>} descriptions - 枚举项的完整描述数组\n * @property {UnionToTuple<keyof E>} keys - 枚举键名的元组\n * @property {\"UnionToTuple<keyof E>[\\\"length\\\"]\"} length - 枚举项的数量\n * @property {UnionToTuple<E[keyof E][\"value\"]>} values - 枚举值的元组\n * @property {Record<keyof E, E[keyof E][\"value\"]>} kvRecord - 键到值的映射记录\n * @property {Record<E[keyof E][\"value\"], E[keyof E]>} vkRecord - 值到键的映射记录\n * @property {function} toKeyRecord - 根据属性名创建键到属性值的映射记录\n * @property {function} toValueRecord - 根据属性名创建值到属性值的映射记录\n */\nfunction _defineDict<A extends DictMetaAppend, const E extends DictDescription<A>>(\n definition: _CheckDefinition<E>,\n): DictExpose<A, E> {\n const keys = Object.keys(definition);\n\n return {\n ...[...Object.entries(definition)].reduce((acc, [key, dfn]) => {\n if (key[0].toUpperCase() !== key[0]) {\n throw new Error(`错误:枚举键名 ${key} 必须以大写字母开头`);\n }\n\n if (key.startsWith('$')) {\n throw new Error(`错误:枚举键名 ${key} 不能以 $ 符号开头`);\n }\n\n // @ts-ignore\n acc[key] = dfn.value;\n // @ts-ignore\n acc[`$${key}`] = { key, ...dfn };\n return acc;\n }, {}),\n definition,\n descriptions: keys.map((key) => ({\n key,\n ...definition[key],\n })),\n keys,\n length: keys.length,\n values: keys.map((key) => definition[key as keyof E].value),\n kvRecord: keys.reduce((acc, key) => {\n // @ts-ignore\n acc[key] = definition[key].value;\n return acc;\n }, {}),\n vkRecord: keys.reduce((acc, key) => {\n // @ts-ignore\n acc[definition[key].value] = key;\n return acc;\n }, {}),\n toKeyRecord<P extends keyof A>(prop: P) {\n return keys.reduce((acc, key) => {\n // @ts-ignore\n acc[key] = definition[key][prop];\n return acc;\n }, {});\n },\n toValRecord<P extends keyof A>(prop: P) {\n return keys.reduce(\n (acc, key) => {\n // @ts-ignore\n acc[definition[key].value] = definition[key][prop];\n return acc;\n },\n {} as Record<E[keyof E]['value'], A[P]>,\n );\n },\n } as unknown as DictExpose<A, E>;\n}\n\n/**\n * 声明一个枚举工厂函数\n * @template A - 枚举元数据附加类型,扩展自 DictMetaAppend\n * @returns {Object} 返回包含 define 方法的对象\n *\n * @property {function} define - 定义枚举的函数\n * @template E - 枚举描述类型\n * @param {_CheckDefinition<E>} definition - 枚举定义对象\n * @returns {DictExpose<A, E>} 返回枚举的完整暴露对象\n * @example\n * ```typescript\n * const createStatusDict = declareDict<{ label: string }>();\n * const Status = createStatusDict.define({\n * Pending: { value: 0, label: '待处理' },\n * Approved: { value: 1, label: '已批准' }\n * });\n * ```\n */\nexport function declareDict<A extends DictMetaAppend>() {\n return {\n define<const E extends DictDescription<A>>(definition: _CheckDefinition<E>): DictExpose<A, E> {\n return _defineDict(definition);\n },\n };\n}\n"],"names":[],"mappings":"AAuFA,SAAS,YACP,YACkB;AACZ,QAAA,OAAO,OAAO,KAAK,UAAU;AAE5B,SAAA;AAAA,IACL,GAAG,CAAC,GAAG,OAAO,QAAQ,UAAU,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM;AAC7D,UAAI,IAAI,CAAC,EAAE,kBAAkB,IAAI,CAAC,GAAG;AACnC,cAAM,IAAI,MAAM,WAAW,GAAG,YAAY;AAAA,MAAA;AAGxC,UAAA,IAAI,WAAW,GAAG,GAAG;AACvB,cAAM,IAAI,MAAM,WAAW,GAAG,aAAa;AAAA,MAAA;AAIzC,UAAA,GAAG,IAAI,IAAI;AAEf,UAAI,IAAI,GAAG,EAAE,IAAI,EAAE,KAAK,GAAG,IAAI;AACxB,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,IACL;AAAA,IACA,cAAc,KAAK,IAAI,CAAC,SAAS;AAAA,MAC/B;AAAA,MACA,GAAG,WAAW,GAAG;AAAA,IAAA,EACjB;AAAA,IACF;AAAA,IACA,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK,IAAI,CAAC,QAAQ,WAAW,GAAc,EAAE,KAAK;AAAA,IAC1D,UAAU,KAAK,OAAO,CAAC,KAAK,QAAQ;AAElC,UAAI,GAAG,IAAI,WAAW,GAAG,EAAE;AACpB,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,IACL,UAAU,KAAK,OAAO,CAAC,KAAK,QAAQ;AAElC,UAAI,WAAW,GAAG,EAAE,KAAK,IAAI;AACtB,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,IACL,YAA+B,MAAS;AACtC,aAAO,KAAK,OAAO,CAAC,KAAK,QAAQ;AAE/B,YAAI,GAAG,IAAI,WAAW,GAAG,EAAE,IAAI;AACxB,eAAA;AAAA,MACT,GAAG,EAAE;AAAA,IACP;AAAA,IACA,YAA+B,MAAS;AACtC,aAAO,KAAK;AAAA,QACV,CAAC,KAAK,QAAQ;AAER,cAAA,WAAW,GAAG,EAAE,KAAK,IAAI,WAAW,GAAG,EAAE,IAAI;AAC1C,iBAAA;AAAA,QACT;AAAA,QACA,CAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;AACF;AAoBO,SAAS,cAAwC;AAC/C,SAAA;AAAA,IACL,OAA2C,YAAmD;AAC5F,aAAO,YAAY,UAAU;AAAA,IAAA;AAAA,EAEjC;AACF;"}
@@ -73,4 +73,4 @@ exports.fnDebounce = fnDebounce;
73
73
  exports.fnNoop = fnNoop;
74
74
  exports.fnOnce = fnOnce;
75
75
  exports.fnThrottle = fnThrottle;
76
- //# sourceMappingURL=fn.cjs.map
76
+ //# sourceMappingURL=function.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function.cjs","sources":["../src/function.ts"],"sourcesContent":["import { isNumber } from './type';\nimport type { AnyFunction } from './types';\n\n/**\n * 一个空操作函数,不执行任何操作。\n *\n * @example\n * ```typescript\n * fnNoop(); // 不执行任何操作\n * ```\n */\nexport function fnNoop() {\n //\n}\n\n/**\n * 防抖函数的配置选项。\n */\nexport type DebounceOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n /**\n * 是否在等待开始时立即执行一次\n * @default false\n */\n leading?: boolean;\n};\n\n/**\n * 创建一个防抖函数,该函数会在指定的等待时间后执行,如果在等待时间内再次调用,则重新计时。\n *\n * @param fn - 需要防抖的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait` 和 `leading` 选项的对象。\n * @returns 返回一个防抖函数,该函数具有 `cancel` 方法,用于取消防抖操作。\n *\n * @example\n * ```typescript\n * const debouncedFn = fnDebounce(() => {\n * console.log('Debounced!');\n * }, 100);\n *\n * debouncedFn(); // 不会立即执行\n * debouncedFn(); // 重新计时\n * debouncedFn.cancel(); // 取消防抖操作\n * ```\n */\nexport function fnDebounce<F extends AnyFunction>(fn: F, wait: number | DebounceOptions) {\n const options: DebounceOptions = isNumber(wait) ? { wait } : wait;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let timer: any;\n let leading = false;\n\n const debounced = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n // 第一次执行\n if (options.leading && !leading) {\n leading = true;\n fn.apply(this, args);\n return;\n }\n\n // 最后一次执行\n clearTimeout(timer);\n timer = setTimeout(() => {\n if (canceled) return;\n\n fn.apply(this, args);\n }, options.wait);\n };\n\n debounced.cancel = () => {\n clearTimeout(timer);\n canceled = true;\n };\n\n return debounced;\n}\n\nexport type ThrottleOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n\n /**\n * 是否在第一次调用时立即执行\n * @default false\n */\n leading?: boolean;\n\n /**\n * 是否在调用结束后等待一段时间再执行\n * @default false\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个节流函数,该函数会在指定的等待时间内最多执行一次,如果在等待时间内再次调用,则忽略后续调用。\n *\n * @param fn - 需要节流的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait`、`leading` 和 `trailing` 选项的对象。\n * @returns 返回一个节流函数,该函数具有 `cancel` 方法,用于取消节流操作。\n *\n * @example\n * ```typescript\n * const throttledFn = fnThrottle(() => {\n * console.log('Throttled!');\n * }, 100);\n *\n * throttledFn(); // 立即执行\n * throttledFn(); // 忽略\n * throttledFn.cancel(); // 取消节流操作\n * ```\n */\nexport function fnThrottle<F extends AnyFunction>(fn: F, wait: number | ThrottleOptions) {\n const options = isNumber(wait) ? { wait } : wait;\n const waitFinal = options.wait;\n let lastTime = 0;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let timer: any;\n\n const throttled = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n const now = Date.now();\n\n // 第一次执行\n if (options.leading && lastTime === 0) {\n lastTime = now;\n fn.apply(this, args);\n }\n\n // 中间控频执行\n else if (lastTime > 0 && now - lastTime >= waitFinal) {\n lastTime = now;\n fn.apply(this, args);\n }\n\n // 首次计时\n else if (lastTime === 0) {\n lastTime = now;\n }\n\n // 最后一次执行\n if (options.trailing) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, waitFinal);\n }\n };\n\n throttled.cancel = () => {\n canceled = true;\n clearTimeout(timer);\n };\n\n return throttled;\n}\n\n/**\n * 创建一个只执行一次的函数,无论调用多少次,实际执行的函数体也只会执行一次。\n *\n * @param fn - 需要只执行一次的函数。\n * @returns 返回一个只执行一次的函数,该函数在第一次调用后会缓存结果并返回缓存的结果。\n *\n * @example\n * ```typescript\n * const onceFn = fnOnce(() => {\n * console.log('This will be logged only once.');\n * return 42;\n * });\n *\n * console.log(onceFn()); // 输出: This will be logged only once. 42\n * console.log(onceFn()); // 输出: 42\n * ```\n */\nexport function fnOnce<F extends AnyFunction>(fn: F) {\n let called = false;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let result: any;\n\n return function (this: unknown, ...args: Parameters<F>) {\n if (!called) {\n called = true;\n result = fn.apply(this, args);\n }\n\n return result;\n };\n}\n"],"names":["isNumber"],"mappings":";;;AAWO,SAAS,SAAS;AAEzB;AAmCgB,SAAA,WAAkC,IAAO,MAAgC;AACvF,QAAM,UAA2BA,KAAAA,SAAS,IAAI,IAAI,EAAE,KAAS,IAAA;AAC7D,MAAI,WAAW;AAEX,MAAA;AACJ,MAAI,UAAU;AAER,QAAA,YAAY,YAA4B,MAAqB;AACjE,QAAI,SAAU;AAGV,QAAA,QAAQ,WAAW,CAAC,SAAS;AACrB,gBAAA;AACP,SAAA,MAAM,MAAM,IAAI;AACnB;AAAA,IAAA;AAIF,iBAAa,KAAK;AAClB,YAAQ,WAAW,MAAM;AACvB,UAAI,SAAU;AAEX,SAAA,MAAM,MAAM,IAAI;AAAA,IAAA,GAClB,QAAQ,IAAI;AAAA,EACjB;AAEA,YAAU,SAAS,MAAM;AACvB,iBAAa,KAAK;AACP,eAAA;AAAA,EACb;AAEO,SAAA;AACT;AAuCgB,SAAA,WAAkC,IAAO,MAAgC;AACvF,QAAM,UAAUA,KAAAA,SAAS,IAAI,IAAI,EAAE,KAAS,IAAA;AAC5C,QAAM,YAAY,QAAQ;AAC1B,MAAI,WAAW;AACf,MAAI,WAAW;AAEX,MAAA;AAEE,QAAA,YAAY,YAA4B,MAAqB;AACjE,QAAI,SAAU;AAER,UAAA,MAAM,KAAK,IAAI;AAGjB,QAAA,QAAQ,WAAW,aAAa,GAAG;AAC1B,iBAAA;AACR,SAAA,MAAM,MAAM,IAAI;AAAA,IAIZ,WAAA,WAAW,KAAK,MAAM,YAAY,WAAW;AACzC,iBAAA;AACR,SAAA,MAAM,MAAM,IAAI;AAAA,IAAA,WAIZ,aAAa,GAAG;AACZ,iBAAA;AAAA,IAAA;AAIb,QAAI,QAAQ,UAAU;AACpB,mBAAa,KAAK;AAClB,cAAQ,WAAW,MAAM;AACpB,WAAA,MAAM,MAAM,IAAI;AAAA,SAClB,SAAS;AAAA,IAAA;AAAA,EAEhB;AAEA,YAAU,SAAS,MAAM;AACZ,eAAA;AACX,iBAAa,KAAK;AAAA,EACpB;AAEO,SAAA;AACT;AAmBO,SAAS,OAA8B,IAAO;AACnD,MAAI,SAAS;AAET,MAAA;AAEJ,SAAO,YAA4B,MAAqB;AACtD,QAAI,CAAC,QAAQ;AACF,eAAA;AACA,eAAA,GAAG,MAAM,MAAM,IAAI;AAAA,IAAA;AAGvB,WAAA;AAAA,EACT;AACF;;;;;"}
@@ -73,4 +73,4 @@ export {
73
73
  fnOnce,
74
74
  fnThrottle
75
75
  };
76
- //# sourceMappingURL=fn.mjs.map
76
+ //# sourceMappingURL=function.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function.mjs","sources":["../src/function.ts"],"sourcesContent":["import { isNumber } from './type';\nimport type { AnyFunction } from './types';\n\n/**\n * 一个空操作函数,不执行任何操作。\n *\n * @example\n * ```typescript\n * fnNoop(); // 不执行任何操作\n * ```\n */\nexport function fnNoop() {\n //\n}\n\n/**\n * 防抖函数的配置选项。\n */\nexport type DebounceOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n /**\n * 是否在等待开始时立即执行一次\n * @default false\n */\n leading?: boolean;\n};\n\n/**\n * 创建一个防抖函数,该函数会在指定的等待时间后执行,如果在等待时间内再次调用,则重新计时。\n *\n * @param fn - 需要防抖的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait` 和 `leading` 选项的对象。\n * @returns 返回一个防抖函数,该函数具有 `cancel` 方法,用于取消防抖操作。\n *\n * @example\n * ```typescript\n * const debouncedFn = fnDebounce(() => {\n * console.log('Debounced!');\n * }, 100);\n *\n * debouncedFn(); // 不会立即执行\n * debouncedFn(); // 重新计时\n * debouncedFn.cancel(); // 取消防抖操作\n * ```\n */\nexport function fnDebounce<F extends AnyFunction>(fn: F, wait: number | DebounceOptions) {\n const options: DebounceOptions = isNumber(wait) ? { wait } : wait;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let timer: any;\n let leading = false;\n\n const debounced = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n // 第一次执行\n if (options.leading && !leading) {\n leading = true;\n fn.apply(this, args);\n return;\n }\n\n // 最后一次执行\n clearTimeout(timer);\n timer = setTimeout(() => {\n if (canceled) return;\n\n fn.apply(this, args);\n }, options.wait);\n };\n\n debounced.cancel = () => {\n clearTimeout(timer);\n canceled = true;\n };\n\n return debounced;\n}\n\nexport type ThrottleOptions = {\n /**\n * 等待时间(毫秒)。\n */\n wait: number;\n\n /**\n * 是否在第一次调用时立即执行\n * @default false\n */\n leading?: boolean;\n\n /**\n * 是否在调用结束后等待一段时间再执行\n * @default false\n */\n trailing?: boolean;\n};\n\n/**\n * 创建一个节流函数,该函数会在指定的等待时间内最多执行一次,如果在等待时间内再次调用,则忽略后续调用。\n *\n * @param fn - 需要节流的函数。\n * @param wait - 等待时间(毫秒)或包含 `wait`、`leading` 和 `trailing` 选项的对象。\n * @returns 返回一个节流函数,该函数具有 `cancel` 方法,用于取消节流操作。\n *\n * @example\n * ```typescript\n * const throttledFn = fnThrottle(() => {\n * console.log('Throttled!');\n * }, 100);\n *\n * throttledFn(); // 立即执行\n * throttledFn(); // 忽略\n * throttledFn.cancel(); // 取消节流操作\n * ```\n */\nexport function fnThrottle<F extends AnyFunction>(fn: F, wait: number | ThrottleOptions) {\n const options = isNumber(wait) ? { wait } : wait;\n const waitFinal = options.wait;\n let lastTime = 0;\n let canceled = false;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let timer: any;\n\n const throttled = function (this: unknown, ...args: Parameters<F>) {\n if (canceled) return;\n\n const now = Date.now();\n\n // 第一次执行\n if (options.leading && lastTime === 0) {\n lastTime = now;\n fn.apply(this, args);\n }\n\n // 中间控频执行\n else if (lastTime > 0 && now - lastTime >= waitFinal) {\n lastTime = now;\n fn.apply(this, args);\n }\n\n // 首次计时\n else if (lastTime === 0) {\n lastTime = now;\n }\n\n // 最后一次执行\n if (options.trailing) {\n clearTimeout(timer);\n timer = setTimeout(() => {\n fn.apply(this, args);\n }, waitFinal);\n }\n };\n\n throttled.cancel = () => {\n canceled = true;\n clearTimeout(timer);\n };\n\n return throttled;\n}\n\n/**\n * 创建一个只执行一次的函数,无论调用多少次,实际执行的函数体也只会执行一次。\n *\n * @param fn - 需要只执行一次的函数。\n * @returns 返回一个只执行一次的函数,该函数在第一次调用后会缓存结果并返回缓存的结果。\n *\n * @example\n * ```typescript\n * const onceFn = fnOnce(() => {\n * console.log('This will be logged only once.');\n * return 42;\n * });\n *\n * console.log(onceFn()); // 输出: This will be logged only once. 42\n * console.log(onceFn()); // 输出: 42\n * ```\n */\nexport function fnOnce<F extends AnyFunction>(fn: F) {\n let called = false;\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let result: any;\n\n return function (this: unknown, ...args: Parameters<F>) {\n if (!called) {\n called = true;\n result = fn.apply(this, args);\n }\n\n return result;\n };\n}\n"],"names":[],"mappings":";AAWO,SAAS,SAAS;AAEzB;AAmCgB,SAAA,WAAkC,IAAO,MAAgC;AACvF,QAAM,UAA2B,SAAS,IAAI,IAAI,EAAE,KAAS,IAAA;AAC7D,MAAI,WAAW;AAEX,MAAA;AACJ,MAAI,UAAU;AAER,QAAA,YAAY,YAA4B,MAAqB;AACjE,QAAI,SAAU;AAGV,QAAA,QAAQ,WAAW,CAAC,SAAS;AACrB,gBAAA;AACP,SAAA,MAAM,MAAM,IAAI;AACnB;AAAA,IAAA;AAIF,iBAAa,KAAK;AAClB,YAAQ,WAAW,MAAM;AACvB,UAAI,SAAU;AAEX,SAAA,MAAM,MAAM,IAAI;AAAA,IAAA,GAClB,QAAQ,IAAI;AAAA,EACjB;AAEA,YAAU,SAAS,MAAM;AACvB,iBAAa,KAAK;AACP,eAAA;AAAA,EACb;AAEO,SAAA;AACT;AAuCgB,SAAA,WAAkC,IAAO,MAAgC;AACvF,QAAM,UAAU,SAAS,IAAI,IAAI,EAAE,KAAS,IAAA;AAC5C,QAAM,YAAY,QAAQ;AAC1B,MAAI,WAAW;AACf,MAAI,WAAW;AAEX,MAAA;AAEE,QAAA,YAAY,YAA4B,MAAqB;AACjE,QAAI,SAAU;AAER,UAAA,MAAM,KAAK,IAAI;AAGjB,QAAA,QAAQ,WAAW,aAAa,GAAG;AAC1B,iBAAA;AACR,SAAA,MAAM,MAAM,IAAI;AAAA,IAIZ,WAAA,WAAW,KAAK,MAAM,YAAY,WAAW;AACzC,iBAAA;AACR,SAAA,MAAM,MAAM,IAAI;AAAA,IAAA,WAIZ,aAAa,GAAG;AACZ,iBAAA;AAAA,IAAA;AAIb,QAAI,QAAQ,UAAU;AACpB,mBAAa,KAAK;AAClB,cAAQ,WAAW,MAAM;AACpB,WAAA,MAAM,MAAM,IAAI;AAAA,SAClB,SAAS;AAAA,IAAA;AAAA,EAEhB;AAEA,YAAU,SAAS,MAAM;AACZ,eAAA;AACX,iBAAa,KAAK;AAAA,EACpB;AAEO,SAAA;AACT;AAmBO,SAAS,OAA8B,IAAO;AACnD,MAAI,SAAS;AAET,MAAA;AAEJ,SAAO,YAA4B,MAAqB;AACtD,QAAI,CAAC,QAAQ;AACF,eAAA;AACA,eAAA,GAAG,MAAM,MAAM,IAAI;AAAA,IAAA;AAGvB,WAAA;AAAA,EACT;AACF;"}
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const VERSION = "1.5.0";
3
+ const VERSION = "1.7.0";
4
4
  exports.VERSION = VERSION;
5
5
  //# sourceMappingURL=index.cjs.map
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- const VERSION = "1.5.0";
1
+ const VERSION = "1.7.0";
2
2
  export {
3
3
  VERSION
4
4
  };
@@ -44,3 +44,18 @@ export declare function objectOmit<T extends AnyObject, K extends keyof T>(objec
44
44
  * ```
45
45
  */
46
46
  export declare function objectMap<T extends AnyObject, V>(object: T, mapper: (value: T[keyof T], key: keyof T) => V): Record<keyof T, V>;
47
+ /**
48
+ * 根据提供的过滤函数过滤对象的属性,返回一个新对象,只包含满足条件的属性。
49
+ *
50
+ * @param object - 要过滤的对象。
51
+ * @param predicate - 过滤函数,接收值和键作为参数,返回布尔值。
52
+ * @returns 包含满足过滤条件的属性的新对象。
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const obj = { a: 1, b: 2, c: 3, d: 4 };
57
+ * const result = objectFilter(obj, (value, key) => value > 2);
58
+ * console.log(result); // { c: 3, d: 4 }
59
+ * ```
60
+ */
61
+ export declare function objectFilter<T extends AnyObject>(object: T, predicate: (value: T[keyof T], key: keyof T) => boolean): Partial<T>;
package/dist/object.cjs CHANGED
@@ -116,12 +116,18 @@ function objectMap(object, mapper) {
116
116
  ])
117
117
  );
118
118
  }
119
+ function objectFilter(object, predicate) {
120
+ return Object.fromEntries(
121
+ Object.entries(object).filter(([key, value]) => predicate(value, key))
122
+ );
123
+ }
119
124
  exports.objectEach = each.objectEach;
120
125
  exports.objectEachAsync = each.objectEachAsync;
121
126
  exports.objectDefaults = merge.objectDefaults;
122
127
  exports.objectMerge = merge.objectMerge;
123
128
  exports.isEmptyObject = isEmptyObject;
124
129
  exports.isPlainObject = isPlainObject;
130
+ exports.objectFilter = objectFilter;
125
131
  exports.objectGet = objectGet;
126
132
  exports.objectMap = objectMap;
127
133
  exports.objectOmit = objectOmit;
@@ -1 +1 @@
1
- {"version":3,"file":"object.cjs","sources":["../src/object/get-set.ts","../src/object/is.ts","../src/object/process.ts"],"sourcesContent":["import { isArray, isObject, isUndefined } from '@/type';\nimport type { AnyArray, AnyObject } from '@/types';\n\n// @ref https://stackoverflow.com/a/67609485\n\ntype Idx<T, K> = K extends keyof T\n ? T[K]\n : number extends keyof T\n ? K extends `${number}`\n ? T[number]\n : never\n : never;\n\ntype Join<K, P> = K extends string | number\n ? P extends string | number\n ? `${K}${'' extends P ? '' : '.'}${P}`\n : never\n : never;\n\ntype Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]];\n\nexport type ObjectPath<O, D extends number = 4> = [D] extends [never]\n ? never\n : O extends object\n ? {\n [K in keyof O]-?: K extends string | number ? `${K}` | Join<K, ObjectPath<O[K], Prev[D]>> : never;\n }[keyof O]\n : '';\n\nexport type ObjectLeafPath<O, D extends number = 4> = [D] extends [never]\n ? never\n : O extends object\n ? {\n [K in keyof O]-?: K extends string | number\n ? O[K] extends string | number\n ? `${K}` | Join<K, ObjectLeafPath<O[K], Prev[D]>>\n : Join<K, ObjectLeafPath<O[K], Prev[D]>>\n : never;\n }[keyof O]\n : '';\n\nexport type ObjectPathValue<O, P extends ObjectPath<O, 4>> = P extends `${infer Key}.${infer Rest}`\n ? Rest extends ObjectPath<Idx<O, Key>, 4>\n ? ObjectPathValue<Idx<O, Key>, Rest>\n : never\n : Idx<O, P>;\n\nfunction pathToKeys(path: string | string[]) {\n // 下文用到该数组时会进行修改操作,因此复制一份\n if (isArray(path)) return [...path];\n\n let pathFinal = path.replace(/\\[(\\w+)\\]/g, '.$1');\n pathFinal = pathFinal.replace(/^\\./, '');\n return pathFinal.split('.');\n}\n\nfunction isObjectOrArray(v: unknown) {\n return isObject(v) || isArray(v);\n}\n\n/**\n * 表示对象节点的信息。\n *\n * @template V - 键值的类型。\n */\nexport type ObjectNode<V = unknown | undefined> = {\n /**\n * 当前节点的父级对象。\n */\n parent: unknown | undefined;\n\n /**\n * 当前节点的键名路径。\n */\n keys: string[];\n\n /**\n * 当前节点的键名。\n */\n key: string | undefined;\n\n /**\n * 当前节点的键值。\n */\n value: V;\n};\n\n/**\n * 根据属性路径获取属性值\n * @param {O} obj\n * @param {string | string[] | P} path\n * @returns {ObjectNode<O>}\n * 根据属性路径获取属性值。\n *\n * @template O - 目标对象的类型。\n * @template P - 属性路径的类型。\n * @param {O} obj - 要操作的目标对象。\n * @param {P | string | string[]} path - 属性路径,可以是字符串或字符串数组。支持点分隔符(如 \"a.b.c\")或数组形式(如 [\"a\", \"b\", \"c\"])。\n * @returns {ObjectNode<O>} 返回一个包含父级、键名路径、键名和键值的对象节点。\n *\n * @example\n * ```typescript\n * const obj = { a: { b: { c: 42 } } };\n * const result = objectGet(obj, 'a.b.c');\n * console.log(result.value); // 输出 42\n * ```\n */\nexport function objectGet<O extends AnyObject, P extends ObjectPath<O>>(\n obj: O,\n path: P | string | string[],\n): ObjectNode<O> {\n const keys = pathToKeys(path);\n const lastKey = keys.pop();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let parent: any = obj;\n const keysFinal: string[] = [];\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n\n keysFinal.push(key);\n if (!isObjectOrArray(parent)) break;\n\n // @ts-ignore\n parent = parent[key];\n }\n\n return {\n parent: parent,\n keys: keysFinal,\n key: lastKey,\n // @ts-ignore\n value: isObjectOrArray(parent) && lastKey ? parent[lastKey] : undefined,\n };\n}\n\n// /**\n// * 根据路径获取对象叶子节点值\n// * @param {O} obj\n// * @param {P} path\n// * @returns {ObjectNode<O>}\n// */\n// export function objectLeaf<O extends AnyObject, P extends ObjectLeafPath<O>>(obj: O, path: P) {\n// return objectGet(obj, path);\n// }\n\n/**\n * 配置选项,用于控制 `objectSet` 的行为。\n *\n * @template O - 目标对象的类型。\n */\nexport type ObjectSetOptions<O extends AnyObject> = {\n /**\n * 在设置值之前调用的钩子函数。\n * 如果返回 `false`,则阻止设置值。\n *\n * @param {ObjectNode<O> & { key: string }} node - 当前节点信息。\n * @returns {boolean | undefined | void} 返回 `false` 时阻止设置值。\n */\n // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>\n beforeSet(node: ObjectNode<O> & { key: string }): boolean | undefined | void;\n\n /**\n * 当遇到未定义的中间节点时调用的钩子函数。\n * 返回值将用于创建中间节点。\n *\n * @param {ObjectNode<O>} node - 当前节点信息。\n * @returns {AnyObject | AnyArray | undefined | void} 返回值将用于创建中间节点。\n */\n // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>\n undefinedSet(node: ObjectNode<O>): AnyObject | AnyArray | undefined | void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\n// biome-ignore lint/suspicious/noExplicitAny: <explanation>\nconst defaultObjectSetOptions: ObjectSetOptions<any> = {\n beforeSet: () => true,\n undefinedSet: () => ({}),\n};\n\n/**\n * 根据属性路径设置属性值\n * @param {AnyObject} obj\n * @param {string} path\n * @param {V} val\n * @param {Partial<ObjectSetOptions<O>>} options\n * @returns {ObjectNode<O, V>}\n * 根据属性路径设置属性值。\n *\n * @template O - 目标对象的类型。\n * @template V - 要设置的值的类型。\n * @param {O} obj - 要操作的目标对象。\n * @param {string | string[]} path - 属性路径,可以是字符串或字符串数组。支持点分隔符(如 \"a.b.c\")或数组形式(如 [\"a\", \"b\", \"c\"])。\n * @param {V} val - 要设置的值。\n * @param {Partial<ObjectSetOptions<O>>} [options] - 可选配置项,用于控制设置行为。\n * @returns {ObjectNode<V>} 返回一个包含父级、键名路径、键名和键值的对象节点。\n *\n * @example\n * ```typescript\n * const obj = {};\n * objectSet(obj, 'a.b.c', 42);\n * console.log(obj); // 输出 { a: { b: { c: 42 } } }\n *\n * objectSet(obj, 'a.b.c', 100, {\n * beforeSet: (node) => node.key === 'c',\n * });\n * console.log(obj); // 输出 { a: { b: { c: 100 } } }\n * ```\n */\nexport function objectSet<O extends AnyObject, V>(\n obj: O,\n path: string | string[],\n val: V,\n options?: Partial<ObjectSetOptions<O>>,\n): ObjectNode<V> {\n const { beforeSet, undefinedSet } = Object.assign({}, defaultObjectSetOptions, options);\n const keys = pathToKeys(path);\n const lastKey = keys.pop();\n let parent = obj;\n let stopped = false;\n const keysFinal: string[] = [];\n\n for (const key of keys) {\n let val = parent[key];\n keysFinal.push(key);\n\n if (isUndefined(val)) {\n const seted = undefinedSet({\n parent: parent,\n keys: keysFinal,\n key: key,\n value: val,\n });\n\n if (!seted) {\n stopped = true;\n break;\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n val = parent[key] = seted;\n }\n\n // @ts-ignore\n parent = val;\n }\n\n if (!stopped && !isUndefined(lastKey)) {\n keysFinal.push(lastKey);\n\n if (\n beforeSet({\n parent: parent,\n keys: keysFinal,\n key: lastKey,\n value: parent[lastKey],\n })\n ) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n parent[lastKey] = val;\n }\n }\n\n return {\n keys: keysFinal,\n parent: parent,\n key: lastKey,\n value: val,\n };\n}\n","import { isArray, isObject, isString, typeIs } from '@/type';\nimport type { AnyArray, AnyFunction, AnyObject } from '@/types';\n\n/**\n * 检查一个对象是否为空对象(不包含任何自有属性,包括符号属性)。\n *\n * @param obj - 要检查的对象\n * @returns 如果对象没有自有属性(包括符号属性)则返回 true,否则返回 false\n *\n * @example\n * ```typescript\n * isEmptyObject({}); // true\n * isEmptyObject({ a: 1 }); // false\n * isEmptyObject(Object.create(null)); // true\n * isEmptyObject({ [Symbol('key')]: 'value' }); // false\n * ```\n */\nexport function isEmptyObject(obj: AnyObject): boolean {\n return Object.getOwnPropertyNames(obj).length === 0 && Object.getOwnPropertySymbols(obj).length === 0;\n}\n\n/**\n * 检查一个对象是否为纯对象(通过对象字面量或Object构造函数创建,而非其他构造函数的实例)。\n *\n * @param obj - 要检查的对象\n * @returns 如果是纯对象则返回 true,否则返回 false\n *\n * @example\n * ```typescript\n * isPlainObject({}); // true\n * isPlainObject(Object.create(null)); // true\n * isPlainObject(new Date()); // false\n * isPlainObject([]); // false\n * isPlainObject(() => {}); // false\n * ```\n */\nexport function isPlainObject(obj: AnyObject): boolean {\n const proto: unknown = Object.getPrototypeOf(obj);\n\n // 对象无原型\n if (!proto) return true;\n\n // 是否对象直接实例\n return proto === Object.prototype;\n}\n\n// 移除,原因是,定义对象尽可能的使用 type 关键字即可避开此问题\n// /**\n// * 精确对象,常用于联合类型判断\n// * 相关 bug:https://l.ydr.me/Zp88vFKc\n// */\n// // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n// export type ExactObject<T = any> = T extends AnyFunction\n// ? never\n// : T extends AnyArray\n// ? never\n// : T extends object\n// ? T\n// : never;\n//\n// /**\n// * 检查值是否为精确接口对象\n// * @param object - 传入对象,必须是一个对象与其他类型的联合\n// * @returns 如果值为对象则返回 true,否则返回 false\n// * @example\n// * ```typescript\n// * type Id = string | string[] | (() => string);\n// *\n// * interface Cache {\n// * id?: Id;\n// * }\n// *\n// * type Share = {\n// * id?: Id;\n// * }\n// *\n// * interface Options {\n// * cache?: Id | Cache;\n// * share?: Id | Share;\n// * }\n// *\n// * function test(options: Options) {\n// * // string | string[] | (() => string) | Cache | undefined\n// * const cache = options.cache;\n// *\n// * // Cache\n// * // 需要使用\n// * if (isExactObject(cache)) {\n// * cache.id;\n// * }\n// * // string[]\n// * else if (isArray(cache)) {\n// * cache.push();\n// * }\n// * // string\n// * else if (isString(cache)) {\n// * cache.charCodeAt(0);\n// * }\n// * // (() => string) | undefined\n// * else {\n// * cache?.();\n// * }\n// *\n// * // string | string[] | (() => string) | Share | undefined\n// * const share = options.share;\n// *\n// * // Share\n// * if (isObject(share)) {\n// * share.id;\n// * }\n// * // string[]\n// * else if (isArray(share)) {\n// * share.push();\n// * }\n// * // string\n// * else if (isString(share)) {\n// * share.charCodeAt(0);\n// * }\n// * // (() => string) | undefined\n// * else {\n// * share?.();\n// * }\n// * }\n// * ```\n// */\n// export function isExactObject<T>(object: T): object is ExactObject<T> {\n// return typeIs(object) === 'object';\n// }\n","import type { AnyObject } from '@/types';\n\n/**\n * 从对象中选择指定键的属性,返回一个新的对象。\n *\n * @param object - 要从中选择属性的对象。\n * @param keys - 要选择的键数组。\n * @returns 包含指定键属性的新对象。\n *\n * @example\n * ```typescript\n * const obj = { a: 1, b: 2, c: 3 };\n * const result = objectPick(obj, ['a', 'c']);\n * console.log(result); // { a: 1, c: 3 }\n * ```\n */\nexport function objectPick<T extends AnyObject, K extends keyof T>(object: T, keys: K[]): Pick<T, K> {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in object) {\n result[key] = object[key];\n }\n }\n return result;\n}\n\n/**\n * 从对象中排除指定键的属性,返回一个新的对象。\n *\n * @param object - 要从中排除属性的对象。\n * @param keys - 要排除的键数组。\n * @returns 排除指定键属性后的新对象。\n *\n * @example\n * ```typescript\n * const obj = { a: 1, b: 2, c: 3, d: 4 };\n * const result = objectOmit(obj, ['a', 'd']);\n * console.log(result); // { b: 2, c: 3 }\n * ```\n */\nexport function objectOmit<T extends AnyObject, K extends keyof T>(object: T, keys: K[]): Omit<T, K> {\n const result = {} as Omit<T, K>;\n for (const key in object) {\n if (!keys.includes(key as unknown as K)) {\n // @ts-expect-error\n result[key] = object[key];\n }\n }\n return result;\n}\n\n/**\n * 遍历对象的每个键值对,并对每个键值对执行提供的映射函数,返回一个新的对象。\n *\n * @param object - 要遍历的对象。\n * @param mapper - 对每个键值对执行的映射函数。\n * @returns 返回一个新的对象,其中每个值都是通过映射函数处理后的结果。\n *\n * @example\n * ```typescript\n * const obj = { a: 1, b: 2, c: 3 };\n * const result = objectMap(obj, (val, key) => String(val * 2));\n * console.log(result); // { a: '2', b: '4', c: '6' }\n * ```\n */\nexport function objectMap<T extends AnyObject, V>(\n object: T,\n mapper: (value: T[keyof T], key: keyof T) => V,\n): Record<keyof T, V> {\n return Object.fromEntries(\n Object.entries(object).map(([key, value]) => [\n key,\n mapper(\n // @ts-expect-error\n value,\n key as keyof T,\n ),\n ]),\n ) as Record<keyof T, V>;\n}\n"],"names":["isArray","isObject","val","isUndefined"],"mappings":"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"}
1
+ {"version":3,"file":"object.cjs","sources":["../src/object/get-set.ts","../src/object/is.ts","../src/object/process.ts"],"sourcesContent":["import { isArray, isObject, isUndefined } from '@/type';\nimport type { AnyArray, AnyObject } from '@/types';\n\n// @ref https://stackoverflow.com/a/67609485\n\ntype Idx<T, K> = K extends keyof T\n ? T[K]\n : number extends keyof T\n ? K extends `${number}`\n ? T[number]\n : never\n : never;\n\ntype Join<K, P> = K extends string | number\n ? P extends string | number\n ? `${K}${'' extends P ? '' : '.'}${P}`\n : never\n : never;\n\ntype Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]];\n\nexport type ObjectPath<O, D extends number = 4> = [D] extends [never]\n ? never\n : O extends object\n ? {\n [K in keyof O]-?: K extends string | number ? `${K}` | Join<K, ObjectPath<O[K], Prev[D]>> : never;\n }[keyof O]\n : '';\n\nexport type ObjectLeafPath<O, D extends number = 4> = [D] extends [never]\n ? never\n : O extends object\n ? {\n [K in keyof O]-?: K extends string | number\n ? O[K] extends string | number\n ? `${K}` | Join<K, ObjectLeafPath<O[K], Prev[D]>>\n : Join<K, ObjectLeafPath<O[K], Prev[D]>>\n : never;\n }[keyof O]\n : '';\n\nexport type ObjectPathValue<O, P extends ObjectPath<O, 4>> = P extends `${infer Key}.${infer Rest}`\n ? Rest extends ObjectPath<Idx<O, Key>, 4>\n ? ObjectPathValue<Idx<O, Key>, Rest>\n : never\n : Idx<O, P>;\n\nfunction pathToKeys(path: string | string[]) {\n // 下文用到该数组时会进行修改操作,因此复制一份\n if (isArray(path)) return [...path];\n\n let pathFinal = path.replace(/\\[(\\w+)\\]/g, '.$1');\n pathFinal = pathFinal.replace(/^\\./, '');\n return pathFinal.split('.');\n}\n\nfunction isObjectOrArray(v: unknown) {\n return isObject(v) || isArray(v);\n}\n\n/**\n * 表示对象节点的信息。\n *\n * @template V - 键值的类型。\n */\nexport type ObjectNode<V = unknown | undefined> = {\n /**\n * 当前节点的父级对象。\n */\n parent: unknown | undefined;\n\n /**\n * 当前节点的键名路径。\n */\n keys: string[];\n\n /**\n * 当前节点的键名。\n */\n key: string | undefined;\n\n /**\n * 当前节点的键值。\n */\n value: V;\n};\n\n/**\n * 根据属性路径获取属性值\n * @param {O} obj\n * @param {string | string[] | P} path\n * @returns {ObjectNode<O>}\n * 根据属性路径获取属性值。\n *\n * @template O - 目标对象的类型。\n * @template P - 属性路径的类型。\n * @param {O} obj - 要操作的目标对象。\n * @param {P | string | string[]} path - 属性路径,可以是字符串或字符串数组。支持点分隔符(如 \"a.b.c\")或数组形式(如 [\"a\", \"b\", \"c\"])。\n * @returns {ObjectNode<O>} 返回一个包含父级、键名路径、键名和键值的对象节点。\n *\n * @example\n * ```typescript\n * const obj = { a: { b: { c: 42 } } };\n * const result = objectGet(obj, 'a.b.c');\n * console.log(result.value); // 输出 42\n * ```\n */\nexport function objectGet<O extends AnyObject, P extends ObjectPath<O>>(\n obj: O,\n path: P | string | string[],\n): ObjectNode<O> {\n const keys = pathToKeys(path);\n const lastKey = keys.pop();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n let parent: any = obj;\n const keysFinal: string[] = [];\n\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n\n keysFinal.push(key);\n if (!isObjectOrArray(parent)) break;\n\n // @ts-ignore\n parent = parent[key];\n }\n\n return {\n parent: parent,\n keys: keysFinal,\n key: lastKey,\n // @ts-ignore\n value: isObjectOrArray(parent) && lastKey ? parent[lastKey] : undefined,\n };\n}\n\n// /**\n// * 根据路径获取对象叶子节点值\n// * @param {O} obj\n// * @param {P} path\n// * @returns {ObjectNode<O>}\n// */\n// export function objectLeaf<O extends AnyObject, P extends ObjectLeafPath<O>>(obj: O, path: P) {\n// return objectGet(obj, path);\n// }\n\n/**\n * 配置选项,用于控制 `objectSet` 的行为。\n *\n * @template O - 目标对象的类型。\n */\nexport type ObjectSetOptions<O extends AnyObject> = {\n /**\n * 在设置值之前调用的钩子函数。\n * 如果返回 `false`,则阻止设置值。\n *\n * @param {ObjectNode<O> & { key: string }} node - 当前节点信息。\n * @returns {boolean | undefined | void} 返回 `false` 时阻止设置值。\n */\n // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>\n beforeSet(node: ObjectNode<O> & { key: string }): boolean | undefined | void;\n\n /**\n * 当遇到未定义的中间节点时调用的钩子函数。\n * 返回值将用于创建中间节点。\n *\n * @param {ObjectNode<O>} node - 当前节点信息。\n * @returns {AnyObject | AnyArray | undefined | void} 返回值将用于创建中间节点。\n */\n // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>\n undefinedSet(node: ObjectNode<O>): AnyObject | AnyArray | undefined | void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\n// biome-ignore lint/suspicious/noExplicitAny: <explanation>\nconst defaultObjectSetOptions: ObjectSetOptions<any> = {\n beforeSet: () => true,\n undefinedSet: () => ({}),\n};\n\n/**\n * 根据属性路径设置属性值\n * @param {AnyObject} obj\n * @param {string} path\n * @param {V} val\n * @param {Partial<ObjectSetOptions<O>>} options\n * @returns {ObjectNode<O, V>}\n * 根据属性路径设置属性值。\n *\n * @template O - 目标对象的类型。\n * @template V - 要设置的值的类型。\n * @param {O} obj - 要操作的目标对象。\n * @param {string | string[]} path - 属性路径,可以是字符串或字符串数组。支持点分隔符(如 \"a.b.c\")或数组形式(如 [\"a\", \"b\", \"c\"])。\n * @param {V} val - 要设置的值。\n * @param {Partial<ObjectSetOptions<O>>} [options] - 可选配置项,用于控制设置行为。\n * @returns {ObjectNode<V>} 返回一个包含父级、键名路径、键名和键值的对象节点。\n *\n * @example\n * ```typescript\n * const obj = {};\n * objectSet(obj, 'a.b.c', 42);\n * console.log(obj); // 输出 { a: { b: { c: 42 } } }\n *\n * objectSet(obj, 'a.b.c', 100, {\n * beforeSet: (node) => node.key === 'c',\n * });\n * console.log(obj); // 输出 { a: { b: { c: 100 } } }\n * ```\n */\nexport function objectSet<O extends AnyObject, V>(\n obj: O,\n path: string | string[],\n val: V,\n options?: Partial<ObjectSetOptions<O>>,\n): ObjectNode<V> {\n const { beforeSet, undefinedSet } = Object.assign({}, defaultObjectSetOptions, options);\n const keys = pathToKeys(path);\n const lastKey = keys.pop();\n let parent = obj;\n let stopped = false;\n const keysFinal: string[] = [];\n\n for (const key of keys) {\n let val = parent[key];\n keysFinal.push(key);\n\n if (isUndefined(val)) {\n const seted = undefinedSet({\n parent: parent,\n keys: keysFinal,\n key: key,\n value: val,\n });\n\n if (!seted) {\n stopped = true;\n break;\n }\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n val = parent[key] = seted;\n }\n\n // @ts-ignore\n parent = val;\n }\n\n if (!stopped && !isUndefined(lastKey)) {\n keysFinal.push(lastKey);\n\n if (\n beforeSet({\n parent: parent,\n keys: keysFinal,\n key: lastKey,\n value: parent[lastKey],\n })\n ) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n parent[lastKey] = val;\n }\n }\n\n return {\n keys: keysFinal,\n parent: parent,\n key: lastKey,\n value: val,\n };\n}\n","import { isArray, isObject, isString, typeIs } from '@/type';\nimport type { AnyArray, AnyFunction, AnyObject } from '@/types';\n\n/**\n * 检查一个对象是否为空对象(不包含任何自有属性,包括符号属性)。\n *\n * @param obj - 要检查的对象\n * @returns 如果对象没有自有属性(包括符号属性)则返回 true,否则返回 false\n *\n * @example\n * ```typescript\n * isEmptyObject({}); // true\n * isEmptyObject({ a: 1 }); // false\n * isEmptyObject(Object.create(null)); // true\n * isEmptyObject({ [Symbol('key')]: 'value' }); // false\n * ```\n */\nexport function isEmptyObject(obj: AnyObject): boolean {\n return Object.getOwnPropertyNames(obj).length === 0 && Object.getOwnPropertySymbols(obj).length === 0;\n}\n\n/**\n * 检查一个对象是否为纯对象(通过对象字面量或Object构造函数创建,而非其他构造函数的实例)。\n *\n * @param obj - 要检查的对象\n * @returns 如果是纯对象则返回 true,否则返回 false\n *\n * @example\n * ```typescript\n * isPlainObject({}); // true\n * isPlainObject(Object.create(null)); // true\n * isPlainObject(new Date()); // false\n * isPlainObject([]); // false\n * isPlainObject(() => {}); // false\n * ```\n */\nexport function isPlainObject(obj: AnyObject): boolean {\n const proto: unknown = Object.getPrototypeOf(obj);\n\n // 对象无原型\n if (!proto) return true;\n\n // 是否对象直接实例\n return proto === Object.prototype;\n}\n\n// 移除,原因是,定义对象尽可能的使用 type 关键字即可避开此问题\n// /**\n// * 精确对象,常用于联合类型判断\n// * 相关 bug:https://l.ydr.me/Zp88vFKc\n// */\n// // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n// export type ExactObject<T = any> = T extends AnyFunction\n// ? never\n// : T extends AnyArray\n// ? never\n// : T extends object\n// ? T\n// : never;\n//\n// /**\n// * 检查值是否为精确接口对象\n// * @param object - 传入对象,必须是一个对象与其他类型的联合\n// * @returns 如果值为对象则返回 true,否则返回 false\n// * @example\n// * ```typescript\n// * type Id = string | string[] | (() => string);\n// *\n// * interface Cache {\n// * id?: Id;\n// * }\n// *\n// * type Share = {\n// * id?: Id;\n// * }\n// *\n// * interface Options {\n// * cache?: Id | Cache;\n// * share?: Id | Share;\n// * }\n// *\n// * function test(options: Options) {\n// * // string | string[] | (() => string) | Cache | undefined\n// * const cache = options.cache;\n// *\n// * // Cache\n// * // 需要使用\n// * if (isExactObject(cache)) {\n// * cache.id;\n// * }\n// * // string[]\n// * else if (isArray(cache)) {\n// * cache.push();\n// * }\n// * // string\n// * else if (isString(cache)) {\n// * cache.charCodeAt(0);\n// * }\n// * // (() => string) | undefined\n// * else {\n// * cache?.();\n// * }\n// *\n// * // string | string[] | (() => string) | Share | undefined\n// * const share = options.share;\n// *\n// * // Share\n// * if (isObject(share)) {\n// * share.id;\n// * }\n// * // string[]\n// * else if (isArray(share)) {\n// * share.push();\n// * }\n// * // string\n// * else if (isString(share)) {\n// * share.charCodeAt(0);\n// * }\n// * // (() => string) | undefined\n// * else {\n// * share?.();\n// * }\n// * }\n// * ```\n// */\n// export function isExactObject<T>(object: T): object is ExactObject<T> {\n// return typeIs(object) === 'object';\n// }\n","import type { AnyObject } from '@/types';\n\n/**\n * 从对象中选择指定键的属性,返回一个新的对象。\n *\n * @param object - 要从中选择属性的对象。\n * @param keys - 要选择的键数组。\n * @returns 包含指定键属性的新对象。\n *\n * @example\n * ```typescript\n * const obj = { a: 1, b: 2, c: 3 };\n * const result = objectPick(obj, ['a', 'c']);\n * console.log(result); // { a: 1, c: 3 }\n * ```\n */\nexport function objectPick<T extends AnyObject, K extends keyof T>(object: T, keys: K[]): Pick<T, K> {\n const result = {} as Pick<T, K>;\n for (const key of keys) {\n if (key in object) {\n result[key] = object[key];\n }\n }\n return result;\n}\n\n/**\n * 从对象中排除指定键的属性,返回一个新的对象。\n *\n * @param object - 要从中排除属性的对象。\n * @param keys - 要排除的键数组。\n * @returns 排除指定键属性后的新对象。\n *\n * @example\n * ```typescript\n * const obj = { a: 1, b: 2, c: 3, d: 4 };\n * const result = objectOmit(obj, ['a', 'd']);\n * console.log(result); // { b: 2, c: 3 }\n * ```\n */\nexport function objectOmit<T extends AnyObject, K extends keyof T>(object: T, keys: K[]): Omit<T, K> {\n const result = {} as Omit<T, K>;\n for (const key in object) {\n if (!keys.includes(key as unknown as K)) {\n // @ts-expect-error\n result[key] = object[key];\n }\n }\n return result;\n}\n\n/**\n * 遍历对象的每个键值对,并对每个键值对执行提供的映射函数,返回一个新的对象。\n *\n * @param object - 要遍历的对象。\n * @param mapper - 对每个键值对执行的映射函数。\n * @returns 返回一个新的对象,其中每个值都是通过映射函数处理后的结果。\n *\n * @example\n * ```typescript\n * const obj = { a: 1, b: 2, c: 3 };\n * const result = objectMap(obj, (val, key) => String(val * 2));\n * console.log(result); // { a: '2', b: '4', c: '6' }\n * ```\n */\nexport function objectMap<T extends AnyObject, V>(\n object: T,\n mapper: (value: T[keyof T], key: keyof T) => V,\n): Record<keyof T, V> {\n return Object.fromEntries(\n Object.entries(object).map(([key, value]) => [\n key,\n mapper(\n // @ts-expect-error\n value,\n key as keyof T,\n ),\n ]),\n ) as Record<keyof T, V>;\n}\n\n/**\n * 根据提供的过滤函数过滤对象的属性,返回一个新对象,只包含满足条件的属性。\n *\n * @param object - 要过滤的对象。\n * @param predicate - 过滤函数,接收值和键作为参数,返回布尔值。\n * @returns 包含满足过滤条件的属性的新对象。\n *\n * @example\n * ```typescript\n * const obj = { a: 1, b: 2, c: 3, d: 4 };\n * const result = objectFilter(obj, (value, key) => value > 2);\n * console.log(result); // { c: 3, d: 4 }\n * ```\n */\nexport function objectFilter<T extends AnyObject>(\n object: T,\n predicate: (value: T[keyof T], key: keyof T) => boolean,\n): Partial<T> {\n return Object.fromEntries(\n Object.entries(object).filter(([key, value]) => predicate(value as T[keyof T], key as keyof T)),\n ) as Partial<T>;\n}\n"],"names":["isArray","isObject","val","isUndefined"],"mappings":"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"}
package/dist/object.mjs CHANGED
@@ -114,12 +114,18 @@ function objectMap(object, mapper) {
114
114
  ])
115
115
  );
116
116
  }
117
+ function objectFilter(object, predicate) {
118
+ return Object.fromEntries(
119
+ Object.entries(object).filter(([key, value]) => predicate(value, key))
120
+ );
121
+ }
117
122
  export {
118
123
  isEmptyObject,
119
124
  isPlainObject,
120
125
  o2 as objectDefaults,
121
126
  o as objectEach,
122
127
  a as objectEachAsync,
128
+ objectFilter,
123
129
  objectGet,
124
130
  objectMap,
125
131
  a2 as objectMerge,