@byloth/core 2.2.3 → 2.2.4
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/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.esm.js +485 -209
- package/dist/core.esm.js.map +1 -1
- package/dist/core.global.js +1 -1
- package/dist/core.global.js.map +1 -1
- package/dist/core.umd.cjs +1 -1
- package/dist/core.umd.cjs.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +3 -1
- package/src/models/collections/array-view.ts +348 -0
- package/src/models/collections/index.ts +1 -0
- package/src/models/collections/map-view.ts +5 -3
- package/src/models/collections/set-view.ts +6 -4
- package/src/models/exceptions/index.ts +51 -10
- package/src/models/index.ts +2 -1
- package/src/utils/random.ts +126 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@byloth/core",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "An unopinionated collection of useful functions and classes that I use widely in all my projects. 🔧",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Core",
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
"types": "src/index.ts",
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@byloth/eslint-config-typescript": "^3.2.2",
|
|
53
|
-
"@eslint/compat": "^2.0.
|
|
54
|
-
"@types/node": "^22.19.
|
|
55
|
-
"@vitest/coverage-v8": "^4.0.
|
|
53
|
+
"@eslint/compat": "^2.0.2",
|
|
54
|
+
"@types/node": "^22.19.11",
|
|
55
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
56
56
|
"eslint": "^9.39.2",
|
|
57
57
|
"husky": "^9.1.7",
|
|
58
58
|
"jsdom": "^27.4.0",
|
|
59
59
|
"typescript": "^5.9.3",
|
|
60
|
-
"vite": "^7.3.
|
|
61
|
-
"vitest": "^4.0.
|
|
60
|
+
"vite": "^7.3.1",
|
|
61
|
+
"vitest": "^4.0.18"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"dev": "vite",
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const VERSION = "2.2.
|
|
1
|
+
export const VERSION = "2.2.4";
|
|
2
2
|
|
|
3
3
|
export type { Constructor, Interval, Timeout, ValueOf } from "./core/types.js";
|
|
4
4
|
|
|
@@ -6,6 +6,7 @@ export { isBrowser, isNode, isWorker } from "./helpers.js";
|
|
|
6
6
|
export {
|
|
7
7
|
AggregatedIterator,
|
|
8
8
|
AggregatedAsyncIterator,
|
|
9
|
+
ArrayView,
|
|
9
10
|
CallableObject,
|
|
10
11
|
CallbackChain,
|
|
11
12
|
Clock,
|
|
@@ -29,6 +30,7 @@ export {
|
|
|
29
30
|
RangeException,
|
|
30
31
|
ReducedIterator,
|
|
31
32
|
ReferenceException,
|
|
33
|
+
ResponseException,
|
|
32
34
|
RuntimeException,
|
|
33
35
|
SetView,
|
|
34
36
|
SmartIterator,
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import Publisher from "../callbacks/publisher.js";
|
|
2
|
+
import type { Callback } from "../types.js";
|
|
3
|
+
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
import type MapView from "./map-view.js";
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
|
+
import type SetView from "./set-view.js";
|
|
8
|
+
|
|
9
|
+
interface ArrayViewEventsMap<T>
|
|
10
|
+
{
|
|
11
|
+
"add": (value: T, index: number) => void;
|
|
12
|
+
"remove": (value: T, index: number) => void;
|
|
13
|
+
|
|
14
|
+
"clear": () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A wrapper class around the native {@link Array} class that provides additional functionality
|
|
19
|
+
* for publishing events when entries are added, removed or the collection is cleared.
|
|
20
|
+
* There are also complementary classes that work with the native `Map` and `Set` classes.
|
|
21
|
+
* See also {@link MapView} and {@link SetView}.
|
|
22
|
+
*
|
|
23
|
+
* ---
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const array = new ArrayView<number>();
|
|
28
|
+
*
|
|
29
|
+
* array.onAdd((value: number, index: number) => console.log(`Added ${value} at index ${index}`));
|
|
30
|
+
* array.push(42); // "Added 42 at index 0"
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ---
|
|
34
|
+
*
|
|
35
|
+
* @template T The type of the values in the array.
|
|
36
|
+
*/
|
|
37
|
+
export default class ArrayView<T> extends Array<T>
|
|
38
|
+
{
|
|
39
|
+
/**
|
|
40
|
+
* The internal {@link Publisher} instance used to publish events.
|
|
41
|
+
*/
|
|
42
|
+
protected readonly _publisher: Publisher<ArrayViewEventsMap<T>>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Initializes a new instance of the {@link ArrayView} class.
|
|
46
|
+
*
|
|
47
|
+
* ---
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* const array = new ArrayView<number>();
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
public constructor();
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Initializes a new instance of the {@link ArrayView} class.
|
|
58
|
+
*
|
|
59
|
+
* ---
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const array = new ArrayView<number>(3);
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* ---
|
|
67
|
+
*
|
|
68
|
+
* @param length The initial length of the {@link Array}.
|
|
69
|
+
*/
|
|
70
|
+
public constructor(length: number);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Initializes a new instance of the {@link ArrayView} class.
|
|
74
|
+
*
|
|
75
|
+
* ---
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const array = new ArrayView<number>(2, 4, 8);
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* ---
|
|
83
|
+
*
|
|
84
|
+
* @param items The items to initialize the {@link Array} with.
|
|
85
|
+
*/
|
|
86
|
+
public constructor(...items: T[]);
|
|
87
|
+
public constructor(...items: T[])
|
|
88
|
+
{
|
|
89
|
+
super(...items);
|
|
90
|
+
|
|
91
|
+
this._publisher = new Publisher();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Appends new elements to the end of the {@link Array} and returns the new length of the array.
|
|
96
|
+
*
|
|
97
|
+
* ---
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* const array = new ArrayView<number>();
|
|
102
|
+
* array.push(2, 4, 8);
|
|
103
|
+
*
|
|
104
|
+
* console.log(array); // ArrayView(3) [2, 4, 8]
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* ---
|
|
108
|
+
*
|
|
109
|
+
* @param items New elements to add to the array.
|
|
110
|
+
*
|
|
111
|
+
* @returns The new length of the array.
|
|
112
|
+
*/
|
|
113
|
+
public override push(...items: T[]): number
|
|
114
|
+
{
|
|
115
|
+
const startIndex = this.length;
|
|
116
|
+
|
|
117
|
+
const result = super.push(...items);
|
|
118
|
+
for (let i = 0; i < items.length; i += 1)
|
|
119
|
+
{
|
|
120
|
+
this._publisher.publish("add", items[i], startIndex + i);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Removes the last element from the {@link Array} and returns it.
|
|
128
|
+
*
|
|
129
|
+
* ---
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* const array = new ArrayView<number>(2, 4, 8);
|
|
134
|
+
* array.pop(); // 8
|
|
135
|
+
*
|
|
136
|
+
* console.log(array); // ArrayView(2) [2, 4]
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* ---
|
|
140
|
+
*
|
|
141
|
+
* @returns The removed element, or `undefined` if the array is empty.
|
|
142
|
+
*/
|
|
143
|
+
public override pop(): T | undefined
|
|
144
|
+
{
|
|
145
|
+
const index = this.length - 1;
|
|
146
|
+
if (index < 0) { return undefined; }
|
|
147
|
+
|
|
148
|
+
const value = super.pop();
|
|
149
|
+
this._publisher.publish("remove", value!, index);
|
|
150
|
+
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Removes the first element from the {@link Array} and returns it.
|
|
156
|
+
*
|
|
157
|
+
* ---
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```ts
|
|
161
|
+
* const array = new ArrayView<number>(2, 4, 8);
|
|
162
|
+
* array.shift(); // 2
|
|
163
|
+
*
|
|
164
|
+
* console.log(array); // ArrayView(2) [4, 8]
|
|
165
|
+
* ```
|
|
166
|
+
*
|
|
167
|
+
* ---
|
|
168
|
+
*
|
|
169
|
+
* @returns The removed element, or `undefined` if the array is empty.
|
|
170
|
+
*/
|
|
171
|
+
public override shift(): T | undefined
|
|
172
|
+
{
|
|
173
|
+
if (this.length === 0) { return undefined; }
|
|
174
|
+
|
|
175
|
+
const value = super.shift();
|
|
176
|
+
this._publisher.publish("remove", value!, 0);
|
|
177
|
+
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Inserts new elements at the start of the {@link Array} and returns the new length of the array.
|
|
183
|
+
*
|
|
184
|
+
* ---
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* const array = new ArrayView<number>(4, 8);
|
|
189
|
+
* array.unshift(2);
|
|
190
|
+
*
|
|
191
|
+
* console.log(array); // ArrayView(3) [2, 4, 8]
|
|
192
|
+
* ```
|
|
193
|
+
*
|
|
194
|
+
* ---
|
|
195
|
+
*
|
|
196
|
+
* @param items Elements to insert at the start of the array.
|
|
197
|
+
*
|
|
198
|
+
* @returns The new length of the array.
|
|
199
|
+
*/
|
|
200
|
+
public override unshift(...items: T[]): number
|
|
201
|
+
{
|
|
202
|
+
const result = super.unshift(...items);
|
|
203
|
+
for (let i = 0; i < items.length; i += 1)
|
|
204
|
+
{
|
|
205
|
+
this._publisher.publish("add", items[i], i);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Removes elements from the {@link Array} and, if necessary, inserts new elements in their place,
|
|
213
|
+
* returning the deleted elements.
|
|
214
|
+
*
|
|
215
|
+
* ---
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```ts
|
|
219
|
+
* const array = new ArrayView<number>(2, 4, 8, 16);
|
|
220
|
+
* array.splice(1, 2, 32, 64); // [4, 8]
|
|
221
|
+
*
|
|
222
|
+
* console.log(array); // ArrayView(4) [2, 32, 64, 16]
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* ---
|
|
226
|
+
*
|
|
227
|
+
* @param start The zero-based location in the array from which to start removing elements.
|
|
228
|
+
* @param deleteCount The number of elements to remove.
|
|
229
|
+
* @param items Elements to insert into the array in place of the deleted elements.
|
|
230
|
+
*
|
|
231
|
+
* @returns An array containing the elements that were deleted.
|
|
232
|
+
*/
|
|
233
|
+
public override splice(start: number, deleteCount?: number, ...items: T[]): T[]
|
|
234
|
+
{
|
|
235
|
+
const normalizedStart = start < 0 ? Math.max(this.length + start, 0) : Math.min(start, this.length);
|
|
236
|
+
|
|
237
|
+
const actualDeleteCount = deleteCount === undefined ?
|
|
238
|
+
this.length - normalizedStart :
|
|
239
|
+
Math.min(Math.max(deleteCount, 0), this.length - normalizedStart);
|
|
240
|
+
|
|
241
|
+
const removed = super.splice(start, actualDeleteCount, ...items);
|
|
242
|
+
for (let i = 0; i < removed.length; i += 1)
|
|
243
|
+
{
|
|
244
|
+
this._publisher.publish("remove", removed[i], normalizedStart + i);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (let i = 0; i < items.length; i += 1)
|
|
248
|
+
{
|
|
249
|
+
this._publisher.publish("add", items[i], normalizedStart + i);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return removed;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Removes all elements from the {@link Array}.
|
|
257
|
+
*
|
|
258
|
+
* ---
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* ```ts
|
|
262
|
+
* const array = new ArrayView<number>(2, 4, 8);
|
|
263
|
+
* array.clear();
|
|
264
|
+
*
|
|
265
|
+
* console.log(array); // ArrayView(0) []
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
public clear(): void
|
|
269
|
+
{
|
|
270
|
+
const length = this.length;
|
|
271
|
+
this.length = 0;
|
|
272
|
+
|
|
273
|
+
if (length > 0) { this._publisher.publish("clear"); }
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Subscribes to the `add` event of the array with a callback that will be executed when an element is added.
|
|
278
|
+
*
|
|
279
|
+
* ---
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* array.onAdd((value, index) => console.log(`Added ${value} at index ${index}`));
|
|
284
|
+
*
|
|
285
|
+
* array.push(2); // "Added 2 at index 0"
|
|
286
|
+
* array.push(42); // "Added 42 at index 1"
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* ---
|
|
290
|
+
*
|
|
291
|
+
* @param callback The callback that will be executed when an element is added to the array.
|
|
292
|
+
*
|
|
293
|
+
* @returns A function that can be used to unsubscribe from the event.
|
|
294
|
+
*/
|
|
295
|
+
public onAdd(callback: (value: T, index: number) => void): Callback
|
|
296
|
+
{
|
|
297
|
+
return this._publisher.subscribe("add", callback);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Subscribes to the `remove` event of the array with a callback that will be executed when an element is removed.
|
|
302
|
+
*
|
|
303
|
+
* ---
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```ts
|
|
307
|
+
* array.onRemove((value, index) => console.log(`Removed ${value} at index ${index}`));
|
|
308
|
+
*
|
|
309
|
+
* array.pop(); // "Removed 8 at index 2"
|
|
310
|
+
* array.shift(); // "Removed 2 at index 0"
|
|
311
|
+
* ```
|
|
312
|
+
*
|
|
313
|
+
* ---
|
|
314
|
+
*
|
|
315
|
+
* @param callback The callback that will be executed when an element is removed from the array.
|
|
316
|
+
*
|
|
317
|
+
* @returns A function that can be used to unsubscribe from the event.
|
|
318
|
+
*/
|
|
319
|
+
public onRemove(callback: (value: T, index: number) => void): Callback
|
|
320
|
+
{
|
|
321
|
+
return this._publisher.subscribe("remove", callback);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Subscribes to the `clear` event of the array with a callback that will be executed when the array is cleared.
|
|
326
|
+
*
|
|
327
|
+
* ---
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```ts
|
|
331
|
+
* array.onClear(() => console.log("The array has been cleared."));
|
|
332
|
+
* array.clear(); // "The array has been cleared."
|
|
333
|
+
* ```
|
|
334
|
+
*
|
|
335
|
+
* ---
|
|
336
|
+
*
|
|
337
|
+
* @param callback The callback that will be executed when the array is cleared.
|
|
338
|
+
*
|
|
339
|
+
* @returns A function that can be used to unsubscribe from the event.
|
|
340
|
+
*/
|
|
341
|
+
public onClear(callback: () => void): Callback
|
|
342
|
+
{
|
|
343
|
+
return this._publisher.subscribe("clear", callback);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
public readonly [Symbol.toStringTag]: string = "ArrayView";
|
|
347
|
+
public static override readonly [Symbol.species]: typeof Array = Array;
|
|
348
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Publisher from "../callbacks/publisher.js";
|
|
2
2
|
import type { Callback } from "../types.js";
|
|
3
3
|
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
import type ArrayView from "./array-view.js";
|
|
4
6
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
7
|
import type SetView from "./set-view.js";
|
|
6
8
|
|
|
@@ -15,8 +17,8 @@ interface MapViewEventsMap<K, V>
|
|
|
15
17
|
/**
|
|
16
18
|
* A wrapper class around the native {@link Map} class that provides additional functionality
|
|
17
19
|
* for publishing events when entries are added, removed or the collection is cleared.
|
|
18
|
-
* There
|
|
19
|
-
* See also {@link SetView}.
|
|
20
|
+
* There are also complementary classes that work with the native `Array` and `Set` classes.
|
|
21
|
+
* See also {@link ArrayView} and {@link SetView}.
|
|
20
22
|
*
|
|
21
23
|
* ---
|
|
22
24
|
*
|
|
@@ -62,7 +64,7 @@ export default class MapView<K, V> extends Map<K, V>
|
|
|
62
64
|
|
|
63
65
|
if (iterable)
|
|
64
66
|
{
|
|
65
|
-
for (const [key, value] of iterable) {
|
|
67
|
+
for (const [key, value] of iterable) { super.set(key, value); }
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Publisher from "../callbacks/publisher.js";
|
|
2
2
|
import type { Callback } from "../types.js";
|
|
3
3
|
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
|
+
import type ArrayView from "./array-view.js";
|
|
4
6
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
5
7
|
import type MapView from "./map-view.js";
|
|
6
8
|
|
|
@@ -15,8 +17,8 @@ interface SetViewEventsMap<T>
|
|
|
15
17
|
/**
|
|
16
18
|
* A wrapper class around the native {@link Set} class that provides additional functionality
|
|
17
19
|
* for publishing events when entries are added, removed or the collection is cleared.
|
|
18
|
-
* There
|
|
19
|
-
* See also {@link MapView}.
|
|
20
|
+
* There are also complementary classes that work with the native `Array` and `Map` classes.
|
|
21
|
+
* See also {@link ArrayView} and {@link MapView}.
|
|
20
22
|
*
|
|
21
23
|
* ---
|
|
22
24
|
*
|
|
@@ -61,7 +63,7 @@ export default class SetView<T> extends Set<T>
|
|
|
61
63
|
|
|
62
64
|
if (iterable)
|
|
63
65
|
{
|
|
64
|
-
for (const value of iterable) {
|
|
66
|
+
for (const value of iterable) { super.add(value); }
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -78,7 +80,7 @@ export default class SetView<T> extends Set<T>
|
|
|
78
80
|
* .add(4)
|
|
79
81
|
* .add(8);
|
|
80
82
|
*
|
|
81
|
-
* console.log(set); // SetView(
|
|
83
|
+
* console.log(set); // SetView(3) { 2, 4, 8 }
|
|
82
84
|
* ```
|
|
83
85
|
*
|
|
84
86
|
* ---
|
|
@@ -177,18 +177,14 @@ export class KeyException extends Exception
|
|
|
177
177
|
*
|
|
178
178
|
* @example
|
|
179
179
|
* ```ts
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* try { await axios.get("https://api.example.com/data"); }
|
|
180
|
+
* try { await fetch("https://api.example.com/data"); }
|
|
183
181
|
* catch (error)
|
|
184
182
|
* {
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
* );
|
|
191
|
-
* }
|
|
183
|
+
* throw new NetworkException(
|
|
184
|
+
* "Unable to establish a connection to the server. " +
|
|
185
|
+
* "Please, check your internet connection and try again.",
|
|
186
|
+
* error
|
|
187
|
+
* );
|
|
192
188
|
* }
|
|
193
189
|
* ```
|
|
194
190
|
*/
|
|
@@ -218,6 +214,51 @@ export class NetworkException extends Exception
|
|
|
218
214
|
public override readonly [Symbol.toStringTag]: string = "NetworkException";
|
|
219
215
|
}
|
|
220
216
|
|
|
217
|
+
/**
|
|
218
|
+
* A class representing an exception that can be thrown when a response is not valid or fails.
|
|
219
|
+
* It's commonly used when a request returns an error status code or when the response body is malformed.
|
|
220
|
+
*
|
|
221
|
+
* ---
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts
|
|
225
|
+
* const response = await fetch("https://api.example.com/data");
|
|
226
|
+
* if (!response.ok)
|
|
227
|
+
* {
|
|
228
|
+
* throw new ResponseException(response);
|
|
229
|
+
* }
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
export class ResponseException extends NetworkException
|
|
233
|
+
{
|
|
234
|
+
public readonly response: Response;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Initializes a new instance of the {@link ResponseException} class.
|
|
238
|
+
*
|
|
239
|
+
* ---
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* throw new ResponseException(response);
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* ---
|
|
247
|
+
*
|
|
248
|
+
* @param response The response that caused the error.
|
|
249
|
+
* @param cause The previous caught error that caused this one, if any.
|
|
250
|
+
* @param name The name of the exception. Default is `"ResponseException"`.
|
|
251
|
+
*/
|
|
252
|
+
public constructor(response: Response, cause?: unknown, name = "ResponseException")
|
|
253
|
+
{
|
|
254
|
+
super(`The request failed with the status code ${response.status} (${response.statusText}).`, cause, name);
|
|
255
|
+
|
|
256
|
+
this.response = response;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
public override readonly [Symbol.toStringTag]: string = "ResponseException";
|
|
260
|
+
}
|
|
261
|
+
|
|
221
262
|
/**
|
|
222
263
|
* A class representing an exception that can be thrown when a permission is denied.
|
|
223
264
|
* It's commonly used when an user tries to access a restricted resource or perform a forbidden action.
|
package/src/models/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ export {
|
|
|
6
6
|
} from "./aggregators/index.js";
|
|
7
7
|
|
|
8
8
|
export { CallableObject, CallbackChain, Publisher, SwitchableCallback } from "./callbacks/index.js";
|
|
9
|
-
export { MapView, SetView } from "./collections/index.js";
|
|
9
|
+
export { ArrayView, MapView, SetView } from "./collections/index.js";
|
|
10
10
|
export {
|
|
11
11
|
Exception,
|
|
12
12
|
FatalErrorException,
|
|
@@ -20,6 +20,7 @@ export {
|
|
|
20
20
|
PermissionException,
|
|
21
21
|
RangeException,
|
|
22
22
|
ReferenceException,
|
|
23
|
+
ResponseException,
|
|
23
24
|
RuntimeException,
|
|
24
25
|
TimeoutException,
|
|
25
26
|
TypeException,
|
package/src/utils/random.ts
CHANGED
|
@@ -259,7 +259,7 @@ export default class Random
|
|
|
259
259
|
|
|
260
260
|
if (weights === undefined)
|
|
261
261
|
{
|
|
262
|
-
const pool =
|
|
262
|
+
const pool = Array.from(elements);
|
|
263
263
|
const result: T[] = new Array(count);
|
|
264
264
|
|
|
265
265
|
for (let index = 0; index < count; index += 1)
|
|
@@ -300,6 +300,131 @@ export default class Random
|
|
|
300
300
|
return result;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
static #Split(total: number, parts: number): number[]
|
|
304
|
+
{
|
|
305
|
+
const cuts: number[] = new Array(parts - 1);
|
|
306
|
+
for (let index = 0; index < cuts.length; index += 1)
|
|
307
|
+
{
|
|
308
|
+
cuts[index] = Math.random() * total;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
cuts.sort((a, b) => (a - b));
|
|
312
|
+
|
|
313
|
+
const boundaries = [0, ...cuts, total];
|
|
314
|
+
const values: number[] = new Array(parts);
|
|
315
|
+
|
|
316
|
+
for (let index = 0; index < parts; index += 1)
|
|
317
|
+
{
|
|
318
|
+
values[index] = Math.floor(boundaries[index + 1] - boundaries[index]);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
let remainder = total - values.reduce((sum, val) => (sum + val), 0);
|
|
322
|
+
while (remainder > 0)
|
|
323
|
+
{
|
|
324
|
+
values[this.Integer(parts)] += 1;
|
|
325
|
+
|
|
326
|
+
remainder -= 1;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return values;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Splits a total amount into a given number of randomly balanced integer parts that sum to the total.
|
|
334
|
+
*
|
|
335
|
+
* Uses random cut-points to generate a uniform distribution of parts.
|
|
336
|
+
*
|
|
337
|
+
* ---
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```ts
|
|
341
|
+
* Random.Split(100, 3); // [28, 41, 31]
|
|
342
|
+
* Random.Split(10, 4); // [3, 1, 4, 2]
|
|
343
|
+
* ```
|
|
344
|
+
*
|
|
345
|
+
* ---
|
|
346
|
+
*
|
|
347
|
+
* @param total
|
|
348
|
+
* The total amount to split.
|
|
349
|
+
*
|
|
350
|
+
* It must be non-negative. Otherwise, a {@link ValueException} will be thrown.
|
|
351
|
+
*
|
|
352
|
+
* @param parts
|
|
353
|
+
* The number of parts to split the total into.
|
|
354
|
+
*
|
|
355
|
+
* It must be at least `1`. Otherwise, a {@link ValueException} will be thrown.
|
|
356
|
+
*
|
|
357
|
+
* @returns An array of integers that sum to the given total.
|
|
358
|
+
*/
|
|
359
|
+
public static Split(total: number, parts: number): number[];
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Splits an iterable of elements into a given number of randomly balanced groups.
|
|
363
|
+
*
|
|
364
|
+
* The elements are distributed into groups whose sizes are
|
|
365
|
+
* determined by a random split of the total number of elements.
|
|
366
|
+
*
|
|
367
|
+
* ---
|
|
368
|
+
*
|
|
369
|
+
* @example
|
|
370
|
+
* ```ts
|
|
371
|
+
* Random.Split([1, 2, 3, 4, 5], 2); // [[1, 2], [3, 4, 5]]
|
|
372
|
+
* Random.Split([1, 2, 3, 4, 5], 2); // [[1, 2, 3, 4], [5]]
|
|
373
|
+
* Random.Split("abcdef", 3); // [["a"], ["b", "c", "d"], ["e", "f"]]
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* ---
|
|
377
|
+
*
|
|
378
|
+
* @template T The type of the elements in the iterable.
|
|
379
|
+
*
|
|
380
|
+
* @param elements
|
|
381
|
+
* The iterable of elements to split into groups.
|
|
382
|
+
*
|
|
383
|
+
* It must contain at least one element. Otherwise, a {@link ValueException} will be thrown.
|
|
384
|
+
*
|
|
385
|
+
* @param groups
|
|
386
|
+
* The number of groups to split the elements into.
|
|
387
|
+
*
|
|
388
|
+
* It must be between `1` and the number of elements.
|
|
389
|
+
* Otherwise, a {@link ValueException} will be thrown.
|
|
390
|
+
*
|
|
391
|
+
* @returns An array of arrays, each containing a subset of the original elements.
|
|
392
|
+
*/
|
|
393
|
+
public static Split<T>(elements: Iterable<T>, groups: number): T[][];
|
|
394
|
+
public static Split<T>(totalOrElements: number | Iterable<T>, parts: number): number[] | T[][]
|
|
395
|
+
{
|
|
396
|
+
if (parts < 1) { throw new ValueException("The number of splits must be greater than zero."); }
|
|
397
|
+
|
|
398
|
+
if (typeof totalOrElements === "number")
|
|
399
|
+
{
|
|
400
|
+
if (totalOrElements < 0) { throw new ValueException("The total must be a non-negative number."); }
|
|
401
|
+
|
|
402
|
+
return this.#Split(totalOrElements, parts);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const elements = Array.from(totalOrElements);
|
|
406
|
+
const length = elements.length;
|
|
407
|
+
|
|
408
|
+
if (length === 0) { throw new ValueException("You must provide at least one element."); }
|
|
409
|
+
if (parts > length)
|
|
410
|
+
{
|
|
411
|
+
throw new ValueException("The number of splits cannot exceed the number of elements.");
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const sizes = this.#Split(length, parts);
|
|
415
|
+
const groups: T[][] = new Array(parts);
|
|
416
|
+
|
|
417
|
+
let offset = 0;
|
|
418
|
+
for (let index = 0; index < parts; index += 1)
|
|
419
|
+
{
|
|
420
|
+
groups[index] = elements.slice(offset, offset + sizes[index]);
|
|
421
|
+
|
|
422
|
+
offset += sizes[index];
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return groups;
|
|
426
|
+
}
|
|
427
|
+
|
|
303
428
|
private constructor() { /* ... */ }
|
|
304
429
|
|
|
305
430
|
public readonly [Symbol.toStringTag]: string = "Random";
|