foreslash 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,111 @@
1
+ # Change Log
2
+
3
+ ## Version 0.2.3 - 2025-03-28
4
+
5
+ Unstable version
6
+
7
+ - Feat 🥥 Functions added: `defer`
8
+ - Fix 🥕 Bug fixed: `debounce` doesn't apply the last callee's arguments
9
+ - Other fixes and improvements
10
+
11
+ 不稳定版本
12
+
13
+ - 功能 🥥 添加函数: `defer`
14
+ - 修复 🥕 缺陷修复: `debounce` 没有传入最后一次获取的参数
15
+ - 其他修复与优化
16
+
17
+ ## Version 0.2.2 - 2025-03-18
18
+
19
+ Unstable version
20
+
21
+ - Feat 🥥 Functions added: `throttle` `debounce` `remove` and `clamp`
22
+ - Other fixes and improvements
23
+
24
+ 不稳定版本
25
+
26
+ - 功能 🥥 添加函数: `throttle` `debounce` `remove` 和 `clamp`
27
+ - 其他修复与优化
28
+
29
+ ## Version 0.2.1 - 2025-02-23
30
+
31
+ Unstable version
32
+
33
+ - Feat 🥥 Functions added: `memo`
34
+ - Feat 🥥 Function change: `range` now support single parameter invocation, *compatible with Radash*
35
+ - Fix 🥕 Bug fixed: `deepClone` now processes `DataView.buffer` properly
36
+ - Fix 🥕 Bug fixed: Correct type definition filepath
37
+ - Other fixes and improvements
38
+
39
+ 不稳定版本
40
+
41
+ - 功能 🥥 添加函数: `memo`
42
+ - 功能 🥥 变更函数: `range` 现在支持单个参数的调用, *与 Radash 兼容*
43
+ - 修复 🥕 缺陷修复: `deepClone` 现在能正确处理 `DataView.buffer`
44
+ - 修复 🥕 缺陷修复: 修复了类型定义文件路径
45
+ - 其他修复与优化
46
+
47
+ ## Version 0.2.0 - 2025-02-16
48
+
49
+ Unstable version
50
+
51
+ - Change 🥟 Functions changed: `caseCamel` `casePascal` `caseKebab` `caseSnake` now deprecated, will be removed in the future, use `camelCase` `pascalCase` `kebabCase` `snakeCase` instead
52
+ - Feat 🥥 Functions added: `isTypedArray` `range` `titleCase` `tryit` `withResolvers` and so forth
53
+ - Feat 🥥 Function change: `isEmpty` now validates Date object
54
+ - Feat 🥥 Function change: `fastClone` now clones FormData object
55
+ - Wip 🍉 Function wip: `deepClone` still on progress
56
+ - Other fixes and improvements
57
+
58
+ 不稳定版本
59
+
60
+ - 变更 🥟 变更函数: `caseCamel` `casePascal` `caseKebab` `caseSnake` 弃用,将来会移除,请改用 `camelCase` `pascalCase` `kebabCase` `snakeCase`
61
+ - 功能 🥥 添加函数: `isTypedArray` `range` `titleCase` `tryit` `withResolvers` 等
62
+ - 功能 🥥 变更函数: `isEmpty` 现在会校验 Date 对象
63
+ - 功能 🥥 变更函数: `fastClone` 现在会复制 FormData 对象
64
+ - 开发中 🍉 函数开发中: `deepClone` 尚在开发中
65
+ - 其他修复与优化
66
+
67
+ ## Version 0.1.2 - 2025-01-09
68
+
69
+ Unstable version
70
+
71
+ - Feat 🥥 Functions added: `caseConvert` `uuidV4` `ulid` and so forth
72
+ - Feat 🥥 Misc functions added: `acceptableFileName` `acceptableFileType` `getAcceptableExtByMIME` and `getAcceptableMIMEByExt`
73
+ - Fix 🥕 Bug fixed: Unexpected export `_fastClone`
74
+ - Other fixes and improvements
75
+
76
+ 不稳定版本
77
+
78
+ - 功能 🥥 添加函数: `caseConvert` `uuidV4` `ulid` 等
79
+ - 功能 🥥 添加杂项函数: `acceptableFileName` `acceptableFileType` `getAcceptableExtByMIME` 和 `getAcceptableMIMEByExt`
80
+ - 修复 🥕 缺陷修复: 不应导出 `_fastClone`
81
+ - 其他修复与优化
82
+
83
+ ## Version 0.1.1 - 2024-11-11
84
+
85
+ Unstable version
86
+
87
+ - Feat 🥥 Functions added: `isEmpty` `shuffle` `isPrimitive` and so forth
88
+ - Fix 🥕 Bug fixed: `isNil(document.all)` expected to be `false`
89
+ - Other fixes and improvements
90
+
91
+ 不稳定版本
92
+
93
+ - 功能 🥥 添加函数: `isEmpty` `shuffle` `isPrimitive` 等
94
+ - 修复 🥕 缺陷修复: `isNil(document.all)` 应为 `false`
95
+ - 其他修复与优化
96
+
97
+ ## Version 0.1.0 - 2024-09-28
98
+
99
+ Unstable version
100
+
101
+ - Feat 🥥 Functions added: `fastClone` `compose` `getTag` and so forth
102
+ - Feat 🥥 Function change: `curry` now supports optional arguments
103
+ - Fix 🥕 Bug fixed: Type hint for `passWith` now corrected
104
+ - Other fixes and improvements
105
+
106
+ 不稳定版本
107
+
108
+ - 功能 🥥 添加函数: `fastClone` `compose` `getTag` 等
109
+ - 功能 🥥 变更函数: `curry` 支持可选参数
110
+ - 修复 🥕 缺陷修复: 修复了 `passWith` 的类型提示
111
+ - 其他修复与优化
package/README.EN.md ADDED
@@ -0,0 +1,131 @@
1
+ # Foreslash
2
+
3
+ <div align="center">
4
+ <p align="center">
5
+ <img src="https://img.shields.io/github/languages/top/Moushudyx/foreslash" alt="GitHub top lang" />
6
+ <img src="https://img.shields.io/badge/license-Mulan_PSL_v2-blue" alt="GitHub license" />
7
+ <img src="https://img.shields.io/npm/v/foreslash" alt="NPM Version" />
8
+ <img src="https://img.shields.io/npm/dm/foreslash" alt="NPM Downloads" />
9
+ <img src="https://img.shields.io/bundlejs/size/foreslash?label=gzipped" alt="NPM package gzipped size" />
10
+ </p>
11
+ <p align="center">
12
+ Foreslash is a Javascript utilities lib which contains plenty of practical functions.
13
+ </p>
14
+ <p align="center">
15
+ <a href="https://moushudyx.github.io/foreslash/en">Documentation</a>
16
+ </p>
17
+ <p align="center">
18
+ <a href="./README.md">中文</a>
19
+ |
20
+ <span>English</span>
21
+ </p>
22
+ </div>
23
+
24
+ ## Why Foreslash
25
+
26
+ ### Type definitions
27
+
28
+ Foreslash comes with comprehensive type definitions out of the box, eliminating the need to install `@types/XXX`.
29
+
30
+ - Foreslash is developed using [typescript](https://github.com/microsoft/TypeScript), and we uses [jest](https://github.com/facebook/jest) and [ts-jest](https://github.com/kulshekhar/ts-jest) for unit testing.
31
+
32
+ ### Minimal Side Effects
33
+
34
+ Unless explicitly stated, methods in Foreslash are designed with **minimal side effects**.
35
+
36
+ - Methods from Foreslash do not modify mutate input data, they return a new data instead.
37
+
38
+ ```js
39
+ import { shuffle } from 'foreslash'
40
+
41
+ const arr = [1, 2, 3, 4, 5, 6, 7, 8]
42
+ const shuffled = shuffle(arr) // Returns a new array [3, 2, 6, 5, 8, 1, 7, 4] while `arr` remains unchanged
43
+ ```
44
+
45
+ ### Functional Programming
46
+
47
+ Foreslash offers functional programming utilities methods like `curry`、`pipe`.
48
+
49
+ - For performance reasons, methods are **not curried** unless explicitly stated.
50
+ - The curry method `curry` and the placeholder `_` in Foreslash are compatible with [Ramda](https://github.com/ramda/ramda).
51
+
52
+ ```js
53
+ import { curry, _ } from 'foreslash'
54
+
55
+ // const regTest = (regex) => (str) => regex.test(str) // The order of arguments is strictly defined and less flexible
56
+ const regTest = curry((str, regex) => regex.test(str)) // Currying a function to make it more flexible and reusable
57
+
58
+ const testString = regTest('123')
59
+ testString(/^\d+$/) // true
60
+ testString(/^[a-z]+$/) // false
61
+
62
+ const isDigits = regTest(_, /^\d+$/) // Using a placeholder to skip certain arguments
63
+ isDigits('123') // true
64
+ isDigits('abc') // false
65
+ ```
66
+
67
+ ## Install & Usage
68
+
69
+ ```bash
70
+ npm install foreslash # Install with npm
71
+ yarn add foreslash # Install with yarn
72
+ pnpm install foreslash # Install with pnpm
73
+ ```
74
+
75
+ Foreslash supports ESM/CJS/UMD, and ESM is recommended.
76
+
77
+ Visit [document](https://moushudyx.github.io/foreslash) for more API.
78
+
79
+ ```js
80
+ // curry & randomString
81
+ import { _, curry, randomString } from 'foreslash'
82
+
83
+ randomString(3) // 'bcD' or 'T30' or '7c5' or ...
84
+
85
+ const curriedRanStr = curry(randomString)
86
+
87
+ const randomABCD = curriedRanStr(_, 'ABCD')
88
+ randomABCD(3) // 'BDC' or 'ACD' or 'DBB' or ...
89
+
90
+ const random1234 = curriedRanStr(_, '1234')
91
+ random1234(3) // '431' or '213' or '241' or ...
92
+
93
+ // fastClone
94
+ import { fastClone } from 'foreslash'
95
+
96
+ const obj = { a: { b: { c: {} } }, map: new Map() }
97
+ obj.a.b.c.d = obj
98
+ obj.map.set(obj, 'val')
99
+
100
+ const clone = fastClone(obj)
101
+ clone === obj // false
102
+ // clone Deep
103
+ clone.a.b.c === obj.a.b.c // false
104
+ clone.a.b.c.d === clone // true
105
+ // clone Map
106
+ clone.map === obj.map // false
107
+ clone.map.get(clone) === 'val' // true
108
+ ```
109
+
110
+ ## Compatibility
111
+
112
+ Foreslash compatible with any Javascript runtime that runs ES6 code, including Node.js and web browser.
113
+
114
+ - Internet Explorer is not supported, [core-js](https://github.com/zloirock/core-js) and transformation into ES5(ES2009) via [babel](https://babeljs.io/) might be needed.
115
+
116
+ ## Credits
117
+
118
+ Foreslash based on the following open source projects.
119
+
120
+ - [jest](https://github.com/facebook/jest)
121
+ - [rollup](https://github.com/rollup/rollup)
122
+ - [ts-jest](https://github.com/kulshekhar/ts-jest)
123
+ - [ts-toolbelt](https://github.com/millsp/ts-toolbelt)
124
+ - [typescript](https://github.com/microsoft/TypeScript)
125
+ - [vitepress](https://github.com/vuejs/vitepress)
126
+ - [yarn](https://github.com/yarnpkg/yarn)
127
+
128
+ Some of the methods are inspired by the following open source projects.
129
+
130
+ - [radash](https://github.com/sodiray/radash)
131
+ - [ramda](https://github.com/ramda/ramda)
package/README.md CHANGED
@@ -1,36 +1,77 @@
1
1
  # Foreslash
2
2
 
3
- ![GitHub top lang](https://img.shields.io/github/languages/top/Moushudyx/foreslash)
4
- ![GitHub license](https://img.shields.io/badge/license-Mulan_PSL_v2-blue)
5
- ![NPM Version](https://img.shields.io/npm/v/foreslash)
6
- ![NPM Downloads](https://img.shields.io/npm/dm/foreslash)
7
- ![NPM package minimized gzipped size](https://img.shields.io/bundlejs/size/foreslash?label=gzipped)
3
+ <div align="center">
4
+ <p align="center">
5
+ <img src="https://img.shields.io/github/languages/top/Moushudyx/foreslash" alt="GitHub top lang" />
6
+ <a href="https://github.com/moushudyx/foreslash/blob/master/LICENSE">
7
+ <img src="https://img.shields.io/badge/license-Mulan_PSL_v2-blue" alt="GitHub license" />
8
+ </a>
9
+ <img src="https://img.shields.io/npm/v/foreslash" alt="NPM Version" />
10
+ <img src="https://img.shields.io/npm/dm/foreslash" alt="NPM Downloads" />
11
+ <img src="https://img.shields.io/bundlejs/size/foreslash?label=gzipped" alt="NPM package gzipped size" />
12
+ </p>
13
+ <p align="center">
14
+ Foreslash 是一个 Javascript 工具库,包含大量实用函数。
15
+ </p>
16
+ <p align="center">
17
+ Foreslash is a Javascript utilities lib which contains plenty of practical functions.
18
+ </p>
19
+ <p align="center">
20
+ <a href="https://moushudyx.github.io/foreslash">文档</a>
21
+ |
22
+ <a href="https://moushudyx.github.io/foreslash/en">Documentation</a>
23
+ </p>
24
+ <p align="center">
25
+ <span>中文</span>
26
+ |
27
+ <a href="./README.EN.md">English</a>
28
+ </p>
29
+ </div>
30
+
31
+ ## 为何选择 Foreslash
32
+
33
+ ### 完整类型提示
34
+
35
+ Foreslash 自带完整的类型提示,无需安装 `@types/XXX`。
36
+
37
+ - Foreslash 使用 [typescript](https://github.com/microsoft/TypeScript) 开发,并使用 [jest](https://github.com/facebook/jest) 和 [ts-jest](https://github.com/kulshekhar/ts-jest) 编写单元测试。
38
+
39
+ ### 低副作用
40
+
41
+ 若无特殊说明,Foreslash 的任何方法都**几乎没有副作用**
42
+
43
+ - Foreslash 的方法不会修改传入的原数据,返回值将是一个新的数据。
8
44
 
9
- Foreslash 是一个 Javascript 工具库,包含大量实用函数。
10
-
11
- Foreslash is a Javascript utilities lib which contains plenty of practical functions.
12
-
13
- [文档](https://moushudyx.github.io/foreslash) | [Documentation](https://moushudyx.github.io/foreslash/en)
14
-
15
- ## 设计理念 Design Concept
45
+ ```js
46
+ import { shuffle } from 'foreslash'
16
47
 
17
- ### 函数式 Functional
48
+ const arr = [1, 2, 3, 4, 5, 6, 7, 8]
49
+ const shuffled = shuffle(arr) // 返回新的数组 [3, 2, 6, 5, 8, 1, 7, 4] 同时 arr 并没有受到影响
50
+ ```
18
51
 
19
- 此库提供了诸如`curry`、`pipe`等函数式编程的方法,但若无特殊说明,此库的任何方法都**不是柯里化**的。
52
+ ### 函数式编程
20
53
 
21
- - 这是出于性能优化和调试方便的考量。
54
+ Foreslash 提供了诸如 `curry`、`pipe` 等函数式编程的方法。
22
55
 
23
- 此库的柯里化方法`curry`和柯里化占位符`_`与 [ramda](https://github.com/ramda/ramda) 兼容。
56
+ - 出于性能优化的考量,若无特殊说明,此库的任何方法都**不是柯里化**的。
57
+ - Foreslash 的柯里化方法 `curry` 和柯里化占位符 `_` 与 [ramda](https://github.com/ramda/ramda) 兼容。
24
58
 
25
- ### 不变性 Immutability
59
+ ```js
60
+ import { curry, _ } from 'foreslash'
26
61
 
27
- 若无特殊说明,此库的任何方法都是**不可变**的,即不会修改传入的原数据,返回值将是一个新的数据。
62
+ // const regTest = (regex) => (str) => regex.test(str) // 传统方法,传入参数的顺序有强制规定不够灵活
63
+ const regTest = curry((str, regex) => regex.test(str)) // 这里柯里化了一个函数使其能更灵活地复用
28
64
 
29
- ### 类型 Type
65
+ const testString = regTest('123')
66
+ testString(/^\d+$/) // true
67
+ testString(/^[a-z]+$/) // false
30
68
 
31
- 此库使用 [typescript](https://github.com/microsoft/TypeScript) 编写,并使用 [jest](https://github.com/facebook/jest) [ts-jest](https://github.com/kulshekhar/ts-jest) 来单元测试。
69
+ const isDigits = regTest(_, /^\d+$/) // 使用占位符来跳过填充某些参数,传统方法做不到这点
70
+ isDigits('123') // true
71
+ isDigits('abc') // false
72
+ ```
32
73
 
33
- ## 安装与使用 Install & Usage
74
+ ## 安装与使用
34
75
 
35
76
  ```bash
36
77
  npm install foreslash # 使用 npm 安装
@@ -38,52 +79,50 @@ yarn add foreslash # 使用 yarn 安装
38
79
  pnpm install foreslash # 使用 pnpm 安装
39
80
  ```
40
81
 
82
+ Foreslash 支持 ESM、CJS、UMD 三种引入方式,推荐使用 ESM。
83
+
84
+ 更多 API 请参考[文档](https://moushudyx.github.io/foreslash)。
85
+
41
86
  ```js
42
87
  // curry & randomString
43
88
  import { _, curry, randomString } from 'foreslash'
44
89
 
45
- randomString(3) // 'bcD' or 'T30' or '7c5' or ...
90
+ randomString(3) // 'bcD' 'T30' '7c5' ...
46
91
 
47
92
  const curriedRanStr = curry(randomString)
48
93
 
49
94
  const randomABCD = curriedRanStr(_, 'ABCD')
50
- randomABCD(3) // 'BDC' or 'ACD' or 'DBB' or ...
95
+ randomABCD(3) // 'BDC' 'ACD' 'DBB' ...
51
96
 
52
97
  const random1234 = curriedRanStr(_, '1234')
53
- random1234(3) // '431' or '213' or '241' or ...
98
+ random1234(3) // '431' '213' '241' ...
54
99
 
55
100
  // fastClone
56
101
  import { fastClone } from 'foreslash'
57
102
 
58
103
  const obj = { a: { b: { c: {} } }, map: new Map() }
59
- obj.a.b.c.d = obj
60
- obj.map.set(obj, 'val')
104
+ obj.a.b.c.d = obj // 常见的循环引用
105
+ obj.map.set(obj, 'val') // Map 键上的循环引用
61
106
 
62
107
  const clone = fastClone(obj)
63
108
  clone === obj // false
64
- // clone Deep
109
+ // 处理深层级对象
65
110
  clone.a.b.c === obj.a.b.c // false
66
111
  clone.a.b.c.d === clone // true
67
- // clone Map
112
+ // 处理 Map
68
113
  clone.map === obj.map // false
69
114
  clone.map.get(clone) === 'val' // true
70
115
  ```
71
116
 
72
- ## 兼容性 Compatibility
73
-
74
- 此库兼容任何能正确运行 ES6 代码的 Javascript 环境,包括 node.js 和浏览器
75
-
76
- - 不支持 Internet Explorer,但是使用 [core-js](https://github.com/zloirock/core-js) 处理并由 [babel](https://babeljs.io/) 转译为 ES5(ES2009) 后可以使用
117
+ ## 兼容性
77
118
 
78
- ### polyfill
119
+ Foreslash 兼容任何能正确运行 ES6 代码的 Javascript 环境,包括 Node.js 和浏览器。
79
120
 
80
- 此库没有 polyfill,如果要在旧版浏览器中使用,请使用 [core-js](https://github.com/zloirock/core-js) 或其他 polyfill 工具
121
+ - 不支持 Internet Explorer,但是使用 [core-js](https://github.com/zloirock/core-js) 处理并由 [babel](https://babeljs.io/) 转译为 ES5(ES2009) 后可以使用。
81
122
 
82
- ### ES2015
123
+ ## 开源软件
83
124
 
84
- 此库使用 ES6(ES2015) 语法,如果要在旧版浏览器中使用,请使用 [babel](https://babeljs.io/) 或其他打包/转译工具
85
-
86
- ## 开源软件 Credits
125
+ Foreslash 的诞生离不开这些开源项目:
87
126
 
88
127
  - [jest](https://github.com/facebook/jest)
89
128
  - [rollup](https://github.com/rollup/rollup)
@@ -92,3 +131,8 @@ clone.map.get(clone) === 'val' // true
92
131
  - [typescript](https://github.com/microsoft/TypeScript)
93
132
  - [vitepress](https://github.com/vuejs/vitepress)
94
133
  - [yarn](https://github.com/yarnpkg/yarn)
134
+
135
+ 部分方法灵感源于以下开源项目:
136
+
137
+ - [radash](https://github.com/sodiray/radash)
138
+ - [ramda](https://github.com/ramda/ramda)
package/lib/index.cmn.cjs CHANGED
@@ -47,12 +47,58 @@ function range(start, end, stepOrOptions) {
47
47
  return res;
48
48
  }
49
49
 
50
- function sleep(time = 1000) {
51
- return new Promise((res) => {
52
- setTimeout(res, time);
53
- });
50
+ function remove(arr, ...item) {
51
+ const removeSet = new Set();
52
+ const judgerList = [];
53
+ for (let i = 0; i < item.length; i++) {
54
+ const cond = item[i];
55
+ if (typeof cond === 'function')
56
+ judgerList.push(cond);
57
+ else
58
+ removeSet.add(cond);
59
+ }
60
+ const res = [];
61
+ for (let i = 0; i < arr.length; i++) {
62
+ const el = arr[i];
63
+ if (removeSet.has(el) || judgerList.some((judger) => judger(el)))
64
+ continue;
65
+ res.push(el);
66
+ }
67
+ return res;
54
68
  }
55
69
 
70
+ /******************************************************************************
71
+ Copyright (c) Microsoft Corporation.
72
+
73
+ Permission to use, copy, modify, and/or distribute this software for any
74
+ purpose with or without fee is hereby granted.
75
+
76
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
77
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
78
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
79
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
80
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
81
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
82
+ PERFORMANCE OF THIS SOFTWARE.
83
+ ***************************************************************************** */
84
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
85
+
86
+
87
+ function __awaiter(thisArg, _arguments, P, generator) {
88
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
89
+ return new (P || (P = Promise))(function (resolve, reject) {
90
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
91
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
92
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
93
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
94
+ });
95
+ }
96
+
97
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
98
+ var e = new Error(message);
99
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
100
+ };
101
+
56
102
  const isArray = Array.isArray;
57
103
 
58
104
  const object2String = Object.prototype.toString;
@@ -151,7 +197,7 @@ function isNumber(value) {
151
197
  }
152
198
 
153
199
  function isPrimitive(value) {
154
- return value == null || (typeof value !== 'object' && typeof value !== 'function');
200
+ return value === undefined || value === null || (typeof value !== 'object' && typeof value !== 'function');
155
201
  }
156
202
 
157
203
  function isPromise(value) {
@@ -272,6 +318,45 @@ function tryit(fn) {
272
318
  };
273
319
  }
274
320
 
321
+ function defer(asyncFunction, options) {
322
+ return __awaiter(this, void 0, void 0, function* () {
323
+ const queue = [];
324
+ const { rethrow = false } = options || {};
325
+ const defaultOption = { rethrow };
326
+ const cleanUp = (fn, options) => {
327
+ queue.push({ fn, opt: Object.assign(defaultOption, options) });
328
+ return queue.length - 1;
329
+ };
330
+ const cancelCleanUp = (fnOrIndex) => {
331
+ if (isInteger(fnOrIndex) && fnOrIndex > -1)
332
+ queue[fnOrIndex] = null;
333
+ else if (isFunction(fnOrIndex)) {
334
+ const i = queue.findIndex((item) => item && item.fn === fnOrIndex);
335
+ if (i > -1)
336
+ queue[i] = null;
337
+ }
338
+ };
339
+ const [err, res] = yield tryit(asyncFunction)(cleanUp, cancelCleanUp);
340
+ for (const item of queue) {
341
+ if (!item)
342
+ continue;
343
+ const { fn, opt } = item;
344
+ const [cleanUpErr] = yield tryit(fn)(err);
345
+ if (cleanUpErr && opt.rethrow)
346
+ throw cleanUpErr;
347
+ }
348
+ if (err)
349
+ throw err;
350
+ return res;
351
+ });
352
+ }
353
+
354
+ function sleep(time = 1000) {
355
+ return new Promise((res) => {
356
+ setTimeout(res, time);
357
+ });
358
+ }
359
+
275
360
  const noop = function noop() { };
276
361
 
277
362
  function withResolvers(PromiseLike = Promise) {
@@ -285,6 +370,23 @@ function withResolvers(PromiseLike = Promise) {
285
370
  return { promise, resolve, reject };
286
371
  }
287
372
 
373
+ function clamp(num, min, max, options) {
374
+ var _a, _b;
375
+ if (isNaN(min))
376
+ throw new Error('Invalid min parameter');
377
+ if (isNaN(max))
378
+ throw new Error('Invalid max parameter');
379
+ if (max < min) {
380
+ [min, max] = [max, min];
381
+ }
382
+ const { default: def, defaultMin: _dMin, defaultMax: _dMax } = options || {};
383
+ const defaultMin = (_a = _dMin !== null && _dMin !== void 0 ? _dMin : def) !== null && _a !== void 0 ? _a : min;
384
+ const defaultMax = (_b = _dMax !== null && _dMax !== void 0 ? _dMax : def) !== null && _b !== void 0 ? _b : max;
385
+ if (isNaN(num))
386
+ return defaultMin;
387
+ return num < min ? defaultMin : num > max ? defaultMax : num;
388
+ }
389
+
288
390
  const mimeMap = {
289
391
  application: {
290
392
  acrobat: ['pdf'],
@@ -1283,6 +1385,53 @@ function _curryMore(fn) {
1283
1385
  }
1284
1386
  }
1285
1387
 
1388
+ function _throttle(fn, delay, options) {
1389
+ var _a, _b, _c;
1390
+ const trailing = (_a = options === null || options === void 0 ? void 0 : options.trailing) !== null && _a !== void 0 ? _a : false;
1391
+ const trailingRunLast = (_b = options === null || options === void 0 ? void 0 : options.trailingRunLast) !== null && _b !== void 0 ? _b : true;
1392
+ const leading = (_c = options === null || options === void 0 ? void 0 : options.leading) !== null && _c !== void 0 ? _c : true;
1393
+ let timer = null;
1394
+ let lastTime = 0;
1395
+ const clearTimer = () => {
1396
+ if (timer) {
1397
+ clearInterval(timer);
1398
+ timer = null;
1399
+ }
1400
+ };
1401
+ const reset = () => {
1402
+ clearTimer();
1403
+ lastTime = 0;
1404
+ };
1405
+ return Object.assign(function (...args) {
1406
+ const now = Date.now();
1407
+ const timeGap = now - lastTime - delay;
1408
+ if (timeGap >= 0) {
1409
+ lastTime = now;
1410
+ clearTimer();
1411
+ }
1412
+ if (timeGap < 0 && trailing && trailingRunLast && timer) {
1413
+ clearTimer();
1414
+ }
1415
+ if (timeGap >= 0 && leading) {
1416
+ fn.apply(this, args);
1417
+ }
1418
+ else if (timeGap >= 0 || (timeGap < 0 && trailing && !timer)) {
1419
+ timer = setTimeout(() => {
1420
+ lastTime = Date.now();
1421
+ timer = null;
1422
+ fn.apply(this, args);
1423
+ }, timeGap >= 0 ? delay : -timeGap);
1424
+ }
1425
+ }, { reset });
1426
+ }
1427
+
1428
+ function debounce(fn, delay, options) {
1429
+ if (!isNumber(delay) || !isFinite(delay) || delay <= 0) {
1430
+ throw new Error('Invalid delay parameter');
1431
+ }
1432
+ return _throttle(fn, delay, Object.assign({ trailing: true, leading: false, trailingRunLast: true }, options));
1433
+ }
1434
+
1286
1435
  function _cloneArray(obj, map, cloner, ...args) {
1287
1436
  const res = obj.slice();
1288
1437
  map.set(obj, res);
@@ -1547,6 +1696,13 @@ function pipe(...pipeFunc) {
1547
1696
  };
1548
1697
  }
1549
1698
 
1699
+ function throttle(fn, delay, options) {
1700
+ if (!isNumber(delay) || !isFinite(delay) || delay <= 0) {
1701
+ throw new Error('Invalid delay parameter');
1702
+ }
1703
+ return _throttle(fn, delay, Object.assign({ trailing: false, leading: true }, options));
1704
+ }
1705
+
1550
1706
  exports._ = _;
1551
1707
  exports.acceptableFileName = acceptableFileName;
1552
1708
  exports.acceptableFileType = acceptableFileType;
@@ -1556,9 +1712,12 @@ exports.caseConvert = caseConvert;
1556
1712
  exports.caseKebab = caseKebab;
1557
1713
  exports.casePascal = casePascal;
1558
1714
  exports.caseSnake = caseSnake;
1715
+ exports.clamp = clamp;
1559
1716
  exports.compose = compose;
1560
1717
  exports.curry = _curryMore;
1718
+ exports.debounce = debounce;
1561
1719
  exports.deepClone = deepClone;
1720
+ exports.defer = defer;
1562
1721
  exports.fastClone = fastClone;
1563
1722
  exports.getAcceptableExtByMIME = getAcceptableExtByMIME;
1564
1723
  exports.getAcceptableMIMEByExt = getAcceptableMIMEByExt;
@@ -1627,10 +1786,12 @@ exports.randomInt = randomInt;
1627
1786
  exports.randomIntFloor = randomIntFloor;
1628
1787
  exports.randomString = randomString;
1629
1788
  exports.range = range;
1789
+ exports.remove = remove;
1630
1790
  exports.shuffle = shuffle;
1631
1791
  exports.sleep = sleep;
1632
1792
  exports.snakeCase = snakeCase;
1633
1793
  exports.splitWords = splitWords;
1794
+ exports.throttle = throttle;
1634
1795
  exports.titleCase = titleCase;
1635
1796
  exports.tryit = tryit;
1636
1797
  exports.ulid = ulid;
package/lib/index.d.ts CHANGED
@@ -57,6 +57,65 @@ declare function range(start: number, end: number, step?: number): number[];
57
57
  */
58
58
  declare function range<T = number>(start: number, end: number, options?: RangeOptions<T>): T[];
59
59
 
60
+ /**
61
+ * 根据给定的条件来移除数组中的元素(不会影响原数组)
62
+ * @param arr 需要移除部分元素的数组
63
+ * @param item 需要移除的元素, 或判断某个元素是否应该移除的函数
64
+ * @returns 返回移除后的新数组
65
+ * @example
66
+ * ```js
67
+ * // 基本用法
68
+ * remove([1, 2, 3, 4, 5], 5) // [1, 2, 3, 4]
69
+ * remove([1, 2, 3, 4, 5], (num) => num % 2) // [1, 3, 5]
70
+ * // 可以传入多个过滤条件
71
+ * remove([1, 2, 3, 4, 5], 3, 4, 5) // [1, 2]
72
+ * remove([1, 2, 3, 4, 5], (num) => num % 2, (num) => num > 4) // [1, 3]
73
+ * // 可以混合传入过滤条件
74
+ * remove([1, 2, 3, 4, 5], (num) => num % 2, (num) => num > 4, 3) // [1]
75
+ * ```
76
+ * @version 0.2.2
77
+ */
78
+ declare function remove<T>(arr: ArrayLike<T>, ...item: (T | ((val: T) => unknown))[]): T[];
79
+
80
+ type DeferCallbackFunction = (error?: Error) => unknown;
81
+ type DeferOption = {
82
+ rethrow: boolean;
83
+ };
84
+ /**
85
+ * 在传入的函数执行完毕后(或是抛出异常后), 执行*延迟操作*\
86
+ * 使用场景一般是处理“副作用”, 类似 `try {} finally {}` 结构但是更加灵活
87
+ * - 执行*延迟操作*的时机是函数执行完毕后(或是抛出异常后), 同时 `defer` 返回的 `Promise` 并未结束
88
+ * - 这个设计是防止*延迟操作*与后续逻辑产生干扰
89
+ * - `defer` 返回的 `Promise` 会根据传入的函数是否正常处理/抛出异常而变化
90
+ * - 传入的函数正常处理, 结果为 `1`, 那么 `defer` 将返回 `Promise {<fulfilled>: 1}`
91
+ * - 传入的函数抛出异常, 内容为 `2`, 那么 `defer` 将返回 `Promise {<rejected>: 2}`
92
+ *
93
+ * *延迟操作*时抛出的错误不会正常抛出
94
+ * - 如果希望执行*延迟操作*时正常抛错, 您可以配置 `options.rethrow = true`
95
+ * @param asyncFunction 需要在执行完毕后做额外处理函数
96
+ * @param options.rethrow 执行*延迟操作*时是否正常抛错, 默认不抛错
97
+ * @returns 一个 `Promise` 会根据传入的函数是否正常处理/抛出异常而变化
98
+ * @example
99
+ * ```js
100
+ * // 一般用途
101
+ * defer((cleanUp) => {
102
+ * cleanUp(() => console.log(2))
103
+ * cleanUp(() => console.log(3))
104
+ * console.log(1)
105
+ * }) // 依次输出 1 2 3
106
+ * // 可以动态取消计划好的延迟操作
107
+ * defer((cleanUp, cancelCleanUp) => {
108
+ * const cleanUpId = cleanUp(() => console.log(123)) // 会返回一个数字作为 id
109
+ * cleanUp(() => console.log(5))
110
+ * cleanUp(() => console.log(6))
111
+ * console.log(4)
112
+ * cancelCleanUp(cleanUpId) // 可以用于取消指定的延迟操作
113
+ * }) // 依次输出 4 5 6
114
+ * ```
115
+ * @version 0.2.3
116
+ */
117
+ declare function defer<T>(asyncFunction: (cleanUp: (fn: DeferCallbackFunction) => number, cancelCleanUp: (fnOrIndex: DeferCallbackFunction | number) => void) => T | Promise<T>, options?: Partial<DeferOption>): Promise<T>;
118
+
60
119
  /**
61
120
  * 延迟一定时间
62
121
  * @param time 延迟时间, 单位为毫秒(ms), 默认为 `1000` (即 1 秒)
@@ -701,6 +760,33 @@ declare function isWrapperSymbol(value: unknown): value is Symbol;
701
760
  */
702
761
  declare function isWrapperBigInt(value: unknown): value is BigInt;
703
762
 
763
+ /**
764
+ * 将一个数字限制在指定范围内
765
+ * @param num 初始值
766
+ * @param min 最小值, 没有默认值, 初始值小于最小值或初始值不为数字时返回最小值
767
+ * @param max 最大值, 没有默认值, 初始值大于最大值时返回最大值
768
+ * @param options 配置项, 可以配置默认值等
769
+ * - `default` 默认值, 如果初始值不在范围内, 则返回默认值
770
+ * - `defaultMin` 初始值小于最小值时返回该值, 覆盖 `default` 参数
771
+ * - `defaultMax` 初始值大于最大值时返回该值
772
+ * @returns 返回一个在指定范围内的数字
773
+ * @example
774
+ * ```js
775
+ * clamp(5, 0, 10) // 5
776
+ * clamp(15, 0, 10) // 10
777
+ * clamp(-5, 0, 10) // 0
778
+ * clamp(5, 0, 10, { default: 6 }) // 5
779
+ * clamp(15, 0, 10, { default: 6 }) // 6
780
+ * clamp(-5, 0, 10, { default: 6 }) // 6
781
+ * ```
782
+ * @version 0.2.2
783
+ */
784
+ declare function clamp(num: number, min: number, max: number, options?: {
785
+ default?: number;
786
+ defaultMin?: number;
787
+ defaultMax?: number;
788
+ }): number;
789
+
704
790
  /**
705
791
  * 根据文件名称判断是否匹配支持的文件类型,需要注意 `.C` 与 `.c` 的区别
706
792
  * - `text/x-c++src` 对应 `.C`
@@ -1271,6 +1357,28 @@ declare function _curryMore<Arg1, Arg2, Res>(fn: (arg1: Arg1, arg2: Arg2) => Res
1271
1357
  declare function _curryMore<Arg1, Arg2, Arg3, Res>(fn: (arg1: Arg1, arg2: Arg2, arg3: Arg3) => Res): F.Curry<(arg1: Arg1, arg2: Arg2, arg3: Arg3) => Res>;
1272
1358
  declare function _curryMore<Args extends Array<any>, Res>(fn: (...args: Args) => Res): F.Curry<(...args: Args) => Res>;
1273
1359
 
1360
+ interface ThrottleOptions {
1361
+ /** 节流时触发的最后一次调用是否执行, 默认为否 */
1362
+ trailing?: boolean;
1363
+ /** 触发的最后一次调用是否使用最后一次的参数, 默认为是 */
1364
+ trailingRunLast?: boolean;
1365
+ /** 第一次触发是否直接执行, 默认为是 */
1366
+ leading?: boolean;
1367
+ }
1368
+
1369
+ /**
1370
+ * 将一个高频调用的函数处理为防抖函数, 一段时间内多次调用会合并为一次
1371
+ * - 比如说 `delay` 设置为 120, 那么方法在 120ms 内多次触发, 也只会在第一次触发的 120ms 后执行一次
1372
+ * @param fn 需要防抖的函数
1373
+ * @param delay 防抖时间间隔, 单位毫秒, 建议 100 以上
1374
+ * @param options 防抖函数的配置, 默认值为 `{ trailing: true, leading: false }`
1375
+ * @returns 一个防抖处理的函数, 你可以使用其上的 `reset` 方法重置节流函数内部状态(会停止计划中的函数执行)
1376
+ * @version 0.2.2
1377
+ */
1378
+ declare function debounce<T extends any[]>(fn: (...args: T) => any, delay: number, options?: ThrottleOptions): ((this: any, ...args: T) => void) & {
1379
+ reset: () => void;
1380
+ };
1381
+
1274
1382
  interface CustomCloner<T = any> {
1275
1383
  cloner(val: T, map: Map<any, any>): T;
1276
1384
  judger(val: any): boolean;
@@ -1552,4 +1660,17 @@ declare function pipe<PipeArgs extends any[], PipeResult, Mid1, Mid2, Mid3, Mid4
1552
1660
  declare function pipe<PipeArgs extends any[], PipeResult, Mid1, Mid2, Mid3, Mid4, Mid5, Mid6, Mid7>(...pipeFunc: PipeFuncList8<PipeArgs, PipeResult, Mid1, Mid2, Mid3, Mid4, Mid5, Mid6, Mid7>): (...args: PipeArgs) => PipeResult;
1553
1661
  declare function pipe<PipeArgs extends any[], PipeResult, Mid1, Mid2, Mid3, Mid4, Mid5, Mid6, Mid7>(...pipeFunc: PipeFuncListMore<PipeArgs, PipeResult, Mid1, Mid2, Mid3, Mid4, Mid5, Mid6, Mid7>): (...args: PipeArgs) => PipeResult;
1554
1662
 
1555
- export { type CloneOptions, type CustomCloner, type RangeOptions, type TypedArray, _, acceptableFileName, acceptableFileType, camelCase, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, compose, _curryMore as curry, deepClone, fastClone, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getTag, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBoolean, isBuffer, isDataView, isDate, isEmpty, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isNil, isNull, isNumber, isObject, isPlaceholder, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, memo, noop, not, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomHexString, randomInt, randomIntFloor, randomString, range, shuffle, sleep, snakeCase, splitWords, titleCase, tryit, ulid, uuidNil, uuidV4, withResolvers };
1663
+ /**
1664
+ * 将一个高频调用的函数处理为节流函数, 一段时间内只会调用一次
1665
+ * - 比如说 `delay` 设置为 120, 那么方法在第一次触发后执行, 随后的 120ms 内多次触发也不会再执行
1666
+ * @param fn 需要节流的函数
1667
+ * @param delay 节流时间间隔, 单位毫秒, 建议 100 以上
1668
+ * @param options 节流函数的配置, 默认值为 `{ trailing: false, leading: true }`
1669
+ * @returns 一个节流处理的函数, 你可以使用其上的 `reset` 方法重置节流函数内部状态(会停止计划中的函数执行)
1670
+ * @version 0.2.2
1671
+ */
1672
+ declare function throttle<T extends any[]>(fn: (...args: T) => any, delay: number, options?: ThrottleOptions): ((this: any, ...args: T) => void) & {
1673
+ reset: () => void;
1674
+ };
1675
+
1676
+ export { type CloneOptions, type CustomCloner, type RangeOptions, type TypedArray, _, acceptableFileName, acceptableFileType, camelCase, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, clamp, compose, _curryMore as curry, debounce, deepClone, defer, fastClone, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getTag, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBoolean, isBuffer, isDataView, isDate, isEmpty, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isNil, isNull, isNumber, isObject, isPlaceholder, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, memo, noop, not, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomHexString, randomInt, randomIntFloor, randomString, range, remove, shuffle, sleep, snakeCase, splitWords, throttle, titleCase, tryit, ulid, uuidNil, uuidV4, withResolvers };
package/lib/index.mjs CHANGED
@@ -45,12 +45,58 @@ function range(start, end, stepOrOptions) {
45
45
  return res;
46
46
  }
47
47
 
48
- function sleep(time = 1000) {
49
- return new Promise((res) => {
50
- setTimeout(res, time);
51
- });
48
+ function remove(arr, ...item) {
49
+ const removeSet = new Set();
50
+ const judgerList = [];
51
+ for (let i = 0; i < item.length; i++) {
52
+ const cond = item[i];
53
+ if (typeof cond === 'function')
54
+ judgerList.push(cond);
55
+ else
56
+ removeSet.add(cond);
57
+ }
58
+ const res = [];
59
+ for (let i = 0; i < arr.length; i++) {
60
+ const el = arr[i];
61
+ if (removeSet.has(el) || judgerList.some((judger) => judger(el)))
62
+ continue;
63
+ res.push(el);
64
+ }
65
+ return res;
52
66
  }
53
67
 
68
+ /******************************************************************************
69
+ Copyright (c) Microsoft Corporation.
70
+
71
+ Permission to use, copy, modify, and/or distribute this software for any
72
+ purpose with or without fee is hereby granted.
73
+
74
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
75
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
76
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
77
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
78
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
79
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
80
+ PERFORMANCE OF THIS SOFTWARE.
81
+ ***************************************************************************** */
82
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
83
+
84
+
85
+ function __awaiter(thisArg, _arguments, P, generator) {
86
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
87
+ return new (P || (P = Promise))(function (resolve, reject) {
88
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
89
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
90
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
91
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
92
+ });
93
+ }
94
+
95
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
96
+ var e = new Error(message);
97
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
98
+ };
99
+
54
100
  const isArray = Array.isArray;
55
101
 
56
102
  const object2String = Object.prototype.toString;
@@ -149,7 +195,7 @@ function isNumber(value) {
149
195
  }
150
196
 
151
197
  function isPrimitive(value) {
152
- return value == null || (typeof value !== 'object' && typeof value !== 'function');
198
+ return value === undefined || value === null || (typeof value !== 'object' && typeof value !== 'function');
153
199
  }
154
200
 
155
201
  function isPromise(value) {
@@ -270,6 +316,45 @@ function tryit(fn) {
270
316
  };
271
317
  }
272
318
 
319
+ function defer(asyncFunction, options) {
320
+ return __awaiter(this, void 0, void 0, function* () {
321
+ const queue = [];
322
+ const { rethrow = false } = options || {};
323
+ const defaultOption = { rethrow };
324
+ const cleanUp = (fn, options) => {
325
+ queue.push({ fn, opt: Object.assign(defaultOption, options) });
326
+ return queue.length - 1;
327
+ };
328
+ const cancelCleanUp = (fnOrIndex) => {
329
+ if (isInteger(fnOrIndex) && fnOrIndex > -1)
330
+ queue[fnOrIndex] = null;
331
+ else if (isFunction(fnOrIndex)) {
332
+ const i = queue.findIndex((item) => item && item.fn === fnOrIndex);
333
+ if (i > -1)
334
+ queue[i] = null;
335
+ }
336
+ };
337
+ const [err, res] = yield tryit(asyncFunction)(cleanUp, cancelCleanUp);
338
+ for (const item of queue) {
339
+ if (!item)
340
+ continue;
341
+ const { fn, opt } = item;
342
+ const [cleanUpErr] = yield tryit(fn)(err);
343
+ if (cleanUpErr && opt.rethrow)
344
+ throw cleanUpErr;
345
+ }
346
+ if (err)
347
+ throw err;
348
+ return res;
349
+ });
350
+ }
351
+
352
+ function sleep(time = 1000) {
353
+ return new Promise((res) => {
354
+ setTimeout(res, time);
355
+ });
356
+ }
357
+
273
358
  const noop = function noop() { };
274
359
 
275
360
  function withResolvers(PromiseLike = Promise) {
@@ -283,6 +368,23 @@ function withResolvers(PromiseLike = Promise) {
283
368
  return { promise, resolve, reject };
284
369
  }
285
370
 
371
+ function clamp(num, min, max, options) {
372
+ var _a, _b;
373
+ if (isNaN(min))
374
+ throw new Error('Invalid min parameter');
375
+ if (isNaN(max))
376
+ throw new Error('Invalid max parameter');
377
+ if (max < min) {
378
+ [min, max] = [max, min];
379
+ }
380
+ const { default: def, defaultMin: _dMin, defaultMax: _dMax } = options || {};
381
+ const defaultMin = (_a = _dMin !== null && _dMin !== void 0 ? _dMin : def) !== null && _a !== void 0 ? _a : min;
382
+ const defaultMax = (_b = _dMax !== null && _dMax !== void 0 ? _dMax : def) !== null && _b !== void 0 ? _b : max;
383
+ if (isNaN(num))
384
+ return defaultMin;
385
+ return num < min ? defaultMin : num > max ? defaultMax : num;
386
+ }
387
+
286
388
  const mimeMap = {
287
389
  application: {
288
390
  acrobat: ['pdf'],
@@ -1281,6 +1383,53 @@ function _curryMore(fn) {
1281
1383
  }
1282
1384
  }
1283
1385
 
1386
+ function _throttle(fn, delay, options) {
1387
+ var _a, _b, _c;
1388
+ const trailing = (_a = options === null || options === void 0 ? void 0 : options.trailing) !== null && _a !== void 0 ? _a : false;
1389
+ const trailingRunLast = (_b = options === null || options === void 0 ? void 0 : options.trailingRunLast) !== null && _b !== void 0 ? _b : true;
1390
+ const leading = (_c = options === null || options === void 0 ? void 0 : options.leading) !== null && _c !== void 0 ? _c : true;
1391
+ let timer = null;
1392
+ let lastTime = 0;
1393
+ const clearTimer = () => {
1394
+ if (timer) {
1395
+ clearInterval(timer);
1396
+ timer = null;
1397
+ }
1398
+ };
1399
+ const reset = () => {
1400
+ clearTimer();
1401
+ lastTime = 0;
1402
+ };
1403
+ return Object.assign(function (...args) {
1404
+ const now = Date.now();
1405
+ const timeGap = now - lastTime - delay;
1406
+ if (timeGap >= 0) {
1407
+ lastTime = now;
1408
+ clearTimer();
1409
+ }
1410
+ if (timeGap < 0 && trailing && trailingRunLast && timer) {
1411
+ clearTimer();
1412
+ }
1413
+ if (timeGap >= 0 && leading) {
1414
+ fn.apply(this, args);
1415
+ }
1416
+ else if (timeGap >= 0 || (timeGap < 0 && trailing && !timer)) {
1417
+ timer = setTimeout(() => {
1418
+ lastTime = Date.now();
1419
+ timer = null;
1420
+ fn.apply(this, args);
1421
+ }, timeGap >= 0 ? delay : -timeGap);
1422
+ }
1423
+ }, { reset });
1424
+ }
1425
+
1426
+ function debounce(fn, delay, options) {
1427
+ if (!isNumber(delay) || !isFinite(delay) || delay <= 0) {
1428
+ throw new Error('Invalid delay parameter');
1429
+ }
1430
+ return _throttle(fn, delay, Object.assign({ trailing: true, leading: false, trailingRunLast: true }, options));
1431
+ }
1432
+
1284
1433
  function _cloneArray(obj, map, cloner, ...args) {
1285
1434
  const res = obj.slice();
1286
1435
  map.set(obj, res);
@@ -1545,4 +1694,11 @@ function pipe(...pipeFunc) {
1545
1694
  };
1546
1695
  }
1547
1696
 
1548
- export { _, acceptableFileName, acceptableFileType, camelCase, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, compose, _curryMore as curry, deepClone, fastClone, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getTag, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBoolean, isBuffer, isDataView, isDate, isEmpty, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isNil, isNull, isNumber, isObject, isPlaceholder, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, memo, noop, not, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomHexString, randomInt, randomIntFloor, randomString, range, shuffle, sleep, snakeCase, splitWords, titleCase, tryit, ulid, uuidNil, uuidV4, withResolvers };
1697
+ function throttle(fn, delay, options) {
1698
+ if (!isNumber(delay) || !isFinite(delay) || delay <= 0) {
1699
+ throw new Error('Invalid delay parameter');
1700
+ }
1701
+ return _throttle(fn, delay, Object.assign({ trailing: false, leading: true }, options));
1702
+ }
1703
+
1704
+ export { _, acceptableFileName, acceptableFileType, camelCase, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, clamp, compose, _curryMore as curry, debounce, deepClone, defer, fastClone, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getTag, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBoolean, isBuffer, isDataView, isDate, isEmpty, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isNil, isNull, isNumber, isObject, isPlaceholder, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, memo, noop, not, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomHexString, randomInt, randomIntFloor, randomString, range, remove, shuffle, sleep, snakeCase, splitWords, throttle, titleCase, tryit, ulid, uuidNil, uuidV4, withResolvers };
package/lib/index.umd.js CHANGED
@@ -51,12 +51,58 @@ See the Mulan PSL v2 for more details.
51
51
  return res;
52
52
  }
53
53
 
54
- function sleep(time = 1000) {
55
- return new Promise((res) => {
56
- setTimeout(res, time);
57
- });
54
+ function remove(arr, ...item) {
55
+ const removeSet = new Set();
56
+ const judgerList = [];
57
+ for (let i = 0; i < item.length; i++) {
58
+ const cond = item[i];
59
+ if (typeof cond === 'function')
60
+ judgerList.push(cond);
61
+ else
62
+ removeSet.add(cond);
63
+ }
64
+ const res = [];
65
+ for (let i = 0; i < arr.length; i++) {
66
+ const el = arr[i];
67
+ if (removeSet.has(el) || judgerList.some((judger) => judger(el)))
68
+ continue;
69
+ res.push(el);
70
+ }
71
+ return res;
58
72
  }
59
73
 
74
+ /******************************************************************************
75
+ Copyright (c) Microsoft Corporation.
76
+
77
+ Permission to use, copy, modify, and/or distribute this software for any
78
+ purpose with or without fee is hereby granted.
79
+
80
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
81
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
82
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
83
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
84
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
85
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
86
+ PERFORMANCE OF THIS SOFTWARE.
87
+ ***************************************************************************** */
88
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
89
+
90
+
91
+ function __awaiter(thisArg, _arguments, P, generator) {
92
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
93
+ return new (P || (P = Promise))(function (resolve, reject) {
94
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
95
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
96
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
97
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
98
+ });
99
+ }
100
+
101
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
102
+ var e = new Error(message);
103
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
104
+ };
105
+
60
106
  const isArray = Array.isArray;
61
107
 
62
108
  const object2String = Object.prototype.toString;
@@ -155,7 +201,7 @@ See the Mulan PSL v2 for more details.
155
201
  }
156
202
 
157
203
  function isPrimitive(value) {
158
- return value == null || (typeof value !== 'object' && typeof value !== 'function');
204
+ return value === undefined || value === null || (typeof value !== 'object' && typeof value !== 'function');
159
205
  }
160
206
 
161
207
  function isPromise(value) {
@@ -276,6 +322,45 @@ See the Mulan PSL v2 for more details.
276
322
  };
277
323
  }
278
324
 
325
+ function defer(asyncFunction, options) {
326
+ return __awaiter(this, void 0, void 0, function* () {
327
+ const queue = [];
328
+ const { rethrow = false } = options || {};
329
+ const defaultOption = { rethrow };
330
+ const cleanUp = (fn, options) => {
331
+ queue.push({ fn, opt: Object.assign(defaultOption, options) });
332
+ return queue.length - 1;
333
+ };
334
+ const cancelCleanUp = (fnOrIndex) => {
335
+ if (isInteger(fnOrIndex) && fnOrIndex > -1)
336
+ queue[fnOrIndex] = null;
337
+ else if (isFunction(fnOrIndex)) {
338
+ const i = queue.findIndex((item) => item && item.fn === fnOrIndex);
339
+ if (i > -1)
340
+ queue[i] = null;
341
+ }
342
+ };
343
+ const [err, res] = yield tryit(asyncFunction)(cleanUp, cancelCleanUp);
344
+ for (const item of queue) {
345
+ if (!item)
346
+ continue;
347
+ const { fn, opt } = item;
348
+ const [cleanUpErr] = yield tryit(fn)(err);
349
+ if (cleanUpErr && opt.rethrow)
350
+ throw cleanUpErr;
351
+ }
352
+ if (err)
353
+ throw err;
354
+ return res;
355
+ });
356
+ }
357
+
358
+ function sleep(time = 1000) {
359
+ return new Promise((res) => {
360
+ setTimeout(res, time);
361
+ });
362
+ }
363
+
279
364
  const noop = function noop() { };
280
365
 
281
366
  function withResolvers(PromiseLike = Promise) {
@@ -289,6 +374,23 @@ See the Mulan PSL v2 for more details.
289
374
  return { promise, resolve, reject };
290
375
  }
291
376
 
377
+ function clamp(num, min, max, options) {
378
+ var _a, _b;
379
+ if (isNaN(min))
380
+ throw new Error('Invalid min parameter');
381
+ if (isNaN(max))
382
+ throw new Error('Invalid max parameter');
383
+ if (max < min) {
384
+ [min, max] = [max, min];
385
+ }
386
+ const { default: def, defaultMin: _dMin, defaultMax: _dMax } = options || {};
387
+ const defaultMin = (_a = _dMin !== null && _dMin !== void 0 ? _dMin : def) !== null && _a !== void 0 ? _a : min;
388
+ const defaultMax = (_b = _dMax !== null && _dMax !== void 0 ? _dMax : def) !== null && _b !== void 0 ? _b : max;
389
+ if (isNaN(num))
390
+ return defaultMin;
391
+ return num < min ? defaultMin : num > max ? defaultMax : num;
392
+ }
393
+
292
394
  const mimeMap = {
293
395
  application: {
294
396
  acrobat: ['pdf'],
@@ -1287,6 +1389,53 @@ See the Mulan PSL v2 for more details.
1287
1389
  }
1288
1390
  }
1289
1391
 
1392
+ function _throttle(fn, delay, options) {
1393
+ var _a, _b, _c;
1394
+ const trailing = (_a = options === null || options === void 0 ? void 0 : options.trailing) !== null && _a !== void 0 ? _a : false;
1395
+ const trailingRunLast = (_b = options === null || options === void 0 ? void 0 : options.trailingRunLast) !== null && _b !== void 0 ? _b : true;
1396
+ const leading = (_c = options === null || options === void 0 ? void 0 : options.leading) !== null && _c !== void 0 ? _c : true;
1397
+ let timer = null;
1398
+ let lastTime = 0;
1399
+ const clearTimer = () => {
1400
+ if (timer) {
1401
+ clearInterval(timer);
1402
+ timer = null;
1403
+ }
1404
+ };
1405
+ const reset = () => {
1406
+ clearTimer();
1407
+ lastTime = 0;
1408
+ };
1409
+ return Object.assign(function (...args) {
1410
+ const now = Date.now();
1411
+ const timeGap = now - lastTime - delay;
1412
+ if (timeGap >= 0) {
1413
+ lastTime = now;
1414
+ clearTimer();
1415
+ }
1416
+ if (timeGap < 0 && trailing && trailingRunLast && timer) {
1417
+ clearTimer();
1418
+ }
1419
+ if (timeGap >= 0 && leading) {
1420
+ fn.apply(this, args);
1421
+ }
1422
+ else if (timeGap >= 0 || (timeGap < 0 && trailing && !timer)) {
1423
+ timer = setTimeout(() => {
1424
+ lastTime = Date.now();
1425
+ timer = null;
1426
+ fn.apply(this, args);
1427
+ }, timeGap >= 0 ? delay : -timeGap);
1428
+ }
1429
+ }, { reset });
1430
+ }
1431
+
1432
+ function debounce(fn, delay, options) {
1433
+ if (!isNumber(delay) || !isFinite(delay) || delay <= 0) {
1434
+ throw new Error('Invalid delay parameter');
1435
+ }
1436
+ return _throttle(fn, delay, Object.assign({ trailing: true, leading: false, trailingRunLast: true }, options));
1437
+ }
1438
+
1290
1439
  function _cloneArray(obj, map, cloner, ...args) {
1291
1440
  const res = obj.slice();
1292
1441
  map.set(obj, res);
@@ -1551,6 +1700,13 @@ See the Mulan PSL v2 for more details.
1551
1700
  };
1552
1701
  }
1553
1702
 
1703
+ function throttle(fn, delay, options) {
1704
+ if (!isNumber(delay) || !isFinite(delay) || delay <= 0) {
1705
+ throw new Error('Invalid delay parameter');
1706
+ }
1707
+ return _throttle(fn, delay, Object.assign({ trailing: false, leading: true }, options));
1708
+ }
1709
+
1554
1710
  exports._ = _;
1555
1711
  exports.acceptableFileName = acceptableFileName;
1556
1712
  exports.acceptableFileType = acceptableFileType;
@@ -1560,9 +1716,12 @@ See the Mulan PSL v2 for more details.
1560
1716
  exports.caseKebab = caseKebab;
1561
1717
  exports.casePascal = casePascal;
1562
1718
  exports.caseSnake = caseSnake;
1719
+ exports.clamp = clamp;
1563
1720
  exports.compose = compose;
1564
1721
  exports.curry = _curryMore;
1722
+ exports.debounce = debounce;
1565
1723
  exports.deepClone = deepClone;
1724
+ exports.defer = defer;
1566
1725
  exports.fastClone = fastClone;
1567
1726
  exports.getAcceptableExtByMIME = getAcceptableExtByMIME;
1568
1727
  exports.getAcceptableMIMEByExt = getAcceptableMIMEByExt;
@@ -1631,10 +1790,12 @@ See the Mulan PSL v2 for more details.
1631
1790
  exports.randomIntFloor = randomIntFloor;
1632
1791
  exports.randomString = randomString;
1633
1792
  exports.range = range;
1793
+ exports.remove = remove;
1634
1794
  exports.shuffle = shuffle;
1635
1795
  exports.sleep = sleep;
1636
1796
  exports.snakeCase = snakeCase;
1637
1797
  exports.splitWords = splitWords;
1798
+ exports.throttle = throttle;
1638
1799
  exports.titleCase = titleCase;
1639
1800
  exports.tryit = tryit;
1640
1801
  exports.ulid = ulid;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreslash",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Foreslash is a Javascript utilities lib which contains plenty of practical functions.",
5
5
  "author": "moushu",
6
6
  "license": "Mulan PSL v2",
@@ -27,7 +27,10 @@
27
27
  },
28
28
  "files": [
29
29
  "lib",
30
- "README.md"
30
+ "README.EN.md",
31
+ "CHANGELOG.md",
32
+ "README.md",
33
+ "LICENSE"
31
34
  ],
32
35
  "scripts": {
33
36
  "dev": "rollup -c -w",