@bodil/monkey-business 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/array.d.ts +59 -0
- package/dist/array.js +100 -0
- package/dist/array.js.map +1 -0
- package/dist/index.d.ts +35 -142
- package/dist/index.js +37 -185
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +25 -3
- package/dist/index.test.js.map +1 -1
- package/dist/iterator.d.ts +78 -0
- package/dist/iterator.js +176 -0
- package/dist/iterator.js.map +1 -0
- package/dist/map.d.ts +19 -0
- package/dist/map.js +31 -0
- package/dist/map.js.map +1 -0
- package/dist/object.d.ts +12 -0
- package/dist/object.js +17 -0
- package/dist/object.js.map +1 -0
- package/dist/weakmap.d.ts +19 -0
- package/dist/weakmap.js +31 -0
- package/dist/weakmap.js.map +1 -0
- package/package.json +63 -11
- package/src/array.ts +128 -0
- package/src/es-iterator-helpers.d.ts +24 -0
- package/src/index.test.ts +30 -7
- package/src/index.ts +77 -390
- package/src/iterator.ts +223 -0
- package/src/map.ts +37 -0
- package/src/object.ts +21 -0
- package/src/weakmap.ts +37 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bodil/monkey-business",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "Let's give JS core types a polish",
|
|
5
5
|
"homepage": "https://codeberg.org/bodil/monkey-business",
|
|
6
6
|
"repository": {
|
|
@@ -20,6 +20,36 @@
|
|
|
20
20
|
"types": "./dist/index.d.ts",
|
|
21
21
|
"import": "./dist/index.js"
|
|
22
22
|
}
|
|
23
|
+
},
|
|
24
|
+
"./array": {
|
|
25
|
+
"import": {
|
|
26
|
+
"types": "./dist/array.d.ts",
|
|
27
|
+
"import": "./dist/array.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"./iterator": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/iterator.d.ts",
|
|
33
|
+
"import": "./dist/iterator.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"./map": {
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/map.d.ts",
|
|
39
|
+
"import": "./dist/map.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"./object": {
|
|
43
|
+
"import": {
|
|
44
|
+
"types": "./dist/object.d.ts",
|
|
45
|
+
"import": "./dist/object.js"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"./weakmap": {
|
|
49
|
+
"import": {
|
|
50
|
+
"types": "./dist/weakmap.d.ts",
|
|
51
|
+
"import": "./dist/weakmap.js"
|
|
52
|
+
}
|
|
23
53
|
}
|
|
24
54
|
},
|
|
25
55
|
"publishConfig": {
|
|
@@ -37,22 +67,44 @@
|
|
|
37
67
|
"@bodil/opt-vitest": "^1.1.0",
|
|
38
68
|
"@eslint/eslintrc": "^3.3.7",
|
|
39
69
|
"@eslint/js": "^10.0.1",
|
|
40
|
-
"@
|
|
41
|
-
"@typescript-eslint/
|
|
70
|
+
"@ianvs/prettier-plugin-sort-imports": "^4.7.1",
|
|
71
|
+
"@typescript-eslint/eslint-plugin": "^8.72.2",
|
|
72
|
+
"@typescript-eslint/parser": "^8.72.2",
|
|
42
73
|
"@typhonjs-typedoc/ts-lib-docs": "^2024.12.25",
|
|
43
|
-
"eslint": "^10.
|
|
74
|
+
"eslint": "^10.14.0",
|
|
44
75
|
"eslint-config-prettier": "^10.1.8",
|
|
45
|
-
"eslint-plugin-jsdoc": "^64.
|
|
46
|
-
"globals": "^17.
|
|
47
|
-
"npm-run-all2": "^9.0.
|
|
48
|
-
"prettier": "^3.9.
|
|
76
|
+
"eslint-plugin-jsdoc": "^64.5.4",
|
|
77
|
+
"globals": "^17.14.0",
|
|
78
|
+
"npm-run-all2": "^9.0.4",
|
|
79
|
+
"prettier": "^3.9.10",
|
|
49
80
|
"typedoc": "^0.28.20",
|
|
50
81
|
"typedoc-plugin-extras": "^4.0.1",
|
|
51
82
|
"typedoc-plugin-mdn-links": "^5.1.1",
|
|
52
83
|
"typedoc-theme-fresh": "^0.2.3",
|
|
53
84
|
"typescript": "^6.0.3",
|
|
54
|
-
"vite": "^7.3.
|
|
55
|
-
"vitest": "^4.1.
|
|
85
|
+
"vite": "^7.3.8",
|
|
86
|
+
"vitest": "^4.1.12"
|
|
87
|
+
},
|
|
88
|
+
"prettier": {
|
|
89
|
+
"editorconfig": true,
|
|
90
|
+
"plugins": [
|
|
91
|
+
"@ianvs/prettier-plugin-sort-imports"
|
|
92
|
+
],
|
|
93
|
+
"importOrderParserPlugins": [
|
|
94
|
+
"typescript",
|
|
95
|
+
"decorators"
|
|
96
|
+
],
|
|
97
|
+
"importOrderTypeScriptVersion": "5.0.0",
|
|
98
|
+
"importOrder": [
|
|
99
|
+
"",
|
|
100
|
+
"<BUILTIN_MODULES>",
|
|
101
|
+
"",
|
|
102
|
+
"<THIRD_PARTY_MODULES>",
|
|
103
|
+
"",
|
|
104
|
+
"^[.](?!.*[?]inline$)",
|
|
105
|
+
"",
|
|
106
|
+
"[?]inline$"
|
|
107
|
+
]
|
|
56
108
|
},
|
|
57
109
|
"scripts": {
|
|
58
110
|
"build": "tsc",
|
|
@@ -62,6 +114,6 @@
|
|
|
62
114
|
"check": "run-p check:tsc check:eslint",
|
|
63
115
|
"doc": "typedoc",
|
|
64
116
|
"doc:readthedocs": "typedoc --out $READTHEDOCS_OUTPUT/html",
|
|
65
|
-
"prepublish": "
|
|
117
|
+
"prepublish": "rm -rf dist && run-p check test build"
|
|
66
118
|
}
|
|
67
119
|
}
|
package/src/array.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { bisectHigh, bisectLow, type OrderFn } from "@bodil/core/order";
|
|
2
|
+
import type { Present } from "@bodil/core/types";
|
|
3
|
+
import { None, Option, type Some } from "@bodil/opt";
|
|
4
|
+
|
|
5
|
+
import { get as objectGet } from "./object";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Insert `value` at `index` in the array.
|
|
9
|
+
*/
|
|
10
|
+
export function insert<T>(this: Array<T>, value: T, index: number): Array<T> {
|
|
11
|
+
if (!Number.isInteger(index)) {
|
|
12
|
+
throw new TypeError(`Array.insert(): index ${index} is not an integer`);
|
|
13
|
+
}
|
|
14
|
+
if (index > this.length || index < 0) {
|
|
15
|
+
throw new RangeError(`Array.insert: index ${index} out of bounds`);
|
|
16
|
+
}
|
|
17
|
+
this.splice(index, 0, value);
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Insert `value` into the array at the correct position according to
|
|
23
|
+
* the ordering function `cmp`. The array must already be ordered
|
|
24
|
+
* accordingly.
|
|
25
|
+
*
|
|
26
|
+
* If `duplicates` is `"low"`, the correct position in case of
|
|
27
|
+
* duplicates is considered to be the index of the first occurrence of a
|
|
28
|
+
* duplicate. If it is `"high"`, which is the default, the correct
|
|
29
|
+
* position is considered to be the index after the last occurrence of a
|
|
30
|
+
* duplicate.
|
|
31
|
+
*/
|
|
32
|
+
export function insertOrdered<T>(
|
|
33
|
+
this: Array<T>,
|
|
34
|
+
value: T,
|
|
35
|
+
cmp: OrderFn<T>,
|
|
36
|
+
duplicates: "low" | "high" = "high",
|
|
37
|
+
): Array<T> {
|
|
38
|
+
return (insert<T>).call(
|
|
39
|
+
this,
|
|
40
|
+
value,
|
|
41
|
+
(duplicates === "low" ? bisectLow : bisectHigh)(cmp, this, value),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Remove the first occurrence of a given value from an array.
|
|
47
|
+
*
|
|
48
|
+
* Returns an {@link Option} containing the removed value, or {@link None} if
|
|
49
|
+
* no matches were found.
|
|
50
|
+
*/
|
|
51
|
+
export function remove<T>(this: Array<T>, value: T): Option<T> {
|
|
52
|
+
return (removeIndex<T>).call(this, this.indexOf(value));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Remove the first value for which the provided predicate function
|
|
57
|
+
* returns `true` from the array.
|
|
58
|
+
*
|
|
59
|
+
* Returns an {@link Option} containing the removed value, or {@link None} if
|
|
60
|
+
* no matches were found.
|
|
61
|
+
*/
|
|
62
|
+
export function removeFn<T>(
|
|
63
|
+
this: Array<T>,
|
|
64
|
+
predicate: (value: T, index: number, obj: Array<T>) => boolean,
|
|
65
|
+
): Option<T> {
|
|
66
|
+
return (removeIndex<T>).call(this, this.findIndex(predicate));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Remove the value at the given index from the array.
|
|
71
|
+
*
|
|
72
|
+
* Returns an {@link Option} containing the removed value, or {@link None} if
|
|
73
|
+
* the index was out of bounds.
|
|
74
|
+
*/
|
|
75
|
+
export function removeIndex<T>(this: Array<T>, index: number): Option<T> {
|
|
76
|
+
if (!Number.isInteger(index)) {
|
|
77
|
+
throw new TypeError(`Array.removeIndex(): index ${index} is not an integer`);
|
|
78
|
+
}
|
|
79
|
+
if (index < 0 || index >= this.length) {
|
|
80
|
+
return None;
|
|
81
|
+
}
|
|
82
|
+
const removed = this.splice(index, 1).pop();
|
|
83
|
+
return Option.from(removed);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Return a copy of the array with any `undefined` values removed.
|
|
88
|
+
*/
|
|
89
|
+
export function present<T>(this: Array<T>): Array<Present<T>> {
|
|
90
|
+
return this.filter((value: T): value is Present<T> => value !== undefined);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Return a copy of the array with any `null` or `undefined` values removed.
|
|
95
|
+
*/
|
|
96
|
+
export function nonNullable<T>(this: Array<T>): Array<NonNullable<T>> {
|
|
97
|
+
return this.filter(
|
|
98
|
+
(value: T): value is NonNullable<T> => value !== undefined && value !== null,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Checked index operator. Returns {@link None} if an index isn't
|
|
104
|
+
* present, even for sparse arrays. Returns a {@link Some} if the index
|
|
105
|
+
* is present, even if the value is `undefined`.
|
|
106
|
+
*/
|
|
107
|
+
export function get<T>(this: Array<T>, index: number): Option<T> {
|
|
108
|
+
return objectGet(this, index);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Test whether a value is an array with the same contents as this one.
|
|
113
|
+
*/
|
|
114
|
+
export function equals<T, U = unknown>(
|
|
115
|
+
this: Array<T>,
|
|
116
|
+
other: unknown,
|
|
117
|
+
equalsFn: (a: T, b: U) => boolean = Object.is,
|
|
118
|
+
): other is Array<T> {
|
|
119
|
+
if (!Array.isArray(other) || this.length !== other.length) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
for (let index = 0; index < this.length; index++) {
|
|
123
|
+
if (!equalsFn(this[index], other[index])) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
declare module "es-iterator-helpers/Iterator.concat/polyfill" {
|
|
2
|
+
function getPolyfill(): <T>(...inputs: Array<Iterable<T>>) => IteratorObject<T>;
|
|
3
|
+
export default getPolyfill;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare module "es-iterator-helpers/Iterator.zip/polyfill" {
|
|
7
|
+
function getPolyfill(): <T>(inputs: Iterable<Iterable<T>>) => IteratorObject<Array<T>>;
|
|
8
|
+
export default getPolyfill;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module "es-iterator-helpers/Iterator.zipKeyed/polyfill" {
|
|
12
|
+
function getPolyfill(): <T>(
|
|
13
|
+
inputs: Record<string, Iterable<T>>,
|
|
14
|
+
) => IteratorObject<Record<string, T>>;
|
|
15
|
+
export default getPolyfill;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare module "es-iterator-helpers/Iterator.prototype.chunks/polyfill" {
|
|
19
|
+
function getPolyfill(): <T>(
|
|
20
|
+
this: Iterator<T>,
|
|
21
|
+
size: number,
|
|
22
|
+
) => IteratorObject<Array<T>, undefined>;
|
|
23
|
+
export default getPolyfill;
|
|
24
|
+
}
|
package/src/index.test.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { test, expect, expectTypeOf } from "vitest";
|
|
2
|
-
import type { Option } from "@bodil/opt";
|
|
3
1
|
import { order as Order } from "@bodil/core";
|
|
2
|
+
import type { Option } from "@bodil/opt";
|
|
3
|
+
import { expect, expectTypeOf, test } from "vitest";
|
|
4
4
|
|
|
5
5
|
import ".";
|
|
6
|
-
|
|
7
6
|
import "@bodil/opt-vitest";
|
|
8
7
|
|
|
9
8
|
const Iterator = globalThis.Iterator;
|
|
@@ -52,6 +51,22 @@ test("Map.getOrSet", () => {
|
|
|
52
51
|
expect(map.getOrInsertComputed("bar", defaultValue)).toEqual("Robert");
|
|
53
52
|
expect(map.getOrInsertComputed("Mike", defaultValueCat)).toEqual("RobertMike");
|
|
54
53
|
expect(map.get("bar")).toEqual("Robert");
|
|
54
|
+
expect(map.getOrSet("foo", defaultValue)).toEqual("Joe");
|
|
55
|
+
expect(map.getOrSet("baz", defaultValue)).toEqual("Robert");
|
|
56
|
+
expect(map.get("baz")).toEqual("Robert");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("WeakMap.getOrSet", () => {
|
|
60
|
+
const defaultValue = () => "Robert";
|
|
61
|
+
const weakmap = new WeakMap();
|
|
62
|
+
const foo = {};
|
|
63
|
+
const bar = {};
|
|
64
|
+
weakmap.set(foo, "Joe");
|
|
65
|
+
expect(weakmap.getOrInsertComputed(foo, defaultValue)).toEqual("Joe");
|
|
66
|
+
expect(weakmap.getOrInsertComputed(bar, defaultValue)).toEqual("Robert");
|
|
67
|
+
expect(weakmap.getOrSet(foo, defaultValue)).toEqual("Joe");
|
|
68
|
+
expect(weakmap.getOrSet(bar, defaultValue)).toEqual("Robert");
|
|
69
|
+
expect(weakmap.get(bar)).toEqual("Robert");
|
|
55
70
|
});
|
|
56
71
|
|
|
57
72
|
test("Map.remove", () => {
|
|
@@ -102,6 +117,14 @@ test("Array.filterPresent", () => {
|
|
|
102
117
|
expectTypeOf(cleanArray).toExtend<Array<number>>();
|
|
103
118
|
});
|
|
104
119
|
|
|
120
|
+
test("Array.filterNonNullable", () => {
|
|
121
|
+
const array: Array<number | null | undefined> = [1, null, 2, undefined, 3, null, 4];
|
|
122
|
+
const cleanArray = array.nonNullable();
|
|
123
|
+
expect(cleanArray.some((v) => v === undefined || v === null)).toBeFalsy();
|
|
124
|
+
expect(cleanArray).toEqual([1, 2, 3, 4]);
|
|
125
|
+
expectTypeOf(cleanArray).toExtend<Array<number>>();
|
|
126
|
+
});
|
|
127
|
+
|
|
105
128
|
test("Iterator.chunks", () => {
|
|
106
129
|
expect(Array.from(Iterator.from([1, 2, 3, 4]).chunks(2))).toEqual([
|
|
107
130
|
[1, 2],
|
|
@@ -112,12 +135,12 @@ test("Iterator.chunks", () => {
|
|
|
112
135
|
});
|
|
113
136
|
|
|
114
137
|
test("Iterator.partition", () => {
|
|
115
|
-
expect(Array.from(Iterator.from([1, 2, 3, 4]).
|
|
138
|
+
expect(Array.from(Iterator.from([1, 2, 3, 4]).chunks(2))).toEqual([
|
|
116
139
|
[1, 2],
|
|
117
140
|
[3, 4],
|
|
118
141
|
]);
|
|
119
142
|
|
|
120
|
-
expect(Array.from(Iterator.from([1, 2, 3]).
|
|
143
|
+
expect(Array.from(Iterator.from([1, 2, 3]).chunks(2))).toEqual([[1, 2], [3]]);
|
|
121
144
|
});
|
|
122
145
|
|
|
123
146
|
test("Iterator.frontBiasedPartition", () => {
|
|
@@ -170,8 +193,8 @@ test("Iterator.equals", () => {
|
|
|
170
193
|
expect(
|
|
171
194
|
Iterator.from([1, 2, 3]).equals(
|
|
172
195
|
["1", "2", "3"],
|
|
173
|
-
(a: number, b: string) => a.toString() === b
|
|
174
|
-
)
|
|
196
|
+
(a: number, b: string) => a.toString() === b,
|
|
197
|
+
),
|
|
175
198
|
).toBeTruthy();
|
|
176
199
|
});
|
|
177
200
|
|