@oscarpalmer/atoms 0.71.0 → 0.72.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/js/array/index.js +109 -10
- package/dist/js/array/sort.mjs +3 -10
- package/dist/js/emitter.js +28 -16
- package/dist/js/emitter.mjs +28 -16
- package/dist/js/function.js +9 -4
- package/dist/js/function.mjs +9 -4
- package/dist/js/index.js +253 -115
- package/dist/js/index.mjs +1 -1
- package/dist/js/query.js +0 -2
- package/dist/js/sized.js +98 -0
- package/dist/js/sized.mjs +91 -0
- package/dist/js/string/index.js +1 -1
- package/dist/js/string/template.mjs +2 -1
- package/dist/js/value/compare.mjs +61 -0
- package/dist/js/value/index.js +94 -2
- package/dist/js/value/index.mjs +1 -0
- package/dist/js/value/set.mjs +0 -2
- package/package.json +9 -9
- package/src/js/array/sort.ts +3 -15
- package/src/js/emitter.ts +41 -24
- package/src/js/function.ts +14 -4
- package/src/js/index.ts +1 -1
- package/src/js/sized.ts +203 -0
- package/src/js/string/index.ts +0 -1
- package/src/js/string/template.ts +2 -1
- package/src/js/value/compare.ts +85 -0
- package/src/js/value/index.ts +12 -12
- package/src/js/value/set.ts +0 -2
- package/types/emitter.d.ts +1 -0
- package/types/function.d.ts +4 -0
- package/types/index.d.cts +255 -72
- package/types/index.d.ts +1 -1
- package/types/sized.d.ts +78 -0
- package/types/value/compare.d.ts +4 -0
- package/types/value/index.d.ts +1 -0
- package/dist/js/event.js +0 -16
- package/dist/js/event.mjs +0 -16
- package/src/js/event.ts +0 -21
- package/types/event.d.ts +0 -5
package/src/js/sized.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import {clamp} from './number';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A Map with a maximum size
|
|
5
|
+
* - Maximum size defaults to _2^20_; any provided size will be clamped at _2^24_
|
|
6
|
+
* - Behaviour is similar to a _LRU_-cache, where the least recently used entries are removed
|
|
7
|
+
*/
|
|
8
|
+
export class SizedMap<Key = unknown, Value = unknown> extends Map<Key, Value> {
|
|
9
|
+
private readonly maximumSize: number;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Is the Map full?
|
|
13
|
+
*/
|
|
14
|
+
get full() {
|
|
15
|
+
return this.size >= this.maximumSize;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The maximum size of the Map
|
|
20
|
+
*/
|
|
21
|
+
get maximum() {
|
|
22
|
+
return this.maximumSize;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new Map with entries and a maximum size _(2^20)_
|
|
27
|
+
*/
|
|
28
|
+
constructor(entries: Array<[Key, Value]>);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates a new Map with a maximum size _(but clamped at 2^24)_
|
|
32
|
+
*/
|
|
33
|
+
constructor(maximum: number);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new Map with _(optional)_ entries and a maximum size _(defaults to 2^20; clamped at 2^24)_
|
|
37
|
+
*/
|
|
38
|
+
constructor(entries?: Array<[Key, Value]>, maximum?: number);
|
|
39
|
+
|
|
40
|
+
constructor(entries?: Array<[Key, Value]> | number, maximum?: number) {
|
|
41
|
+
const maximumSize = getMaximum(
|
|
42
|
+
typeof entries === 'number'
|
|
43
|
+
? entries
|
|
44
|
+
: typeof maximum === 'number'
|
|
45
|
+
? maximum
|
|
46
|
+
: undefined,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
super(Array.isArray(entries) ? entries.slice(0, maximumSize) : undefined);
|
|
50
|
+
|
|
51
|
+
this.maximumSize = maximumSize;
|
|
52
|
+
|
|
53
|
+
if (Array.isArray(entries) && entries.length > maximumSize) {
|
|
54
|
+
for (let index = 0; index < maximumSize; index += 1) {
|
|
55
|
+
this.set(...entries[entries.length - maximumSize + index]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @inheritdoc
|
|
62
|
+
*/
|
|
63
|
+
get(key: Key): Value | undefined {
|
|
64
|
+
const value = super.get(key);
|
|
65
|
+
|
|
66
|
+
if (value === undefined && !this.has(key)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.set(key, value as Value);
|
|
71
|
+
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @inheritdoc
|
|
77
|
+
*/
|
|
78
|
+
set(key: Key, value: Value): this {
|
|
79
|
+
if (this.has(key)) {
|
|
80
|
+
this.delete(key);
|
|
81
|
+
} else if (this.size >= this.maximumSize) {
|
|
82
|
+
this.delete(this.keys().next().value as Key);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return super.set(key, value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A Set with a maximum size
|
|
91
|
+
* - Maximum size defaults to _2^20_; any provided size will be clamped at _2^24_
|
|
92
|
+
* - Behaviour is similar to a _LRU_-cache, where the oldest values are removed
|
|
93
|
+
*/
|
|
94
|
+
export class SizedSet<Value = unknown> extends Set<Value> {
|
|
95
|
+
private readonly maximumSize: number;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Is the Set full?
|
|
99
|
+
*/
|
|
100
|
+
get full() {
|
|
101
|
+
return this.size >= this.maximumSize;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The maximum size of the Set
|
|
106
|
+
*/
|
|
107
|
+
get maximum() {
|
|
108
|
+
return this.maximumSize;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Creates a new Set with values and a maximum size _(2^20)_
|
|
113
|
+
*/
|
|
114
|
+
constructor(values: Value[]);
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Creates a new Set with a maximum size _(but clamped at 2^24)_
|
|
118
|
+
*/
|
|
119
|
+
constructor(maximum: number);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Creates a new Set with _(optional)_ values and a maximum size _(defaults to 2^20; clamped at 2^24)_
|
|
123
|
+
*/
|
|
124
|
+
constructor(values?: Value[], maximum?: number);
|
|
125
|
+
|
|
126
|
+
constructor(values?: Value[] | number, maximum?: number) {
|
|
127
|
+
const maximumSize = getMaximum(
|
|
128
|
+
typeof values === 'number'
|
|
129
|
+
? values
|
|
130
|
+
: typeof maximum === 'number'
|
|
131
|
+
? maximum
|
|
132
|
+
: undefined,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
super(
|
|
136
|
+
Array.isArray(values) && values.length <= maximumSize
|
|
137
|
+
? values
|
|
138
|
+
: undefined,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
this.maximumSize = maximumSize;
|
|
142
|
+
|
|
143
|
+
if (Array.isArray(values) && values.length > maximumSize) {
|
|
144
|
+
for (let index = 0; index < maximumSize; index += 1) {
|
|
145
|
+
this.add(values[values.length - maximumSize + index]);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @inheritdoc
|
|
152
|
+
*/
|
|
153
|
+
add(value: Value): this {
|
|
154
|
+
if (this.has(value)) {
|
|
155
|
+
this.delete(value);
|
|
156
|
+
} else if (this.size >= this.maximumSize) {
|
|
157
|
+
this.delete(this.values().next().value as Value);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return super.add(value);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get a value from an index in the Set, if it exists
|
|
165
|
+
* - Negative indices are counted from the end
|
|
166
|
+
* - Optionally move the value to the end with `update`
|
|
167
|
+
*/
|
|
168
|
+
at(index: number, update?: boolean): Value | undefined {
|
|
169
|
+
const value = [...this.values()][index < 0 ? this.size + index : index];
|
|
170
|
+
|
|
171
|
+
if ((update ?? false) && this.has(value)) {
|
|
172
|
+
this.delete(value);
|
|
173
|
+
this.add(value);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get a value from the Set, if it exists _(and move it to the end)_
|
|
181
|
+
*/
|
|
182
|
+
get(value: Value, update?: boolean): Value | undefined {
|
|
183
|
+
if (this.has(value)) {
|
|
184
|
+
if (update ?? false) {
|
|
185
|
+
this.delete(value);
|
|
186
|
+
this.add(value);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function getMaximum(first?: unknown, second?: unknown): number {
|
|
195
|
+
const actual =
|
|
196
|
+
(typeof first === 'number'
|
|
197
|
+
? first
|
|
198
|
+
: typeof second === 'number'
|
|
199
|
+
? second
|
|
200
|
+
: undefined) ?? 2 ** 20;
|
|
201
|
+
|
|
202
|
+
return clamp(actual, 1, 2 ** 24);
|
|
203
|
+
}
|
package/src/js/string/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {PlainObject} from '../models';
|
|
2
2
|
import {getValue} from '../value';
|
|
3
|
+
import {getString} from './index';
|
|
3
4
|
|
|
4
5
|
type Options = {
|
|
5
6
|
/**
|
|
@@ -35,7 +36,7 @@ export function template(
|
|
|
35
36
|
return '';
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
values[key] =
|
|
39
|
+
values[key] = getString(value);
|
|
39
40
|
|
|
40
41
|
return values[key];
|
|
41
42
|
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import {max} from '../math';
|
|
2
|
+
import {getNumber} from '../number';
|
|
3
|
+
import {words, getString, join} from '../string';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compare two values _(for sorting purposes)_
|
|
7
|
+
*/
|
|
8
|
+
export function compare(first: unknown, second: unknown): number {
|
|
9
|
+
const firstParts = getParts(first);
|
|
10
|
+
const secondParts = getParts(second);
|
|
11
|
+
const length = max([firstParts.length, secondParts.length]);
|
|
12
|
+
const lastIndex = length - 1;
|
|
13
|
+
|
|
14
|
+
for (let index = 0; index < length; index += 1) {
|
|
15
|
+
const firstPart = firstParts[index];
|
|
16
|
+
const secondPart = secondParts[index];
|
|
17
|
+
|
|
18
|
+
if (firstPart === secondPart) {
|
|
19
|
+
if (index === lastIndex) {
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
firstPart == null ||
|
|
28
|
+
(typeof firstPart === 'string' && firstPart.length === 0)
|
|
29
|
+
) {
|
|
30
|
+
return -1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (
|
|
34
|
+
secondPart == null ||
|
|
35
|
+
(typeof secondPart === 'string' && secondPart.length === 0)
|
|
36
|
+
) {
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const firstNumber = getNumber(firstPart);
|
|
41
|
+
const secondNumber = getNumber(secondPart);
|
|
42
|
+
|
|
43
|
+
const firstIsNaN = Number.isNaN(firstNumber);
|
|
44
|
+
const secondIsNaN = Number.isNaN(secondNumber);
|
|
45
|
+
|
|
46
|
+
if (firstIsNaN || secondIsNaN) {
|
|
47
|
+
if (firstIsNaN && secondIsNaN) {
|
|
48
|
+
return getString(firstPart).localeCompare(getString(secondPart));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (firstIsNaN) {
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (secondIsNaN) {
|
|
56
|
+
return -1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (firstNumber === secondNumber) {
|
|
61
|
+
if (index === lastIndex) {
|
|
62
|
+
// Same value on last part? let's not fall through to string comparison
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return firstNumber - secondNumber;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return join(firstParts).localeCompare(join(secondParts));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function getParts(value: unknown): unknown[] {
|
|
76
|
+
if (value == null) {
|
|
77
|
+
return [''];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return typeof value === 'object' ? [value] : words(getString(value));
|
|
85
|
+
}
|
package/src/js/value/index.ts
CHANGED
|
@@ -4,22 +4,23 @@ import type {PlainObject} from '../models';
|
|
|
4
4
|
* Creates a new object with only the specified keys
|
|
5
5
|
*/
|
|
6
6
|
export function partial<Value extends PlainObject, Key extends keyof Value>(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
value: Value,
|
|
8
|
+
keys: Key[],
|
|
9
|
+
): Pick<Value, Key> {
|
|
10
|
+
const result = {} as Pick<Value, Key>;
|
|
11
|
+
const {length} = keys;
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
for (let index = 0; index < length; index += 1) {
|
|
14
|
+
const key = keys[index];
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return result;
|
|
16
|
+
result[key] = value[key];
|
|
20
17
|
}
|
|
21
18
|
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
22
|
export * from './clone';
|
|
23
|
+
export * from './compare';
|
|
23
24
|
export * from './diff';
|
|
24
25
|
export * from './equal';
|
|
25
26
|
export * from './get';
|
|
@@ -27,4 +28,3 @@ export * from './merge';
|
|
|
27
28
|
export * from './set';
|
|
28
29
|
export * from './smush';
|
|
29
30
|
export * from './unsmush';
|
|
30
|
-
|
package/src/js/value/set.ts
CHANGED
|
@@ -38,7 +38,6 @@ export function setValue<Data extends PlainObject>(
|
|
|
38
38
|
const {length} = parts;
|
|
39
39
|
const lastIndex = length - 1;
|
|
40
40
|
|
|
41
|
-
let previous: PlainObject | undefined;
|
|
42
41
|
let target: PlainObject =
|
|
43
42
|
typeof data === 'object' && data !== null ? data : {};
|
|
44
43
|
|
|
@@ -59,7 +58,6 @@ export function setValue<Data extends PlainObject>(
|
|
|
59
58
|
target[part] = next;
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
previous = target;
|
|
63
61
|
target = next as PlainObject;
|
|
64
62
|
}
|
|
65
63
|
|
package/types/emitter.d.ts
CHANGED
package/types/function.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ declare class Memoised<Callback extends GenericCallback> {
|
|
|
16
16
|
* Deletes a result from the cache
|
|
17
17
|
*/
|
|
18
18
|
delete(key: Parameters<Callback>[0]): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Destroys the instance, clearing its cache and removing its callback
|
|
21
|
+
*/
|
|
22
|
+
destroy(): void;
|
|
19
23
|
/**
|
|
20
24
|
* Retrieves the result from the cache if it exists, or `undefined` otherwise
|
|
21
25
|
*/
|