@the-w1zard/dx 1.0.11 → 1.0.13
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/index.js +4 -3
- package/package.json +2 -2
- package/src/array.ts +301 -0
- package/src/boolean_byte.ts +23 -0
- package/src/class.ts +0 -0
- package/src/date.ts +660 -0
- package/src/dx.d.ts +7 -1
- package/src/error.ts +81 -0
- package/src/index.ts +45 -0
- package/src/iterator.ts +407 -0
- package/src/map.ts +513 -0
- package/src/map_bin.ts +179 -0
- package/src/map_byte.ts +418 -0
- package/src/map_dense.ts +167 -0
- package/src/map_rows.ts +145 -0
- package/src/number.ts +212 -0
- package/src/object.ts +291 -0
- package/src/promise.ts +309 -0
- package/src/reg_ex.ts +423 -0
- package/src/set.ts +198 -0
- package/src/set_byte.ts +223 -0
- package/src/soa_codec.ts +27 -0
- package/src/string.ts +175 -0
- package/src/unit8array.ts +374 -0
- package/src/url_search_params.ts +187 -0
- package/src/weak_map.ts +155 -0
- package/src/weak_set.ts +101 -0
package/src/map_rows.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export class M2RI<K = N> {
|
|
4
|
+
#vals: Float64Array;
|
|
5
|
+
#pidToSlot: Int32Array;
|
|
6
|
+
#slotToPid: Int32Array;
|
|
7
|
+
#size = 0;
|
|
8
|
+
readonly stride: N;
|
|
9
|
+
|
|
10
|
+
constructor(capacity = 1024, stride = 3) {
|
|
11
|
+
this.stride = Math.max(1, stride | 0);
|
|
12
|
+
const cap = Math.max(1, capacity | 0);
|
|
13
|
+
this.#pidToSlot = new Int32Array(cap).fill(-1);
|
|
14
|
+
this.#slotToPid = new Int32Array(cap).fill(-1);
|
|
15
|
+
this.#vals = new Float64Array(cap * this.stride);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get size(): N {
|
|
19
|
+
return this.#size;
|
|
20
|
+
}
|
|
21
|
+
get capacity(): N {
|
|
22
|
+
return this.#pidToSlot.length;
|
|
23
|
+
}
|
|
24
|
+
get buffer(): Float64Array {
|
|
25
|
+
return this.#vals;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get(pid: N): Float64Array | UD {
|
|
29
|
+
const i = this.#pidToSlot[pid] as N;
|
|
30
|
+
if (i < 0) return undefined;
|
|
31
|
+
const o = i * this.stride;
|
|
32
|
+
return this.#vals.subarray(o, o + this.stride);
|
|
33
|
+
}
|
|
34
|
+
has(pid: N): boolean {
|
|
35
|
+
const i = this.#pidToSlot[pid];
|
|
36
|
+
return i !== undefined && i !== -1;
|
|
37
|
+
}
|
|
38
|
+
set(pid: N, v: Float64Array | ArrayLike<N>): this {
|
|
39
|
+
this.write(pid, v);
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
delete(pid: N): boolean {
|
|
43
|
+
const i = this.#pidToSlot[pid] as N;
|
|
44
|
+
if (i === undefined || i < 0) return false;
|
|
45
|
+
const st = this.stride;
|
|
46
|
+
const last = --this.#size;
|
|
47
|
+
const lastPid = this.#slotToPid[last];
|
|
48
|
+
if (i !== last) {
|
|
49
|
+
this.#vals.copyWithin(i * st, last * st, last * st + st);
|
|
50
|
+
this.#slotToPid[i] = lastPid;
|
|
51
|
+
this.#pidToSlot[lastPid] = i;
|
|
52
|
+
}
|
|
53
|
+
this.#pidToSlot[pid] = -1;
|
|
54
|
+
this.#slotToPid[last] = -1;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
clear(): void {
|
|
58
|
+
this.#pidToSlot.fill(-1);
|
|
59
|
+
this.#slotToPid.fill(-1);
|
|
60
|
+
this.#size = 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** copy a row in, no validation per component */
|
|
64
|
+
write(pid: N, v: Float64Array | ArrayLike<N>): V {
|
|
65
|
+
const o = this.#open(pid) * this.stride;
|
|
66
|
+
this.#vals.set(v as ArrayLike<N>, o);
|
|
67
|
+
}
|
|
68
|
+
/** single component: row[c] = v */
|
|
69
|
+
put(pid: N, c: N, v: N): V {
|
|
70
|
+
this.#vals[this.#open(pid) * this.stride + c] = v;
|
|
71
|
+
}
|
|
72
|
+
/** single component: row[c] */
|
|
73
|
+
at(pid: N, c: N): N {
|
|
74
|
+
return this.#vals[this.#pidToSlot[pid] * this.stride + c];
|
|
75
|
+
}
|
|
76
|
+
/** pid -> slot, or -1 */
|
|
77
|
+
slotOf(pid: N): N {
|
|
78
|
+
const i = this.#pidToSlot[pid];
|
|
79
|
+
return i === undefined ? -1 : i;
|
|
80
|
+
}
|
|
81
|
+
/** zero-copy row view you can write through */
|
|
82
|
+
row(pid: N): F64 | UD {
|
|
83
|
+
return this.get(pid);
|
|
84
|
+
}
|
|
85
|
+
/** view over the live prefix of the whole table — feed straight to a shader/worker */
|
|
86
|
+
live(): F64 {
|
|
87
|
+
return this.#vals.subarray(0, this.#size * this.stride);
|
|
88
|
+
}
|
|
89
|
+
*keys(): MapIterator<K> {
|
|
90
|
+
for (let i = 0; i < this.#size; i++) yield this.#slotToPid[i] as K;
|
|
91
|
+
}
|
|
92
|
+
*values(): MapIterator<F64> {
|
|
93
|
+
const st = this.stride;
|
|
94
|
+
for (let i = 0; i < this.#size; i++) yield this.#vals.subarray(i * st, i * st + st);
|
|
95
|
+
}
|
|
96
|
+
*entries(): MapIterator<[K, F64]> {
|
|
97
|
+
const st = this.stride;
|
|
98
|
+
for (let i = 0; i < this.#size; i++) yield [this.#slotToPid[i] as K, this.#vals.subarray(i * st, i * st + st)];
|
|
99
|
+
}
|
|
100
|
+
[Symbol.iterator](): MapIterator<[K, F64]> {
|
|
101
|
+
return this.entries();
|
|
102
|
+
}
|
|
103
|
+
forEach(fn: (v: F64, k: K, m: Map<K, F64>) => V, thisArg?: U): V {
|
|
104
|
+
for (const [k, v] of this.entries()) fn.call(thisArg, v, k, this);
|
|
105
|
+
}
|
|
106
|
+
forEachRaw(fn: (pid: N, slot: N) => V): V {
|
|
107
|
+
for (let i = 0; i < this.#size; i++) fn(this.#slotToPid[i], i);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#open(pid: N): N {
|
|
111
|
+
let p = this.#pidToSlot;
|
|
112
|
+
if (pid >= p.length) {
|
|
113
|
+
this.#grow(pid);
|
|
114
|
+
p = this.#pidToSlot;
|
|
115
|
+
}
|
|
116
|
+
let i = p[pid];
|
|
117
|
+
if (i < 0) {
|
|
118
|
+
if (this.#size === p.length) {
|
|
119
|
+
this.#grow(this.#size);
|
|
120
|
+
p = this.#pidToSlot;
|
|
121
|
+
}
|
|
122
|
+
i = this.#size++;
|
|
123
|
+
p[pid] = i;
|
|
124
|
+
this.#slotToPid[i] = pid;
|
|
125
|
+
}
|
|
126
|
+
return i;
|
|
127
|
+
}
|
|
128
|
+
#grow(need: N): V {
|
|
129
|
+
let cap = this.#pidToSlot.length;
|
|
130
|
+
while (cap <= need) cap *= 2;
|
|
131
|
+
const p2s = new Int32Array(cap).fill(-1);
|
|
132
|
+
p2s.set(this.#pidToSlot);
|
|
133
|
+
this.#pidToSlot = p2s;
|
|
134
|
+
const s2p = new Int32Array(cap).fill(-1);
|
|
135
|
+
s2p.set(this.#slotToPid);
|
|
136
|
+
this.#slotToPid = s2p;
|
|
137
|
+
const v = new Float64Array(cap * this.stride);
|
|
138
|
+
v.set(this.#vals);
|
|
139
|
+
this.#vals = v;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export interface M2R<K> extends M2RI {}
|
|
143
|
+
Object.setPrototypeOf(M2RI.prototype, Map.prototype);
|
|
144
|
+
|
|
145
|
+
globalThis.M2R = M2RI;
|
package/src/number.ts
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export class N2I extends Number implements N2 {
|
|
4
|
+
abs(): N2I {
|
|
5
|
+
return new N2I(Math.abs(this.valueOf()));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
sign(): N2I {
|
|
9
|
+
return new N2I(Math.sign(this.valueOf()));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
neg(): N2I {
|
|
13
|
+
return new N2I(-this.valueOf());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
inv(): N2I {
|
|
17
|
+
return new N2I(1 / this.valueOf());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
clamp(min: N, max: N): N2I {
|
|
21
|
+
return new N2I(Math.min(Math.max(this.valueOf(), min), max));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
wrap(min: N, max: N): N2I {
|
|
25
|
+
const range = max - min;
|
|
26
|
+
if (range <= 0) throw new RangeError("max must be greater than min");
|
|
27
|
+
return new N2I((((this.valueOf() - min) % range) + range) % range + min);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
lerp(to: N, t: N): N2I {
|
|
31
|
+
return new N2I(this.valueOf() + (to - this.valueOf()) * t);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
map(inMin: N, inMax: N, outMin: N, outMax: N): N2I {
|
|
35
|
+
if (inMax === inMin) throw new RangeError("Input range cannot be zero");
|
|
36
|
+
return new N2I(outMin + ((this.valueOf() - inMin) / (inMax - inMin)) * (outMax - outMin));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
remap(inMin: N, inMax: N, outMin: N, outMax: N): N2I {
|
|
40
|
+
return this.map(inMin, inMax, outMin, outMax);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
roundTo(precision: N = 0): N2I {
|
|
44
|
+
const factor = 10 ** precision;
|
|
45
|
+
return new N2I(Math.round((this.valueOf() + Number.EPSILON) * factor) / factor);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
floorTo(precision: N = 0): N2I {
|
|
49
|
+
const factor = 10 ** precision;
|
|
50
|
+
return new N2I(Math.floor(this.valueOf() * factor) / factor);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ceilTo(precision: N = 0): N2I {
|
|
54
|
+
const factor = 10 ** precision;
|
|
55
|
+
return new N2I(Math.ceil(this.valueOf() * factor) / factor);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
truncTo(precision: N = 0): N2I {
|
|
59
|
+
const factor = 10 ** precision;
|
|
60
|
+
return new N2I(Math.trunc(this.valueOf() * factor) / factor);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
isFinite(): B {
|
|
64
|
+
return Number.isFinite(this.valueOf());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
isInteger(): B {
|
|
68
|
+
return Number.isInteger(this.valueOf());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
isSafeInteger(): B {
|
|
72
|
+
return Number.isSafeInteger(this.valueOf());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
isNaN(): B {
|
|
76
|
+
return Number.isNaN(this.valueOf());
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
isBetween(min: N, max: N, inclusive: B = true): B {
|
|
80
|
+
const value = this.valueOf();
|
|
81
|
+
return inclusive ? value >= min && value <= max : value > min && value < max;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
isPositive(): B {
|
|
85
|
+
return this.valueOf() > 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
isNegative(): B {
|
|
89
|
+
return this.valueOf() < 0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
isZero(): B {
|
|
93
|
+
return this.valueOf() === 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
isEven(): B {
|
|
97
|
+
const value = this.valueOf();
|
|
98
|
+
return Number.isInteger(value) && value % 2 === 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
isOdd(): B {
|
|
102
|
+
const value = this.valueOf();
|
|
103
|
+
return Number.isInteger(value) && Math.abs(value % 2) === 1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
squared(): N2I {
|
|
107
|
+
return new N2I(this.valueOf() ** 2);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
cubed(): N2I {
|
|
111
|
+
return new N2I(this.valueOf() ** 3);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
sqrt(): N2I {
|
|
115
|
+
return new N2I(Math.sqrt(this.valueOf()));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
cbrt(): N2I {
|
|
119
|
+
return new N2I(Math.cbrt(this.valueOf()));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
pow(exponent: N): N2I {
|
|
123
|
+
return new N2I(this.valueOf() ** exponent);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
root(degree: N): N2I {
|
|
127
|
+
if (degree === 0) throw new RangeError("Root degree cannot be zero");
|
|
128
|
+
const value = this.valueOf();
|
|
129
|
+
if (value < 0 && Number.isInteger(degree) && Math.abs(degree % 2) === 1) {
|
|
130
|
+
return new N2I(-((-value) ** (1 / degree)));
|
|
131
|
+
}
|
|
132
|
+
return new N2I(value ** (1 / degree));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
mod(divisor: N): N2I {
|
|
136
|
+
if (divisor === 0) throw new RangeError("Divisor cannot be zero");
|
|
137
|
+
return new N2I(((this.valueOf() % divisor) + divisor) % divisor);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
percentOf(total: N): N2I {
|
|
141
|
+
return new N2I((this.valueOf() / 100) * total);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
percentChange(from: N): N2I {
|
|
145
|
+
if (from === 0) throw new RangeError("Cannot calculate percentage change from zero");
|
|
146
|
+
return new N2I(((this.valueOf() - from) / Math.abs(from)) * 100);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
toRadians(): N2I {
|
|
150
|
+
return new N2I(this.valueOf() * (Math.PI / 180));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
toDegrees(): N2I {
|
|
154
|
+
return new N2I(this.valueOf() * (180 / Math.PI));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
toOrdinal(): S {
|
|
158
|
+
const value = this.valueOf();
|
|
159
|
+
const integer = Math.trunc(value);
|
|
160
|
+
const abs = Math.abs(integer);
|
|
161
|
+
const suffix = abs % 100 >= 11 && abs % 100 <= 13
|
|
162
|
+
? "th"
|
|
163
|
+
: ({ 1: "st", 2: "nd", 3: "rd" } as Record<N, S>)[abs % 10] ?? "th";
|
|
164
|
+
return `${value}${suffix}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
toBytes(unit?: N, decimals: N = 2): S {
|
|
168
|
+
const value = this.valueOf();
|
|
169
|
+
const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB"];
|
|
170
|
+
const index = unit === undefined
|
|
171
|
+
? value === 0 ? 0 : Math.min(Math.floor(Math.log(Math.abs(value)) / Math.log(1024)), units.length - 1)
|
|
172
|
+
: Math.max(0, Math.min(Math.trunc(unit), units.length - 1));
|
|
173
|
+
return `${(value / 1024 ** index).toFixed(decimals)} ${units[index]}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
toCompact(locales?: Intl.LocalesArgument): S {
|
|
177
|
+
return new Intl.NumberFormat(locales, { notation: "compact", maximumFractionDigits: 2 }).format(this.valueOf());
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
toCurrency(currency: S = "USD", locales?: Intl.LocalesArgument): S {
|
|
181
|
+
return new Intl.NumberFormat(locales, { style: "currency", currency }).format(this.valueOf());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
toPercent(decimals: N = 0, locales?: Intl.LocalesArgument): S {
|
|
185
|
+
return new Intl.NumberFormat(locales, {
|
|
186
|
+
style: "percent",
|
|
187
|
+
maximumFractionDigits: decimals,
|
|
188
|
+
}).format(this.valueOf());
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
times(fn: (index: N) => void): this {
|
|
192
|
+
const count = this.valueOf();
|
|
193
|
+
for (let i = 0; i < count; i++) fn(i);
|
|
194
|
+
return this;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
upTo(end: N, step: N = 1): AR<N> {
|
|
198
|
+
if (step <= 0) throw new RangeError("step must be positive");
|
|
199
|
+
const out: N[] = [];
|
|
200
|
+
for (let value = this.valueOf(); value <= end; value += step) out.push(value);
|
|
201
|
+
return out as AR<N>;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
downTo(end: N, step: N = 1): AR<N> {
|
|
205
|
+
if (step <= 0) throw new RangeError("step must be positive");
|
|
206
|
+
const out: N[] = [];
|
|
207
|
+
for (let value = this.valueOf(); value >= end; value -= step) out.push(value);
|
|
208
|
+
return out as AR<N>;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
globalThis.N2 = N2I;
|
package/src/object.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export class O2I {
|
|
4
|
+
static isEmpty(value: object): B {
|
|
5
|
+
return Reflect.ownKeys(value).length === 0;
|
|
6
|
+
}
|
|
7
|
+
static isNotEmpty(value: object): B {
|
|
8
|
+
return Reflect.ownKeys(value).length !== 0;
|
|
9
|
+
}
|
|
10
|
+
static size(value: object): N {
|
|
11
|
+
return Reflect.ownKeys(value).length as N;
|
|
12
|
+
}
|
|
13
|
+
static isPlainObject(value: unknown): B {
|
|
14
|
+
if (value === null || typeof value !== "object") return false;
|
|
15
|
+
const prototype = Object.getPrototypeOf(value);
|
|
16
|
+
return prototype === Object.prototype || prototype === null;
|
|
17
|
+
}
|
|
18
|
+
static isObject(value: unknown): B {
|
|
19
|
+
return value !== null && typeof value === "object";
|
|
20
|
+
}
|
|
21
|
+
static get<X = unknown>(
|
|
22
|
+
value: unknown,
|
|
23
|
+
path: string | readonly PropertyKey[],
|
|
24
|
+
fallback?: X
|
|
25
|
+
): unknown | X {
|
|
26
|
+
const parts: readonly PropertyKey[] = typeof path === "string" ? path.split(".") : path;
|
|
27
|
+
let current = value;
|
|
28
|
+
|
|
29
|
+
for (const key of parts) {
|
|
30
|
+
if (current === null || (typeof current !== "object" && typeof current !== "function")) {
|
|
31
|
+
return fallback;
|
|
32
|
+
}
|
|
33
|
+
current = (current as Record<PropertyKey, unknown>)[key];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return current === undefined ? fallback : current;
|
|
37
|
+
}
|
|
38
|
+
static getOr<X>(value: unknown, path: string | readonly PropertyKey[], fallback: X): X {
|
|
39
|
+
return this.get(value, path, fallback) as X;
|
|
40
|
+
}
|
|
41
|
+
static set<T extends object>(value: T, path: string | readonly PropertyKey[], next: unknown): T {
|
|
42
|
+
const parts: readonly PropertyKey[] = typeof path === "string" ? path.split(".") : path;
|
|
43
|
+
if (!parts.length) return value;
|
|
44
|
+
|
|
45
|
+
let current = value as Record<PropertyKey, unknown>;
|
|
46
|
+
|
|
47
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
48
|
+
const key = parts[i];
|
|
49
|
+
const child = current[key];
|
|
50
|
+
|
|
51
|
+
if (child === null || (typeof child !== "object" && typeof child !== "function")) {
|
|
52
|
+
current[key] = {};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
current = current[key] as Record<PropertyKey, unknown>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
current[parts[parts.length - 1]] = next;
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
static unset(value: object, path: string | readonly PropertyKey[]): B {
|
|
62
|
+
const parts: readonly PropertyKey[] = typeof path === "string" ? path.split(".") : path;
|
|
63
|
+
if (!parts.length) return false;
|
|
64
|
+
|
|
65
|
+
let current = value as Record<PropertyKey, unknown>;
|
|
66
|
+
|
|
67
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
68
|
+
const child = current[parts[i]];
|
|
69
|
+
if (child === null || typeof child !== "object") return false;
|
|
70
|
+
current = child as Record<PropertyKey, unknown>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return Reflect.deleteProperty(current, parts[parts.length - 1]);
|
|
74
|
+
}
|
|
75
|
+
static hasPath(value: unknown, path: string | readonly PropertyKey[]): B {
|
|
76
|
+
const parts: readonly PropertyKey[] = typeof path === "string" ? path.split(".") : path;
|
|
77
|
+
let current = value;
|
|
78
|
+
|
|
79
|
+
for (const key of parts) {
|
|
80
|
+
if (current === null || (typeof current !== "object" && typeof current !== "function")) {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
if (!Object.prototype.hasOwnProperty.call(current, key)) return false;
|
|
84
|
+
current = (current as Record<PropertyKey, unknown>)[key];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
static clone<T>(value: T): T {
|
|
90
|
+
if (value === null || typeof value !== "object") return value;
|
|
91
|
+
if (Array.isArray(value)) return value.slice() as T;
|
|
92
|
+
return Object.assign(Object.create(Object.getPrototypeOf(value)), value);
|
|
93
|
+
}
|
|
94
|
+
static deepClone<T>(value: T): T {
|
|
95
|
+
return structuredClone(value);
|
|
96
|
+
}
|
|
97
|
+
static deepMerge<T extends object, U extends object>(target: T, source: U): T & U {
|
|
98
|
+
const result = { ...target } as Record<PropertyKey, unknown>;
|
|
99
|
+
|
|
100
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
101
|
+
const incoming = source[key as keyof U];
|
|
102
|
+
const current = result[key];
|
|
103
|
+
|
|
104
|
+
result[key] =
|
|
105
|
+
this.isPlainObject(current) && this.isPlainObject(incoming)
|
|
106
|
+
? this.deepMerge(
|
|
107
|
+
current as Record<PropertyKey, unknown>,
|
|
108
|
+
incoming as Record<PropertyKey, unknown>
|
|
109
|
+
)
|
|
110
|
+
: incoming;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return result as T & U;
|
|
114
|
+
}
|
|
115
|
+
static defaults<T extends object, U extends object>(target: T, defaults: U): T & U {
|
|
116
|
+
return Object.assign({}, defaults, target) as T & U;
|
|
117
|
+
}
|
|
118
|
+
static getOrCreate<T extends object, K extends PropertyKey, X>(
|
|
119
|
+
value: T,
|
|
120
|
+
key: K,
|
|
121
|
+
create: () => X
|
|
122
|
+
): T & Record<K, X> {
|
|
123
|
+
const object = value as T & Record<K, X>;
|
|
124
|
+
|
|
125
|
+
if (!Object.prototype.hasOwnProperty.call(object, key) || object[key] === undefined) {
|
|
126
|
+
object[key] = create() as (T & Record<K, X>)[K];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return object;
|
|
130
|
+
}
|
|
131
|
+
static pick<T extends object, K extends keyof T>(value: T, keys: readonly K[]): Pick<T, K> {
|
|
132
|
+
const result = {} as Pick<T, K>;
|
|
133
|
+
|
|
134
|
+
for (const key of keys) {
|
|
135
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) result[key] = value[key];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
static omit<T extends object, K extends keyof T>(value: T, keys: readonly K[]): Omit<T, K> {
|
|
141
|
+
const result = { ...value };
|
|
142
|
+
|
|
143
|
+
for (const key of keys) delete result[key];
|
|
144
|
+
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
static pickBy<T extends object>(
|
|
148
|
+
value: T,
|
|
149
|
+
predicate: (value: T[keyof T], key: keyof T) => B
|
|
150
|
+
): Partial<T> {
|
|
151
|
+
const result: Partial<T> = {};
|
|
152
|
+
|
|
153
|
+
for (const key of Object.keys(value) as (keyof T)[]) {
|
|
154
|
+
if (predicate(value[key], key)) result[key] = value[key];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
static omitBy<T extends object>(
|
|
160
|
+
value: T,
|
|
161
|
+
predicate: (value: T[keyof T], key: keyof T) => B
|
|
162
|
+
): Partial<T> {
|
|
163
|
+
const result: Partial<T> = {};
|
|
164
|
+
|
|
165
|
+
for (const key of Object.keys(value) as (keyof T)[]) {
|
|
166
|
+
if (!predicate(value[key], key)) result[key] = value[key];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return result;
|
|
170
|
+
}
|
|
171
|
+
static mapKeys<T extends object, K extends PropertyKey>(
|
|
172
|
+
value: T,
|
|
173
|
+
mapper: (key: keyof T, value: T[keyof T]) => K
|
|
174
|
+
): Partial<Record<K, T[keyof T]>> {
|
|
175
|
+
const result: Partial<Record<K, T[keyof T]>> = {};
|
|
176
|
+
|
|
177
|
+
for (const key of Object.keys(value) as (keyof T)[]) {
|
|
178
|
+
result[mapper(key, value[key])] = value[key];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
static mapValues<T extends object, X>(
|
|
184
|
+
value: T,
|
|
185
|
+
mapper: (value: T[keyof T], key: keyof T) => X
|
|
186
|
+
): { [K in keyof T]: X } {
|
|
187
|
+
const result = {} as { [K in keyof T]: X };
|
|
188
|
+
|
|
189
|
+
for (const key of Object.keys(value) as (keyof T)[]) {
|
|
190
|
+
result[key] = mapper(value[key], key);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return result;
|
|
194
|
+
}
|
|
195
|
+
static mapEntries<T extends object, K extends PropertyKey, X>(
|
|
196
|
+
value: T,
|
|
197
|
+
mapper: (entry: [keyof T, T[keyof T]]) => readonly [K, X]
|
|
198
|
+
): Partial<Record<K, X>> {
|
|
199
|
+
const result: Partial<Record<K, X>> = {};
|
|
200
|
+
|
|
201
|
+
for (const key of Object.keys(value) as (keyof T)[]) {
|
|
202
|
+
const [nextKey, nextValue] = mapper([key, value[key]]);
|
|
203
|
+
result[nextKey] = nextValue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
static filter<T extends object>(
|
|
209
|
+
value: T,
|
|
210
|
+
predicate: (value: T[keyof T], key: keyof T) => B
|
|
211
|
+
): Partial<T> {
|
|
212
|
+
return this.pickBy(value, predicate);
|
|
213
|
+
}
|
|
214
|
+
static isEqual(first: unknown, second: unknown): B {
|
|
215
|
+
if (Object.is(first, second)) return true;
|
|
216
|
+
|
|
217
|
+
if (
|
|
218
|
+
first === null || second === null ||
|
|
219
|
+
typeof first !== "object" || typeof second !== "object"
|
|
220
|
+
) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (first instanceof Date && second instanceof Date) {
|
|
225
|
+
return first.getTime() === second.getTime();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (Array.isArray(first) || Array.isArray(second)) {
|
|
229
|
+
return Array.isArray(first) && Array.isArray(second) &&
|
|
230
|
+
first.length === second.length &&
|
|
231
|
+
first.every((value, index) => this.isEqual(value, second[index]));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const firstKeys = Reflect.ownKeys(first);
|
|
235
|
+
const secondKeys = Reflect.ownKeys(second);
|
|
236
|
+
|
|
237
|
+
return firstKeys.length === secondKeys.length &&
|
|
238
|
+
firstKeys.every(key =>
|
|
239
|
+
secondKeys.includes(key) &&
|
|
240
|
+
this.isEqual(
|
|
241
|
+
(first as Record<PropertyKey, unknown>)[key],
|
|
242
|
+
(second as Record<PropertyKey, unknown>)[key]
|
|
243
|
+
)
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
static isShallowEqual(first: object, second: object): B {
|
|
247
|
+
if (Object.is(first, second)) return true;
|
|
248
|
+
|
|
249
|
+
const firstKeys = Reflect.ownKeys(first);
|
|
250
|
+
const secondKeys = Reflect.ownKeys(second);
|
|
251
|
+
|
|
252
|
+
return firstKeys.length === secondKeys.length &&
|
|
253
|
+
firstKeys.every(key =>
|
|
254
|
+
secondKeys.includes(key) &&
|
|
255
|
+
Object.is(
|
|
256
|
+
(first as Record<PropertyKey, unknown>)[key],
|
|
257
|
+
(second as Record<PropertyKey, unknown>)[key]
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
static hasAll<T extends object>(value: T, keys: readonly PropertyKey[]): B {
|
|
262
|
+
return keys.every(key => Object.prototype.hasOwnProperty.call(value, key));
|
|
263
|
+
}
|
|
264
|
+
static hasAny<T extends object>(value: T, keys: readonly PropertyKey[]): B {
|
|
265
|
+
return keys.some(key => Object.prototype.hasOwnProperty.call(value, key));
|
|
266
|
+
}
|
|
267
|
+
static assertKeys<T extends object>(value: T, keys: readonly PropertyKey[]): asserts value is T {
|
|
268
|
+
const missing = keys.filter(key => !Object.prototype.hasOwnProperty.call(value, key));
|
|
269
|
+
|
|
270
|
+
if (missing.length) {
|
|
271
|
+
throw new TypeError(`Missing required keys: ${missing.map(String).join(", ")}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
static with<T extends object, K extends keyof T>(value: T, key: K, next: T[K]): T {
|
|
275
|
+
return { ...value, [key]: next };
|
|
276
|
+
}
|
|
277
|
+
static without<T extends object, K extends keyof T>(value: T, key: K): Omit<T, K> {
|
|
278
|
+
const result = { ...value };
|
|
279
|
+
delete result[key];
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
static update<T extends object, K extends keyof T>(
|
|
283
|
+
value: T,
|
|
284
|
+
key: K,
|
|
285
|
+
updater: (current: T[K]) => T[K]
|
|
286
|
+
): T {
|
|
287
|
+
return { ...value, [key]: updater(value[key]) };
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
globalThis.O2 = O2I;
|