@oscarpalmer/atoms 0.183.0 → 0.184.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/array/sort.d.mts +9 -1
- package/dist/index.d.mts +217 -4
- package/dist/index.mjs +276 -3
- package/dist/queue.d.mts +98 -3
- package/dist/queue.mjs +171 -3
- package/dist/string/normalize.d.mts +2 -0
- package/dist/string/normalize.mjs +2 -0
- package/dist/value/freeze.d.mts +35 -0
- package/dist/value/freeze.mjs +38 -0
- package/dist/value/index.d.mts +2 -1
- package/dist/value/index.mjs +2 -1
- package/dist/value/merge.d.mts +32 -1
- package/dist/value/merge.mjs +27 -1
- package/dist/value/shake.d.mts +5 -0
- package/dist/value/shake.mjs +5 -0
- package/dist/value/transform.d.mts +43 -0
- package/dist/value/transform.mjs +38 -0
- package/package.json +1 -1
- package/plugin/index.js +2 -0
- package/plugin/rules.js +4 -1
- package/src/array/sort.ts +14 -1
- package/src/index.ts +1 -0
- package/src/queue.ts +293 -6
- package/src/string/normalize.ts +2 -0
- package/src/value/freeze.ts +108 -0
- package/src/value/index.ts +1 -0
- package/src/value/merge.ts +56 -0
- package/src/value/shake.ts +5 -0
- package/src/value/transform.ts +140 -0
package/src/queue.ts
CHANGED
|
@@ -2,6 +2,243 @@ import type {GenericAsyncCallback, GenericCallback} from './models';
|
|
|
2
2
|
|
|
3
3
|
// #region Types
|
|
4
4
|
|
|
5
|
+
type HandleType = 'clear' | 'pause' | 'resume';
|
|
6
|
+
|
|
7
|
+
class KeyedQueue<CallbackParameters extends Parameters<GenericAsyncCallback>, CallbackResult> {
|
|
8
|
+
readonly #callback: GenericAsyncCallback;
|
|
9
|
+
|
|
10
|
+
readonly #options: Required<QueueOptions>;
|
|
11
|
+
|
|
12
|
+
readonly #queues: Map<string, Queue<Tail<CallbackParameters>, CallbackResult>> = new Map();
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Is any queue active?
|
|
16
|
+
*/
|
|
17
|
+
get active(): string[] {
|
|
18
|
+
return this.#getStatus(STATUS_ACTIVE);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Does the queue automatically start when the first item is added?
|
|
23
|
+
*/
|
|
24
|
+
get autostart(): boolean {
|
|
25
|
+
return this.#options.autostart;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Maximum number of runners to process the queue concurrently
|
|
30
|
+
*/
|
|
31
|
+
get concurrency(): number {
|
|
32
|
+
return this.#options.concurrency;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Are all queues empty?
|
|
37
|
+
*/
|
|
38
|
+
get empty(): string[] {
|
|
39
|
+
return this.#getStatus(STATUS_EMPTY);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Are all queues full?
|
|
44
|
+
*/
|
|
45
|
+
get full(): string[] {
|
|
46
|
+
return this.#getStatus(STATUS_FULL);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Number of items in all queues
|
|
51
|
+
*/
|
|
52
|
+
get items(): Record<string, number> {
|
|
53
|
+
const size: Record<string, number> = {};
|
|
54
|
+
|
|
55
|
+
const queues = this.#queues.entries();
|
|
56
|
+
|
|
57
|
+
for (const [key, queue] of queues) {
|
|
58
|
+
size[key] = queue.size;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return size;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Keys of all queues
|
|
66
|
+
*/
|
|
67
|
+
get keys(): string[] {
|
|
68
|
+
return [...this.#queues.keys()];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Maximum number of items allowed in the queue
|
|
73
|
+
*/
|
|
74
|
+
get maximum(): number {
|
|
75
|
+
return this.#options.maximum;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Are all queues paused?
|
|
80
|
+
*/
|
|
81
|
+
get paused(): string[] {
|
|
82
|
+
return this.#getStatus(STATUS_PAUSED);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Number of queues
|
|
87
|
+
*/
|
|
88
|
+
get queues(): number {
|
|
89
|
+
return this.#queues.size;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
constructor(callback: GenericAsyncCallback, options: Required<QueueOptions>) {
|
|
93
|
+
this.#callback = callback;
|
|
94
|
+
this.#options = options;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Queue an item for a specific key
|
|
99
|
+
* @param key Key to queue the item for
|
|
100
|
+
* @param parameters Parameters to use when item runs
|
|
101
|
+
* @param signal Optional signal to abort the item
|
|
102
|
+
* @returns Queued item
|
|
103
|
+
*/
|
|
104
|
+
add(
|
|
105
|
+
key: string,
|
|
106
|
+
parameters: Tail<CallbackParameters>,
|
|
107
|
+
signal?: AbortSignal,
|
|
108
|
+
): Queued<CallbackResult> {
|
|
109
|
+
return this.#getQueue(key, true).add(parameters, signal);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Clear all items for a specific key _(or all items for all keys, if no key is provided)_
|
|
114
|
+
* @param key Optional key to clear the queue for
|
|
115
|
+
*/
|
|
116
|
+
clear(key?: string): void {
|
|
117
|
+
this.#handleQueues(HANDLE_CLEAR, key);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Get the queue for a specific key
|
|
122
|
+
* @param key Key to get the queue for
|
|
123
|
+
* @returns Queue for the key, or `undefined` if it doesn't exist
|
|
124
|
+
*/
|
|
125
|
+
get(key: string): Queue<Tail<CallbackParameters>, CallbackResult> | undefined {
|
|
126
|
+
return this.#getQueue(key);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Pause the queue for a specific key _(or all queues, if no key is provided)_
|
|
131
|
+
* @param key Optional key to pause the queue for
|
|
132
|
+
*/
|
|
133
|
+
pause(key?: string): void {
|
|
134
|
+
this.#handleQueues(HANDLE_PAUSE, key);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Remove a specific item for a specific key
|
|
139
|
+
* @param key Key to remove the item for
|
|
140
|
+
* @param id ID of the item to remove
|
|
141
|
+
*/
|
|
142
|
+
remove(key: string, id: number): void;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Remove a queue and its items for a specific key
|
|
146
|
+
*
|
|
147
|
+
* _(To remove all items for a specific key, use `clear()` instead)_
|
|
148
|
+
* @param key Key to remove the queue for
|
|
149
|
+
*/
|
|
150
|
+
remove(key: string): void;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Remove all queues and their items
|
|
154
|
+
*/
|
|
155
|
+
remove(): void;
|
|
156
|
+
|
|
157
|
+
remove(key?: string, id?: number): void {
|
|
158
|
+
if (key == null) {
|
|
159
|
+
this.#handleQueues(HANDLE_CLEAR);
|
|
160
|
+
|
|
161
|
+
this.#queues.clear();
|
|
162
|
+
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const queue = this.#getQueue(key);
|
|
167
|
+
|
|
168
|
+
if (queue == null) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (typeof id === 'number') {
|
|
173
|
+
queue.remove(id);
|
|
174
|
+
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
queue.clear();
|
|
179
|
+
|
|
180
|
+
this.#queues.delete(key);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Resume the queue for a specific key _(or all queues, if no key is provided)_
|
|
185
|
+
* @param key Optional key to resume the queue for
|
|
186
|
+
*/
|
|
187
|
+
resume(key?: string): void {
|
|
188
|
+
this.#handleQueues(HANDLE_RESUME, key);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#getQueue(key: string, add: true): Queue<Tail<CallbackParameters>, CallbackResult>;
|
|
192
|
+
|
|
193
|
+
#getQueue(key: string): Queue<Tail<CallbackParameters>, CallbackResult> | undefined;
|
|
194
|
+
|
|
195
|
+
#getQueue(
|
|
196
|
+
key: string,
|
|
197
|
+
add?: boolean,
|
|
198
|
+
): Queue<Tail<CallbackParameters>, CallbackResult> | undefined {
|
|
199
|
+
if (typeof key !== 'string' || key.trim().length === 0) {
|
|
200
|
+
throw new TypeError(MESSAGE_KEY);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let queue = this.#queues.get(key);
|
|
204
|
+
|
|
205
|
+
if (queue == null && add === true) {
|
|
206
|
+
queue = new Queue(this.#callback, this.#options, key);
|
|
207
|
+
|
|
208
|
+
this.#queues.set(key, queue);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return queue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#getStatus(status: StatusKey): string[] {
|
|
215
|
+
const queues = this.#queues.entries();
|
|
216
|
+
const result: string[] = [];
|
|
217
|
+
|
|
218
|
+
for (const [key, queue] of queues) {
|
|
219
|
+
if (queue[status]) {
|
|
220
|
+
result.push(key);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
#handleQueues(type: HandleType, key?: string): void {
|
|
228
|
+
if (typeof key === 'string') {
|
|
229
|
+
this.#getQueue(key)?.[type]();
|
|
230
|
+
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const queues = this.#queues.values();
|
|
235
|
+
|
|
236
|
+
for (const queue of queues) {
|
|
237
|
+
queue[type]();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
5
242
|
class Queue<CallbackParameters extends Parameters<GenericAsyncCallback>, CallbackResult> {
|
|
6
243
|
readonly #callback: GenericAsyncCallback;
|
|
7
244
|
|
|
@@ -11,6 +248,8 @@ class Queue<CallbackParameters extends Parameters<GenericAsyncCallback>, Callbac
|
|
|
11
248
|
|
|
12
249
|
readonly #items: Array<QueuedItem<CallbackParameters, CallbackResult>> = [];
|
|
13
250
|
|
|
251
|
+
readonly #key: string | undefined;
|
|
252
|
+
|
|
14
253
|
readonly #options: Required<QueueOptions>;
|
|
15
254
|
|
|
16
255
|
#paused: boolean;
|
|
@@ -73,8 +312,9 @@ class Queue<CallbackParameters extends Parameters<GenericAsyncCallback>, Callbac
|
|
|
73
312
|
return this.#items.length;
|
|
74
313
|
}
|
|
75
314
|
|
|
76
|
-
constructor(callback: GenericAsyncCallback, options: Required<QueueOptions
|
|
315
|
+
constructor(callback: GenericAsyncCallback, options: Required<QueueOptions>, key?: string) {
|
|
77
316
|
this.#callback = callback;
|
|
317
|
+
this.#key = key;
|
|
78
318
|
this.#options = options;
|
|
79
319
|
|
|
80
320
|
this.#paused = !options.autostart;
|
|
@@ -116,6 +356,7 @@ class Queue<CallbackParameters extends Parameters<GenericAsyncCallback>, Callbac
|
|
|
116
356
|
parameters,
|
|
117
357
|
promise,
|
|
118
358
|
abort: aborter,
|
|
359
|
+
key: this.#key,
|
|
119
360
|
reject: rejector!,
|
|
120
361
|
resolve: resolver!,
|
|
121
362
|
signal: abortSignal,
|
|
@@ -214,7 +455,12 @@ class Queue<CallbackParameters extends Parameters<GenericAsyncCallback>, Callbac
|
|
|
214
455
|
|
|
215
456
|
try {
|
|
216
457
|
if (!(item.signal?.aborted ?? false)) {
|
|
217
|
-
|
|
458
|
+
const parameters =
|
|
459
|
+
item.key == null
|
|
460
|
+
? item.parameters
|
|
461
|
+
: ([item.key, ...item.parameters] as Parameters<GenericAsyncCallback>);
|
|
462
|
+
|
|
463
|
+
result = await this.#callback(...parameters);
|
|
218
464
|
}
|
|
219
465
|
} catch (thrown) {
|
|
220
466
|
error = true;
|
|
@@ -277,6 +523,7 @@ type Queued<Value> = {
|
|
|
277
523
|
type QueuedItem<CallbackParameters extends Parameters<GenericAsyncCallback>, CallbackResult> = {
|
|
278
524
|
abort?: () => void;
|
|
279
525
|
id: number;
|
|
526
|
+
key?: string;
|
|
280
527
|
parameters: CallbackParameters;
|
|
281
528
|
promise: Promise<QueuedResult<CallbackResult>>;
|
|
282
529
|
reject: (reason?: unknown) => void;
|
|
@@ -295,6 +542,10 @@ type QueuedResult<Value> = {
|
|
|
295
542
|
value: Value;
|
|
296
543
|
};
|
|
297
544
|
|
|
545
|
+
type StatusKey = 'active' | 'empty' | 'full' | 'paused';
|
|
546
|
+
|
|
547
|
+
type Tail<Values extends any[]> = Values extends [infer _, ...infer Rest] ? Rest : never;
|
|
548
|
+
|
|
298
549
|
// #endregion
|
|
299
550
|
|
|
300
551
|
// #region Functions
|
|
@@ -339,13 +590,24 @@ function handleResult<CallbackParameters extends Parameters<GenericAsyncCallback
|
|
|
339
590
|
}
|
|
340
591
|
}
|
|
341
592
|
|
|
593
|
+
export function keyedQueue<Callback extends GenericAsyncCallback>(
|
|
594
|
+
callback: Callback,
|
|
595
|
+
options?: QueueOptions,
|
|
596
|
+
): KeyedQueue<Parameters<Callback>, Awaited<ReturnType<Callback>>> {
|
|
597
|
+
if (typeof callback !== 'function') {
|
|
598
|
+
throw new TypeError(MESSAGE_CALLBACK);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return new KeyedQueue(callback, getOptions(options));
|
|
602
|
+
}
|
|
603
|
+
|
|
342
604
|
/**
|
|
343
605
|
* Create a queue for an asynchronous callback function
|
|
344
606
|
* @param callback Callback function for queued items
|
|
345
607
|
* @param options Queue options
|
|
346
608
|
* @returns Queue instance
|
|
347
609
|
*/
|
|
348
|
-
function queue<Callback extends
|
|
610
|
+
export function queue<Callback extends (key: string, ...parameters: any[]) => Promise<void>>(
|
|
349
611
|
callback: Callback,
|
|
350
612
|
options?: QueueOptions,
|
|
351
613
|
): Queue<Parameters<Callback>, Awaited<ReturnType<Callback>>>;
|
|
@@ -356,12 +618,12 @@ function queue<Callback extends GenericAsyncCallback>(
|
|
|
356
618
|
* @param options Queue options
|
|
357
619
|
* @returns Queue instance
|
|
358
620
|
*/
|
|
359
|
-
function queue<Callback extends GenericCallback>(
|
|
621
|
+
export function queue<Callback extends GenericCallback>(
|
|
360
622
|
callback: Callback,
|
|
361
623
|
options?: QueueOptions,
|
|
362
624
|
): Queue<Parameters<Callback>, ReturnType<Callback>>;
|
|
363
625
|
|
|
364
|
-
function queue(
|
|
626
|
+
export function queue(
|
|
365
627
|
callback: GenericCallback,
|
|
366
628
|
options?: QueueOptions,
|
|
367
629
|
): Queue<Parameters<GenericCallback>, ReturnType<GenericCallback>> {
|
|
@@ -372,6 +634,8 @@ function queue(
|
|
|
372
634
|
return new Queue(callback, getOptions(options));
|
|
373
635
|
}
|
|
374
636
|
|
|
637
|
+
queue.keyed = keyedQueue;
|
|
638
|
+
|
|
375
639
|
// #endregion
|
|
376
640
|
|
|
377
641
|
// #region Variables
|
|
@@ -382,18 +646,41 @@ const EVENT_NAME = 'abort';
|
|
|
382
646
|
|
|
383
647
|
const EVENT_OPTIONS = {once: true};
|
|
384
648
|
|
|
649
|
+
const HANDLE_CLEAR: HandleType = 'clear';
|
|
650
|
+
|
|
651
|
+
const HANDLE_PAUSE: HandleType = 'pause';
|
|
652
|
+
|
|
653
|
+
const HANDLE_RESUME: HandleType = 'resume';
|
|
654
|
+
|
|
385
655
|
const MESSAGE_CALLBACK = 'A Queue requires a callback function';
|
|
386
656
|
|
|
387
657
|
const MESSAGE_CLEAR = 'Queue was cleared';
|
|
388
658
|
|
|
659
|
+
const MESSAGE_KEY = 'Key must be a non-empty string';
|
|
660
|
+
|
|
389
661
|
const MESSAGE_MAXIMUM = 'Queue has reached its maximum size';
|
|
390
662
|
|
|
391
663
|
const MESSAGE_REMOVE = 'Item removed from queue';
|
|
392
664
|
|
|
665
|
+
const STATUS_ACTIVE: StatusKey = 'active';
|
|
666
|
+
|
|
667
|
+
const STATUS_EMPTY: StatusKey = 'empty';
|
|
668
|
+
|
|
669
|
+
const STATUS_FULL: StatusKey = 'full';
|
|
670
|
+
|
|
671
|
+
const STATUS_PAUSED: StatusKey = 'paused';
|
|
672
|
+
|
|
393
673
|
// #endregion
|
|
394
674
|
|
|
395
675
|
// #region Exports
|
|
396
676
|
|
|
397
|
-
export {
|
|
677
|
+
export {
|
|
678
|
+
type KeyedQueue,
|
|
679
|
+
type QueueError,
|
|
680
|
+
type Queue,
|
|
681
|
+
type Queued,
|
|
682
|
+
type QueueOptions,
|
|
683
|
+
type QueuedResult,
|
|
684
|
+
};
|
|
398
685
|
|
|
399
686
|
// #endregion
|
package/src/string/normalize.ts
CHANGED
|
@@ -88,6 +88,8 @@ function getNormalizeOptions(input?: NormalizeOptions): Options {
|
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* Initialize a string normalizer
|
|
91
|
+
*
|
|
92
|
+
* Available as `initializeNormalizer` and `normalize.initialize`
|
|
91
93
|
* @param options Normalization options
|
|
92
94
|
* @returns Normalizer function
|
|
93
95
|
*/
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {isPlainObject} from '../is';
|
|
2
|
+
import type {ArrayOrPlainObject, GenericCallback, PlainObject} from '../models';
|
|
3
|
+
|
|
4
|
+
// #region Types
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A frozen value with readonly properties _(going as deep as possible)_
|
|
8
|
+
*/
|
|
9
|
+
export type Frozen<Value extends ArrayOrPlainObject> = {
|
|
10
|
+
readonly [Key in keyof Value]: Value[Key] extends ArrayOrPlainObject
|
|
11
|
+
? Frozen<Value[Key]>
|
|
12
|
+
: Value[Key];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// #endregion
|
|
16
|
+
|
|
17
|
+
// #region Functions
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Freeze an array and all its indices recursively
|
|
21
|
+
* @param array Array to freeze
|
|
22
|
+
* @returns Frozen array
|
|
23
|
+
*/
|
|
24
|
+
export function freeze<Item>(array: Item[]): Frozen<Item[]>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Freeze a callback
|
|
28
|
+
* @param callback Function to freeze
|
|
29
|
+
* @returns Frozen function
|
|
30
|
+
*/
|
|
31
|
+
export function freeze<Fn extends GenericCallback>(fn: Fn): Readonly<Fn>;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Freeze an object and all its properties recursively
|
|
35
|
+
* @param object Object to freeze
|
|
36
|
+
* @returns Frozen object
|
|
37
|
+
*/
|
|
38
|
+
export function freeze<Value extends PlainObject>(object: Value): Frozen<Value>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Freeze any value, if possible
|
|
42
|
+
*
|
|
43
|
+
* _(Only arrays, functions, and plain objects are freezable)_
|
|
44
|
+
* @param value Value to freeze
|
|
45
|
+
* @returns Frozen value
|
|
46
|
+
*/
|
|
47
|
+
export function freeze<Value>(value: Value): Value;
|
|
48
|
+
|
|
49
|
+
export function freeze(value: unknown): unknown {
|
|
50
|
+
return freezeValue(value, new WeakSet());
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function freezeArray(array: unknown[], references: WeakSet<any>): Frozen<unknown[]> {
|
|
54
|
+
references.add(array);
|
|
55
|
+
|
|
56
|
+
const {length} = array;
|
|
57
|
+
|
|
58
|
+
for (let index = 0; index < length; index += 1) {
|
|
59
|
+
const value = array[index];
|
|
60
|
+
|
|
61
|
+
if (!references.has(value)) {
|
|
62
|
+
array[index] = freezeValue(array[index], references);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return Object.freeze(array) as Frozen<unknown[]>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function freezeFunction(fn: Function, references: WeakSet<any>): Readonly<Function> {
|
|
70
|
+
references.add(fn);
|
|
71
|
+
|
|
72
|
+
return Object.freeze(fn);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function freezeObject(object: PlainObject, references: WeakSet<any>): Frozen<PlainObject> {
|
|
76
|
+
references.add(object);
|
|
77
|
+
|
|
78
|
+
const keys = Object.keys(object);
|
|
79
|
+
const {length} = keys;
|
|
80
|
+
|
|
81
|
+
for (let index = 0; index < length; index += 1) {
|
|
82
|
+
const key = keys[index];
|
|
83
|
+
|
|
84
|
+
if (!references.has(object[key])) {
|
|
85
|
+
object[key] = freezeValue(object[key], references);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return Object.freeze(object) as Frozen<PlainObject>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function freezeValue(value: unknown, references: WeakSet<any>): unknown {
|
|
93
|
+
switch (true) {
|
|
94
|
+
case typeof value === 'function':
|
|
95
|
+
return freezeFunction(value, references);
|
|
96
|
+
|
|
97
|
+
case Array.isArray(value):
|
|
98
|
+
return freezeArray(value, references);
|
|
99
|
+
|
|
100
|
+
case isPlainObject(value):
|
|
101
|
+
return freezeObject(value, references);
|
|
102
|
+
|
|
103
|
+
default:
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// #endregion
|
package/src/value/index.ts
CHANGED
package/src/value/merge.ts
CHANGED
|
@@ -4,6 +4,22 @@ import type {ArrayOrPlainObject, NestedPartial, PlainObject} from '../models';
|
|
|
4
4
|
|
|
5
5
|
// #region Types
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Options for assigning values
|
|
9
|
+
*/
|
|
10
|
+
export type AssignOptions = Omit<MergeOptions, 'assignValues'>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Assign values from multiple arrays or objects to the first one
|
|
14
|
+
* @param to Value to assign to
|
|
15
|
+
* @param from Values to assign
|
|
16
|
+
* @returns Assigned value
|
|
17
|
+
*/
|
|
18
|
+
export type Assigner<Model extends ArrayOrPlainObject = ArrayOrPlainObject> = (
|
|
19
|
+
to: NestedPartial<Model>,
|
|
20
|
+
from: NestedPartial<Model>[],
|
|
21
|
+
) => Model;
|
|
22
|
+
|
|
7
23
|
/**
|
|
8
24
|
* Options for merging values
|
|
9
25
|
*/
|
|
@@ -60,6 +76,27 @@ type ReplaceableObjectsCallback = (name: string) => boolean;
|
|
|
60
76
|
|
|
61
77
|
// #region Functions
|
|
62
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Assign values from multiple arrays or objects to the first one
|
|
81
|
+
* @param to Value to assign to
|
|
82
|
+
* @param from Values to assign
|
|
83
|
+
* @param options Assigning options
|
|
84
|
+
* @returns Assigned value
|
|
85
|
+
*/
|
|
86
|
+
export function assign<Model extends ArrayOrPlainObject>(
|
|
87
|
+
to: NestedPartial<Model>,
|
|
88
|
+
from: NestedPartial<Model>[],
|
|
89
|
+
options?: AssignOptions,
|
|
90
|
+
): Model {
|
|
91
|
+
const actual = getMergeOptions(options);
|
|
92
|
+
|
|
93
|
+
actual.assignValues = true;
|
|
94
|
+
|
|
95
|
+
return mergeValues([to, ...from], actual, true) as Model;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
assign.initialize = initializeAssigner;
|
|
99
|
+
|
|
63
100
|
function getMergeOptions(options?: MergeOptions): Options {
|
|
64
101
|
const actual: Options = {
|
|
65
102
|
assignValues: false,
|
|
@@ -96,6 +133,24 @@ function handleMerge(values: ArrayOrPlainObject[], options: Options): ArrayOrPla
|
|
|
96
133
|
return !Array.isArray(values) || values.length === 0 ? {} : mergeValues(values, options, true);
|
|
97
134
|
}
|
|
98
135
|
|
|
136
|
+
/**
|
|
137
|
+
* Create an assigner with predefined options
|
|
138
|
+
*
|
|
139
|
+
* Available as `initializeAssigner` and `assign.initialize`
|
|
140
|
+
* @param options Assigning options
|
|
141
|
+
* @returns Assigner function
|
|
142
|
+
*/
|
|
143
|
+
export function initializeAssigner<Model extends ArrayOrPlainObject>(
|
|
144
|
+
options?: AssignOptions,
|
|
145
|
+
): Assigner<Model> {
|
|
146
|
+
const actual = getMergeOptions(options);
|
|
147
|
+
|
|
148
|
+
actual.assignValues = true;
|
|
149
|
+
|
|
150
|
+
return ((to: ArrayOrPlainObject, from: NestedPartial<ArrayOrPlainObject>[]): ArrayOrPlainObject =>
|
|
151
|
+
mergeValues([to, ...from], actual, true)) as Assigner<Model>;
|
|
152
|
+
}
|
|
153
|
+
|
|
99
154
|
/**
|
|
100
155
|
* Create a merger with predefined options
|
|
101
156
|
*
|
|
@@ -139,6 +194,7 @@ export function merge(
|
|
|
139
194
|
return handleMerge(values, getMergeOptions(options));
|
|
140
195
|
}
|
|
141
196
|
|
|
197
|
+
merge.assign = assign;
|
|
142
198
|
merge.initialize = initializeMerger;
|
|
143
199
|
|
|
144
200
|
function mergeObjects(
|
package/src/value/shake.ts
CHANGED
|
@@ -11,6 +11,11 @@ export type Shaken<Value extends PlainObject> = {
|
|
|
11
11
|
|
|
12
12
|
// #region Functions
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Shake an object, removing all keys with `undefined` values
|
|
16
|
+
* @param value Object to shake
|
|
17
|
+
* @returns Shaken object
|
|
18
|
+
*/
|
|
14
19
|
export function shake<Value extends PlainObject>(value: Value): Shaken<Value> {
|
|
15
20
|
const shaken: PlainObject = {};
|
|
16
21
|
|