@sylphx/flow 2.18.1 → 2.18.3
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/CHANGELOG.md +14 -0
- package/assets/slash-commands/continue.md +31 -63
- package/assets/slash-commands/review.md +17 -13
- package/package.json +1 -1
- package/src/core/backup-manager.ts +3 -14
- package/src/core/secrets-manager.ts +3 -14
- package/src/core/target-resolver.ts +28 -0
- package/src/utils/index.ts +1 -5
- package/src/utils/functional/array.ts +0 -355
- package/src/utils/functional/index.ts +0 -15
- package/src/utils/functional/object.ts +0 -279
- package/src/utils/functional/string.ts +0 -281
- package/src/utils/functional.ts +0 -543
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Functional array utilities
|
|
3
|
-
* Pure array transformation functions
|
|
4
|
-
*
|
|
5
|
-
* DESIGN RATIONALE:
|
|
6
|
-
* - Pure functions for array operations
|
|
7
|
-
* - Composable transformations
|
|
8
|
-
* - Type-safe operations
|
|
9
|
-
* - No side effects (no mutations)
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Map over array
|
|
14
|
-
*/
|
|
15
|
-
export const map =
|
|
16
|
-
<T, U>(fn: (item: T, index: number) => U) =>
|
|
17
|
-
(arr: T[]): U[] =>
|
|
18
|
-
arr.map(fn);
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Filter array
|
|
22
|
-
*/
|
|
23
|
-
export const filter =
|
|
24
|
-
<T>(predicate: (item: T, index: number) => boolean) =>
|
|
25
|
-
(arr: T[]): T[] =>
|
|
26
|
-
arr.filter(predicate);
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Reduce array
|
|
30
|
-
*/
|
|
31
|
-
export const reduce =
|
|
32
|
-
<T, U>(fn: (acc: U, item: T, index: number) => U, initial: U) =>
|
|
33
|
-
(arr: T[]): U =>
|
|
34
|
-
arr.reduce(fn, initial);
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Find first element matching predicate
|
|
38
|
-
*/
|
|
39
|
-
export const find =
|
|
40
|
-
<T>(predicate: (item: T, index: number) => boolean) =>
|
|
41
|
-
(arr: T[]): T | undefined =>
|
|
42
|
-
arr.find(predicate);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Find index of first element matching predicate
|
|
46
|
-
*/
|
|
47
|
-
export const findIndex =
|
|
48
|
-
<T>(predicate: (item: T, index: number) => boolean) =>
|
|
49
|
-
(arr: T[]): number =>
|
|
50
|
-
arr.findIndex(predicate);
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Check if any element matches predicate
|
|
54
|
-
*/
|
|
55
|
-
export const some =
|
|
56
|
-
<T>(predicate: (item: T, index: number) => boolean) =>
|
|
57
|
-
(arr: T[]): boolean =>
|
|
58
|
-
arr.some(predicate);
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Check if all elements match predicate
|
|
62
|
-
*/
|
|
63
|
-
export const every =
|
|
64
|
-
<T>(predicate: (item: T, index: number) => boolean) =>
|
|
65
|
-
(arr: T[]): boolean =>
|
|
66
|
-
arr.every(predicate);
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Flatten array one level
|
|
70
|
-
*/
|
|
71
|
-
export const flatten = <T>(arr: T[][]): T[] => arr.flat();
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Flatten array deeply
|
|
75
|
-
*/
|
|
76
|
-
export const flattenDeep = <T>(arr: any[]): T[] => arr.flat(Number.POSITIVE_INFINITY);
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Map and flatten (flatMap)
|
|
80
|
-
*/
|
|
81
|
-
export const flatMap =
|
|
82
|
-
<T, U>(fn: (item: T, index: number) => U[]) =>
|
|
83
|
-
(arr: T[]): U[] =>
|
|
84
|
-
arr.flatMap(fn);
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Take first n elements
|
|
88
|
-
*/
|
|
89
|
-
export const take =
|
|
90
|
-
(n: number) =>
|
|
91
|
-
<T>(arr: T[]): T[] =>
|
|
92
|
-
arr.slice(0, n);
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Skip first n elements
|
|
96
|
-
*/
|
|
97
|
-
export const skip =
|
|
98
|
-
(n: number) =>
|
|
99
|
-
<T>(arr: T[]): T[] =>
|
|
100
|
-
arr.slice(n);
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Take while predicate is true
|
|
104
|
-
*/
|
|
105
|
-
export const takeWhile =
|
|
106
|
-
<T>(predicate: (item: T) => boolean) =>
|
|
107
|
-
(arr: T[]): T[] => {
|
|
108
|
-
const index = arr.findIndex((item) => !predicate(item));
|
|
109
|
-
return index === -1 ? arr : arr.slice(0, index);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Skip while predicate is true
|
|
114
|
-
*/
|
|
115
|
-
export const skipWhile =
|
|
116
|
-
<T>(predicate: (item: T) => boolean) =>
|
|
117
|
-
(arr: T[]): T[] => {
|
|
118
|
-
const index = arr.findIndex((item) => !predicate(item));
|
|
119
|
-
return index === -1 ? [] : arr.slice(index);
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Reverse array
|
|
124
|
-
*/
|
|
125
|
-
export const reverse = <T>(arr: T[]): T[] => [...arr].reverse();
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Sort array
|
|
129
|
-
*/
|
|
130
|
-
export const sort =
|
|
131
|
-
<T>(compareFn?: (a: T, b: T) => number) =>
|
|
132
|
-
(arr: T[]): T[] =>
|
|
133
|
-
[...arr].sort(compareFn);
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Sort by key
|
|
137
|
-
*/
|
|
138
|
-
export const sortBy =
|
|
139
|
-
<T, K extends keyof T>(key: K, order: 'asc' | 'desc' = 'asc') =>
|
|
140
|
-
(arr: T[]): T[] => {
|
|
141
|
-
return [...arr].sort((a, b) => {
|
|
142
|
-
const aVal = a[key];
|
|
143
|
-
const bVal = b[key];
|
|
144
|
-
|
|
145
|
-
if (aVal < bVal) {
|
|
146
|
-
return order === 'asc' ? -1 : 1;
|
|
147
|
-
}
|
|
148
|
-
if (aVal > bVal) {
|
|
149
|
-
return order === 'asc' ? 1 : -1;
|
|
150
|
-
}
|
|
151
|
-
return 0;
|
|
152
|
-
});
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Remove duplicates
|
|
157
|
-
*/
|
|
158
|
-
export const unique = <T>(arr: T[]): T[] => [...new Set(arr)];
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Remove duplicates by key
|
|
162
|
-
*/
|
|
163
|
-
export const uniqueBy =
|
|
164
|
-
<T, K extends keyof T>(key: K) =>
|
|
165
|
-
(arr: T[]): T[] => {
|
|
166
|
-
const seen = new Set();
|
|
167
|
-
return arr.filter((item) => {
|
|
168
|
-
const value = item[key];
|
|
169
|
-
if (seen.has(value)) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
seen.add(value);
|
|
173
|
-
return true;
|
|
174
|
-
});
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Partition array into two based on predicate
|
|
179
|
-
*/
|
|
180
|
-
export const partition =
|
|
181
|
-
<T>(predicate: (item: T) => boolean) =>
|
|
182
|
-
(arr: T[]): [T[], T[]] => {
|
|
183
|
-
const pass: T[] = [];
|
|
184
|
-
const fail: T[] = [];
|
|
185
|
-
|
|
186
|
-
for (const item of arr) {
|
|
187
|
-
if (predicate(item)) {
|
|
188
|
-
pass.push(item);
|
|
189
|
-
} else {
|
|
190
|
-
fail.push(item);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return [pass, fail];
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Group by key
|
|
199
|
-
*/
|
|
200
|
-
export const groupBy =
|
|
201
|
-
<T, K extends keyof T>(key: K) =>
|
|
202
|
-
(arr: T[]): Record<string, T[]> => {
|
|
203
|
-
return arr.reduce(
|
|
204
|
-
(acc, item) => {
|
|
205
|
-
const groupKey = String(item[key]);
|
|
206
|
-
if (!acc[groupKey]) {
|
|
207
|
-
acc[groupKey] = [];
|
|
208
|
-
}
|
|
209
|
-
acc[groupKey].push(item);
|
|
210
|
-
return acc;
|
|
211
|
-
},
|
|
212
|
-
{} as Record<string, T[]>
|
|
213
|
-
);
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Count occurrences
|
|
218
|
-
*/
|
|
219
|
-
export const countBy =
|
|
220
|
-
<T>(fn: (item: T) => string) =>
|
|
221
|
-
(arr: T[]): Record<string, number> => {
|
|
222
|
-
return arr.reduce(
|
|
223
|
-
(acc, item) => {
|
|
224
|
-
const key = fn(item);
|
|
225
|
-
acc[key] = (acc[key] || 0) + 1;
|
|
226
|
-
return acc;
|
|
227
|
-
},
|
|
228
|
-
{} as Record<string, number>
|
|
229
|
-
);
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Chunk array into smaller arrays
|
|
234
|
-
*/
|
|
235
|
-
export const chunk =
|
|
236
|
-
(size: number) =>
|
|
237
|
-
<T>(arr: T[]): T[][] => {
|
|
238
|
-
const chunks: T[][] = [];
|
|
239
|
-
for (let i = 0; i < arr.length; i += size) {
|
|
240
|
-
chunks.push(arr.slice(i, i + size));
|
|
241
|
-
}
|
|
242
|
-
return chunks;
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Zip arrays together
|
|
247
|
-
*/
|
|
248
|
-
export const zip = <T, U>(arr1: T[], arr2: U[]): [T, U][] => {
|
|
249
|
-
const length = Math.min(arr1.length, arr2.length);
|
|
250
|
-
const result: [T, U][] = [];
|
|
251
|
-
|
|
252
|
-
for (let i = 0; i < length; i++) {
|
|
253
|
-
result.push([arr1[i], arr2[i]]);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
return result;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Unzip array of tuples
|
|
261
|
-
*/
|
|
262
|
-
export const unzip = <T, U>(arr: [T, U][]): [T[], U[]] => {
|
|
263
|
-
const first: T[] = [];
|
|
264
|
-
const second: U[] = [];
|
|
265
|
-
|
|
266
|
-
for (const [a, b] of arr) {
|
|
267
|
-
first.push(a);
|
|
268
|
-
second.push(b);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return [first, second];
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Intersperse value between array elements
|
|
276
|
-
*/
|
|
277
|
-
export const intersperse =
|
|
278
|
-
<T>(separator: T) =>
|
|
279
|
-
(arr: T[]): T[] => {
|
|
280
|
-
if (arr.length === 0) {
|
|
281
|
-
return [];
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const result: T[] = [arr[0]];
|
|
285
|
-
for (let i = 1; i < arr.length; i++) {
|
|
286
|
-
result.push(separator, arr[i]);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return result;
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Get first element
|
|
294
|
-
*/
|
|
295
|
-
export const head = <T>(arr: T[]): T | undefined => arr[0];
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Get last element
|
|
299
|
-
*/
|
|
300
|
-
export const last = <T>(arr: T[]): T | undefined => arr[arr.length - 1];
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Get all but first element
|
|
304
|
-
*/
|
|
305
|
-
export const tail = <T>(arr: T[]): T[] => arr.slice(1);
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Get all but last element
|
|
309
|
-
*/
|
|
310
|
-
export const init = <T>(arr: T[]): T[] => arr.slice(0, -1);
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Check if array is empty
|
|
314
|
-
*/
|
|
315
|
-
export const isEmpty = <T>(arr: T[]): boolean => arr.length === 0;
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Check if array is not empty
|
|
319
|
-
*/
|
|
320
|
-
export const isNotEmpty = <T>(arr: T[]): boolean => arr.length > 0;
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Sum numbers in array
|
|
324
|
-
*/
|
|
325
|
-
export const sum = (arr: number[]): number => arr.reduce((a, b) => a + b, 0);
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Get average of numbers in array
|
|
329
|
-
*/
|
|
330
|
-
export const average = (arr: number[]): number => {
|
|
331
|
-
if (arr.length === 0) {
|
|
332
|
-
return 0;
|
|
333
|
-
}
|
|
334
|
-
return sum(arr) / arr.length;
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Get min value
|
|
339
|
-
*/
|
|
340
|
-
export const min = (arr: number[]): number | undefined => {
|
|
341
|
-
if (arr.length === 0) {
|
|
342
|
-
return undefined;
|
|
343
|
-
}
|
|
344
|
-
return Math.min(...arr);
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Get max value
|
|
349
|
-
*/
|
|
350
|
-
export const max = (arr: number[]): number | undefined => {
|
|
351
|
-
if (arr.length === 0) {
|
|
352
|
-
return undefined;
|
|
353
|
-
}
|
|
354
|
-
return Math.max(...arr);
|
|
355
|
-
};
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Functional utilities
|
|
3
|
-
* Pure, composable utility functions
|
|
4
|
-
*
|
|
5
|
-
* DESIGN RATIONALE:
|
|
6
|
-
* - Pure functions for common operations
|
|
7
|
-
* - Composable through currying
|
|
8
|
-
* - Type-safe
|
|
9
|
-
* - No side effects
|
|
10
|
-
* - Point-free style support
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export * as Arr from './array.js';
|
|
14
|
-
export * as Obj from './object.js';
|
|
15
|
-
export * as Str from './string.js';
|
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Functional object utilities
|
|
3
|
-
* Pure object transformation functions
|
|
4
|
-
*
|
|
5
|
-
* DESIGN RATIONALE:
|
|
6
|
-
* - Pure functions for object operations
|
|
7
|
-
* - Immutable transformations
|
|
8
|
-
* - Type-safe operations
|
|
9
|
-
* - No side effects
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get keys of object
|
|
14
|
-
*/
|
|
15
|
-
export const keys = <T extends object>(obj: T): Array<keyof T> => {
|
|
16
|
-
return Object.keys(obj) as Array<keyof T>;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Get values of object
|
|
21
|
-
*/
|
|
22
|
-
export const values = <T extends object>(obj: T): T[keyof T][] => {
|
|
23
|
-
return Object.values(obj);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get entries of object
|
|
28
|
-
*/
|
|
29
|
-
export const entries = <T extends object>(obj: T): [keyof T, T[keyof T]][] => {
|
|
30
|
-
return Object.entries(obj) as [keyof T, T[keyof T]][];
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Create object from entries
|
|
35
|
-
*/
|
|
36
|
-
export const fromEntries = <K extends string | number | symbol, V>(
|
|
37
|
-
entries: [K, V][]
|
|
38
|
-
): Record<K, V> => {
|
|
39
|
-
return Object.fromEntries(entries) as Record<K, V>;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Pick properties from object
|
|
44
|
-
*/
|
|
45
|
-
export const pick =
|
|
46
|
-
<T extends object, K extends keyof T>(keys: K[]) =>
|
|
47
|
-
(obj: T): Pick<T, K> => {
|
|
48
|
-
const result = {} as Pick<T, K>;
|
|
49
|
-
for (const key of keys) {
|
|
50
|
-
if (key in obj) {
|
|
51
|
-
result[key] = obj[key];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return result;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Omit properties from object
|
|
59
|
-
*/
|
|
60
|
-
export const omit =
|
|
61
|
-
<T extends object, K extends keyof T>(keys: K[]) =>
|
|
62
|
-
(obj: T): Omit<T, K> => {
|
|
63
|
-
const result = { ...obj };
|
|
64
|
-
for (const key of keys) {
|
|
65
|
-
delete result[key];
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Map over object values
|
|
72
|
-
*/
|
|
73
|
-
export const mapValues =
|
|
74
|
-
<T extends object, U>(fn: (value: T[keyof T], key: keyof T) => U) =>
|
|
75
|
-
(obj: T): Record<keyof T, U> => {
|
|
76
|
-
const result = {} as Record<keyof T, U>;
|
|
77
|
-
for (const [key, value] of entries(obj)) {
|
|
78
|
-
result[key] = fn(value, key);
|
|
79
|
-
}
|
|
80
|
-
return result;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Map over object keys
|
|
85
|
-
*/
|
|
86
|
-
export const mapKeys =
|
|
87
|
-
<T extends object, K extends string | number | symbol>(
|
|
88
|
-
fn: (key: keyof T, value: T[keyof T]) => K
|
|
89
|
-
) =>
|
|
90
|
-
(obj: T): Record<K, T[keyof T]> => {
|
|
91
|
-
const result = {} as Record<K, T[keyof T]>;
|
|
92
|
-
for (const [key, value] of entries(obj)) {
|
|
93
|
-
const newKey = fn(key, value);
|
|
94
|
-
result[newKey] = value;
|
|
95
|
-
}
|
|
96
|
-
return result;
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Filter object by predicate
|
|
101
|
-
*/
|
|
102
|
-
export const filterObj =
|
|
103
|
-
<T extends object>(predicate: (value: T[keyof T], key: keyof T) => boolean) =>
|
|
104
|
-
(obj: T): Partial<T> => {
|
|
105
|
-
const result = {} as Partial<T>;
|
|
106
|
-
for (const [key, value] of entries(obj)) {
|
|
107
|
-
if (predicate(value, key)) {
|
|
108
|
-
result[key] = value;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
return result;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Merge objects (shallow)
|
|
116
|
-
*/
|
|
117
|
-
export const merge = <T extends object, U extends object>(obj1: T, obj2: U): T & U => {
|
|
118
|
-
return { ...obj1, ...obj2 };
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Deep merge objects
|
|
123
|
-
*/
|
|
124
|
-
export const deepMerge = <T extends object>(target: T, source: Partial<T>): T => {
|
|
125
|
-
const result = { ...target };
|
|
126
|
-
|
|
127
|
-
for (const key of keys(source)) {
|
|
128
|
-
const sourceValue = source[key];
|
|
129
|
-
const targetValue = result[key];
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
typeof sourceValue === 'object' &&
|
|
133
|
-
sourceValue !== null &&
|
|
134
|
-
!Array.isArray(sourceValue) &&
|
|
135
|
-
typeof targetValue === 'object' &&
|
|
136
|
-
targetValue !== null &&
|
|
137
|
-
!Array.isArray(targetValue)
|
|
138
|
-
) {
|
|
139
|
-
result[key] = deepMerge(targetValue, sourceValue as any);
|
|
140
|
-
} else if (sourceValue !== undefined) {
|
|
141
|
-
result[key] = sourceValue as T[Extract<keyof T, string>];
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return result;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Check if object has property
|
|
150
|
-
*/
|
|
151
|
-
export const has =
|
|
152
|
-
<T extends object>(key: PropertyKey) =>
|
|
153
|
-
(obj: T): boolean => {
|
|
154
|
-
return key in obj;
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Get property safely
|
|
159
|
-
*/
|
|
160
|
-
export const get =
|
|
161
|
-
<T extends object, K extends keyof T>(key: K) =>
|
|
162
|
-
(obj: T): T[K] | undefined => {
|
|
163
|
-
return obj[key];
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Get nested property safely
|
|
168
|
-
*/
|
|
169
|
-
export const getPath =
|
|
170
|
-
(path: string) =>
|
|
171
|
-
(obj: any): any => {
|
|
172
|
-
const keys = path.split('.');
|
|
173
|
-
let current = obj;
|
|
174
|
-
|
|
175
|
-
for (const key of keys) {
|
|
176
|
-
if (current === null || current === undefined) {
|
|
177
|
-
return undefined;
|
|
178
|
-
}
|
|
179
|
-
current = current[key];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return current;
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Set property immutably
|
|
187
|
-
*/
|
|
188
|
-
export const set =
|
|
189
|
-
<T extends object, K extends keyof T>(key: K, value: T[K]) =>
|
|
190
|
-
(obj: T): T => {
|
|
191
|
-
return { ...obj, [key]: value };
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Update property immutably
|
|
196
|
-
*/
|
|
197
|
-
export const update =
|
|
198
|
-
<T extends object, K extends keyof T>(key: K, fn: (value: T[K]) => T[K]) =>
|
|
199
|
-
(obj: T): T => {
|
|
200
|
-
return { ...obj, [key]: fn(obj[key]) };
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Check if object is empty
|
|
205
|
-
*/
|
|
206
|
-
export const isEmpty = <T extends object>(obj: T): boolean => {
|
|
207
|
-
return keys(obj).length === 0;
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Check if object is not empty
|
|
212
|
-
*/
|
|
213
|
-
export const isNotEmpty = <T extends object>(obj: T): boolean => {
|
|
214
|
-
return keys(obj).length > 0;
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Remove undefined values
|
|
219
|
-
*/
|
|
220
|
-
export const compact = <T extends object>(obj: T): Partial<T> => {
|
|
221
|
-
return filterObj((value: any) => value !== undefined)(obj);
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Remove null and undefined values
|
|
226
|
-
*/
|
|
227
|
-
export const compactAll = <T extends object>(obj: T): Partial<T> => {
|
|
228
|
-
return filterObj((value: any) => value !== null && value !== undefined)(obj);
|
|
229
|
-
};
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Invert object (swap keys and values)
|
|
233
|
-
*/
|
|
234
|
-
export const invert = <T extends Record<string, string | number>>(
|
|
235
|
-
obj: T
|
|
236
|
-
): Record<string, string> => {
|
|
237
|
-
const result: Record<string, string> = {};
|
|
238
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
239
|
-
result[String(value)] = key;
|
|
240
|
-
}
|
|
241
|
-
return result;
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Deep clone object
|
|
246
|
-
*/
|
|
247
|
-
export const clone = <T>(obj: T): T => {
|
|
248
|
-
if (obj === null || typeof obj !== 'object') {
|
|
249
|
-
return obj;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (Array.isArray(obj)) {
|
|
253
|
-
return obj.map(clone) as any;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const cloned = {} as T;
|
|
257
|
-
for (const key in obj) {
|
|
258
|
-
if (Object.hasOwn(obj, key)) {
|
|
259
|
-
cloned[key] = clone(obj[key]);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return cloned;
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Freeze object deeply (make immutable)
|
|
268
|
-
*/
|
|
269
|
-
export const deepFreeze = <T extends object>(obj: T): Readonly<T> => {
|
|
270
|
-
Object.freeze(obj);
|
|
271
|
-
|
|
272
|
-
for (const value of values(obj)) {
|
|
273
|
-
if (typeof value === 'object' && value !== null) {
|
|
274
|
-
deepFreeze(value);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return obj;
|
|
279
|
-
};
|