@hichchi/utils 0.0.1-beta.2 → 0.0.2

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.
Files changed (90) hide show
  1. package/index.cjs.default.js +1 -0
  2. package/index.cjs.js +3645 -0
  3. package/index.cjs.mjs +2 -0
  4. package/index.d.ts +1 -5
  5. package/index.esm.js +3575 -0
  6. package/package.json +13 -8
  7. package/src/index.d.ts +5 -0
  8. package/{interfaces → src/interfaces}/infinite-object.interface.d.ts +3 -3
  9. package/{interfaces → src/interfaces}/path-value-set.interface.d.ts +12 -12
  10. package/{types → src/types}/deep-partial.type.d.ts +6 -0
  11. package/{types → src/types}/literal-object.type.d.ts +2 -2
  12. package/{utils → src/utils}/index.d.ts +1 -0
  13. package/{utils → src/utils}/object.utils.d.ts +23 -19
  14. package/CHANGELOG.md +0 -19
  15. package/README.md +0 -7944
  16. package/constants/constants.js +0 -227
  17. package/constants/constants.js.map +0 -1
  18. package/constants/english-inflection-rules.js +0 -360
  19. package/constants/english-inflection-rules.js.map +0 -1
  20. package/constants/index.js +0 -6
  21. package/constants/index.js.map +0 -1
  22. package/enums/index.js +0 -5
  23. package/enums/index.js.map +0 -1
  24. package/enums/template-tag.enum.js +0 -39
  25. package/enums/template-tag.enum.js.map +0 -1
  26. package/index.js +0 -9
  27. package/index.js.map +0 -1
  28. package/interfaces/index.js +0 -7
  29. package/interfaces/index.js.map +0 -1
  30. package/interfaces/infinite-object.interface.js +0 -3
  31. package/interfaces/infinite-object.interface.js.map +0 -1
  32. package/interfaces/inflection-rule.interfaces.js +0 -3
  33. package/interfaces/inflection-rule.interfaces.js.map +0 -1
  34. package/interfaces/path-value-set.interface.js +0 -3
  35. package/interfaces/path-value-set.interface.js.map +0 -1
  36. package/readme-top.md +0 -187
  37. package/types/deep-partial.type.js +0 -3
  38. package/types/deep-partial.type.js.map +0 -1
  39. package/types/index.js +0 -38
  40. package/types/index.js.map +0 -1
  41. package/types/is-already-in-path.type.js +0 -3
  42. package/types/is-already-in-path.type.js.map +0 -1
  43. package/types/is-empty.type.js +0 -3
  44. package/types/is-empty.type.js.map +0 -1
  45. package/types/is-primitive.type.js +0 -4
  46. package/types/is-primitive.type.js.map +0 -1
  47. package/types/literal-object.type.js +0 -3
  48. package/types/literal-object.type.js.map +0 -1
  49. package/types/loose-autocomplete.type.js +0 -3
  50. package/types/loose-autocomplete.type.js.map +0 -1
  51. package/types/partial-with-null.type.js +0 -3
  52. package/types/partial-with-null.type.js.map +0 -1
  53. package/types/prettify.type.js +0 -3
  54. package/types/prettify.type.js.map +0 -1
  55. package/types/type.type.js +0 -3
  56. package/types/type.type.js.map +0 -1
  57. package/utils/assertions.utils.js +0 -163
  58. package/utils/assertions.utils.js.map +0 -1
  59. package/utils/file.utils.js +0 -1315
  60. package/utils/file.utils.js.map +0 -1
  61. package/utils/index.js +0 -9
  62. package/utils/index.js.map +0 -1
  63. package/utils/object.utils.js +0 -1069
  64. package/utils/object.utils.js.map +0 -1
  65. package/utils/string-template.utils.js +0 -269
  66. package/utils/string-template.utils.js.map +0 -1
  67. package/utils/string.utils.js +0 -1255
  68. package/utils/string.utils.js.map +0 -1
  69. package/utils/url.utils.js +0 -112
  70. package/utils/url.utils.js.map +0 -1
  71. /package/{constants → src/constants}/constants.d.ts +0 -0
  72. /package/{constants → src/constants}/english-inflection-rules.d.ts +0 -0
  73. /package/{constants → src/constants}/index.d.ts +0 -0
  74. /package/{enums → src/enums}/index.d.ts +0 -0
  75. /package/{enums → src/enums}/template-tag.enum.d.ts +0 -0
  76. /package/{interfaces → src/interfaces}/index.d.ts +0 -0
  77. /package/{interfaces → src/interfaces}/inflection-rule.interfaces.d.ts +0 -0
  78. /package/{types → src/types}/index.d.ts +0 -0
  79. /package/{types → src/types}/is-already-in-path.type.d.ts +0 -0
  80. /package/{types → src/types}/is-empty.type.d.ts +0 -0
  81. /package/{types → src/types}/is-primitive.type.d.ts +0 -0
  82. /package/{types → src/types}/loose-autocomplete.type.d.ts +0 -0
  83. /package/{types → src/types}/partial-with-null.type.d.ts +0 -0
  84. /package/{types → src/types}/prettify.type.d.ts +0 -0
  85. /package/{types → src/types}/type.type.d.ts +0 -0
  86. /package/{utils → src/utils}/assertions.utils.d.ts +0 -0
  87. /package/{utils → src/utils}/file.utils.d.ts +0 -0
  88. /package/{utils → src/utils}/string-template.utils.d.ts +0 -0
  89. /package/{utils → src/utils}/string.utils.d.ts +0 -0
  90. /package/{utils → src/utils}/url.utils.d.ts +0 -0
@@ -1,1069 +0,0 @@
1
- "use strict";
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
- // noinspection JSUnusedGlobalSymbols
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.prune = exports.omit = exports.getValueByPath = exports.searchMapValues = exports.groupBy = exports.getMapKeys = void 0;
6
- exports.deepCopy = deepCopy;
7
- exports.getMapKey = getMapKey;
8
- exports.objectToPathValueSet = objectToPathValueSet;
9
- exports.pathValueSetToObject = pathValueSetToObject;
10
- exports.hasOwnAll = hasOwnAll;
11
- exports.getEnumValues = getEnumValues;
12
- /**
13
- * Deep copy an object.
14
- * @template T Type of the object.
15
- * @param {T} obj Object to copy.
16
- * @returns {T} Copied object.
17
- *
18
- * @example
19
- * ```TypeScript
20
- * // Example usage
21
- * const object = {
22
- * name: "John Doe"
23
- * }
24
- *
25
- * const copiedObject = deepCopy(object);
26
- * ```
27
- */
28
- function deepCopy(obj) {
29
- if (Array.isArray(obj)) {
30
- return obj.map(deepCopy);
31
- }
32
- else if (typeof obj === "object" && obj !== null) {
33
- return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, deepCopy(value)]));
34
- }
35
- return obj;
36
- }
37
- /**
38
- * Get the key of a map by value.
39
- * @param {Map<string, unknown>} map Map to get key from.
40
- * @param {unknown} value Value to get key for.
41
- * @returns {string | undefined} Key of the map.
42
- *
43
- * @example
44
- * ```TypeScript
45
- * // Example usage
46
- * const user = new Map<string, string>([
47
- * ["firstName", "John"],
48
- * ["lastName", "Doe"],
49
- * ["preferredName", "John"],
50
- * ["age", 30],
51
- * ]);
52
- *
53
- * const key = getMapKey(user, "value2");
54
- *
55
- * // Example output: "firstName"
56
- * ```
57
- */
58
- function getMapKey(map, value) {
59
- return [...Array.from(map.entries())].find(([, v]) => v === value)?.[0];
60
- }
61
- /**
62
- * Retrieves all keys from a Map where the corresponding values contain a specified substring.
63
- *
64
- * This utility function searches through a Map's values and returns an array of keys
65
- * whose associated values include the provided partial string. The search is case-sensitive
66
- * and uses JavaScript's native string.includes() method for matching.
67
- *
68
- * This is particularly useful for implementing search functionality, filtering operations,
69
- * or finding related entries in configuration maps, user preference stores, or any
70
- * key-value data structure where you need to locate entries by partial value matching.
71
- *
72
- * @param {Map<string, string>} map - The Map to search through for matching values
73
- * @param {string} partialValue - The substring to search for within the Map's values
74
- * @returns {string[]} An array of keys whose corresponding values contain the partial value
75
- *
76
- * @example
77
- * ```typescript
78
- * // Search user preferences for entries containing "John"
79
- * const userPreferences = new Map<string, string>([
80
- * ["displayName", "John Doe"],
81
- * ["firstName", "John"],
82
- * ["lastName", "Doe"],
83
- * ["preferredName", "Johnny"],
84
- * ["email", "john.doe@example.com"]
85
- * ]);
86
- *
87
- * const johnKeys = getMapKeys(userPreferences, "John");
88
- * // Returns: ["displayName", "firstName", "email"]
89
- * ```
90
- *
91
- * @example
92
- * ```typescript
93
- * // Find configuration keys related to database settings
94
- * const config = new Map<string, string>([
95
- * ["db_host", "localhost"],
96
- * ["db_port", "5432"],
97
- * ["cache_host", "redis-server"],
98
- * ["db_name", "myapp_database"],
99
- * ["log_level", "debug"]
100
- * ]);
101
- *
102
- * const dbKeys = getMapKeys(config, "db");
103
- * // Returns: ["db_name"] (only values containing "db", not keys)
104
- * ```
105
- *
106
- * @example
107
- * ```typescript
108
- * // Search in a translations map
109
- * const translations = new Map<string, string>([
110
- * ["welcome_message", "Welcome to our application"],
111
- * ["error_message", "An error occurred"],
112
- * ["success_message", "Operation completed successfully"],
113
- * ["info_message", "Please check your information"]
114
- * ]);
115
- *
116
- * const messageKeys = getMapKeys(translations, "message");
117
- * // Returns: ["welcome_message", "error_message", "success_message", "info_message"]
118
- * ```
119
- *
120
- * @remarks
121
- * - The search is case-sensitive; "John" will not match "john"
122
- * - Returns an empty array if no matches are found
123
- * - The function iterates through all Map entries, so performance scales with Map size
124
- * - Only works with Maps that have string values; other value types will cause runtime errors
125
- */
126
- const getMapKeys = (map, partialValue) => {
127
- const keys = [];
128
- for (const [key, value] of Array.from(map.entries())) {
129
- if (value.includes(partialValue)) {
130
- keys.push(key);
131
- }
132
- }
133
- return keys;
134
- };
135
- exports.getMapKeys = getMapKeys;
136
- /**
137
- * Groups an array of objects into a Map based on a key extraction function.
138
- *
139
- * This utility function takes an array of objects and organizes them into groups
140
- * based on a common key or property. The grouping is performed using a key extraction
141
- * function that you provide, which determines how each object should be categorized.
142
- * The result is a Map where each key represents a group and the value is an array
143
- * of objects belonging to that group.
144
- *
145
- * This is particularly useful for data analysis, reporting, organizing collections
146
- * for display purposes, or preparing data for further processing where items need
147
- * to be categorized by shared characteristics.
148
- *
149
- * @template K - The type of the grouping key (can be string, number, boolean, etc.)
150
- * @template V - The type of the objects being grouped
151
- * @param {Array<V>} list - The array of objects to group
152
- * @param {(input: V) => K} keyGetter - A function that extracts the grouping key from each object.
153
- * This function receives an object and should return the value
154
- * to group by. If the function returns null or undefined,
155
- * the object will be grouped under a null key.
156
- * @returns {Map<K | null, Array<V>>} A Map where keys are the grouping values and values are arrays
157
- * of objects that share that grouping key
158
- *
159
- * @example
160
- * ```typescript
161
- * // Group users by age
162
- * interface User {
163
- * name: string;
164
- * age: number;
165
- * department: string;
166
- * }
167
- *
168
- * const users: User[] = [
169
- * { name: "John", age: 30, department: "Engineering" },
170
- * { name: "Jane", age: 25, department: "Marketing" },
171
- * { name: "Bob", age: 30, department: "Engineering" },
172
- * { name: "Alice", age: 25, department: "Sales" },
173
- * { name: "Charlie", age: 35, department: "Engineering" }
174
- * ];
175
- *
176
- * const usersByAge = groupBy(users, user => user.age);
177
- * // Returns:
178
- * // Map {
179
- * // 30 => [
180
- * // { name: "John", age: 30, department: "Engineering" },
181
- * // { name: "Bob", age: 30, department: "Engineering" }
182
- * // ],
183
- * // 25 => [
184
- * // { name: "Jane", age: 25, department: "Marketing" },
185
- * // { name: "Alice", age: 25, department: "Sales" }
186
- * // ],
187
- * // 35 => [
188
- * // { name: "Charlie", age: 35, department: "Engineering" }
189
- * // ]
190
- * // }
191
- * ```
192
- *
193
- * @example
194
- * ```typescript
195
- * // Group products by category
196
- * interface Product {
197
- * id: number;
198
- * name: string;
199
- * category: string;
200
- * price: number;
201
- * }
202
- *
203
- * const products: Product[] = [
204
- * { id: 1, name: "Laptop", category: "Electronics", price: 999 },
205
- * { id: 2, name: "Book", category: "Education", price: 29 },
206
- * { id: 3, name: "Phone", category: "Electronics", price: 699 },
207
- * { id: 4, name: "Pen", category: "Office", price: 5 }
208
- * ];
209
- *
210
- * const productsByCategory = groupBy(products, product => product.category);
211
- * // Useful for creating category-based product listings
212
- * ```
213
- *
214
- * @example
215
- * ```typescript
216
- * // Group tasks by completion status with boolean keys
217
- * interface Task {
218
- * id: number;
219
- * title: string;
220
- * completed: boolean;
221
- * }
222
- *
223
- * const tasks: Task[] = [
224
- * { id: 1, title: "Review code", completed: true },
225
- * { id: 2, title: "Write tests", completed: false },
226
- * { id: 3, title: "Deploy app", completed: true },
227
- * { id: 4, title: "Update docs", completed: false }
228
- * ];
229
- *
230
- * const tasksByStatus = groupBy(tasks, task => task.completed);
231
- * // Returns Map with boolean keys: true and false
232
- * ```
233
- *
234
- * @remarks
235
- * - The function preserves the original order of objects within each group
236
- * - If the keyGetter function returns null or undefined, those objects will be grouped under a null key
237
- * - The Map structure allows for efficient lookups and iteration over groups
238
- * - Works with any type of grouping key (string, number, boolean, objects, etc.)
239
- * - Empty arrays will return an empty Map
240
- */
241
- const groupBy = (list, keyGetter) => {
242
- const map = new Map();
243
- list.forEach(item => {
244
- const key = keyGetter(item);
245
- const collection = map.get(key);
246
- if (!collection) {
247
- map.set(key, [item]);
248
- }
249
- else {
250
- collection.push(item);
251
- }
252
- });
253
- return map;
254
- };
255
- exports.groupBy = groupBy;
256
- /**
257
- * Retrieves all values from a Map that contain a specified substring.
258
- *
259
- * This utility function searches through a Map's values and returns an array of all values
260
- * that include the provided partial string. The search is case-sensitive and uses JavaScript's
261
- * native string.includes() method for matching. This is the counterpart to getMapKeys(),
262
- * but instead of returning the keys, it returns the actual values that match.
263
- *
264
- * This is particularly useful for implementing search functionality where you need to find
265
- * and display matching content, filtering data for autocomplete features, or extracting
266
- * related values from configuration maps, user data, or any key-value store where you
267
- * need to locate entries by partial content matching.
268
- *
269
- * @param {Map<string, string>} map - The Map to search through for matching values
270
- * @param {string} partialValue - The substring to search for within the Map's values
271
- * @returns {string[]} An array of values that contain the specified partial value
272
- *
273
- * @example
274
- * ```typescript
275
- * // Search user information for values containing "John"
276
- * const userInfo = new Map<string, string>([
277
- * ["fullName", "John Doe"],
278
- * ["firstName", "John"],
279
- * ["lastName", "Doe"],
280
- * ["nickname", "Johnny"],
281
- * ["email", "john.doe@example.com"],
282
- * ["phone", "555-0123"]
283
- * ]);
284
- *
285
- * const johnValues = searchMapValues(userInfo, "John");
286
- * // Returns: ["John Doe", "John", "Johnny", "john.doe@example.com"]
287
- * ```
288
- *
289
- * @example
290
- * ```typescript
291
- * // Find error messages containing specific keywords
292
- * const errorMessages = new Map<string, string>([
293
- * ["auth_failed", "Authentication failed. Please check your credentials."],
294
- * ["network_error", "Network connection failed. Please try again."],
295
- * ["validation_error", "Validation failed for the provided data."],
296
- * ["timeout_error", "Request timeout. The server took too long to respond."],
297
- * ["permission_denied", "Access denied. You don't have permission."]
298
- * ]);
299
- *
300
- * const failedMessages = searchMapValues(errorMessages, "failed");
301
- * // Returns: ["Authentication failed. Please check your credentials.",
302
- * // "Network connection failed. Please try again.",
303
- * // "Validation failed for the provided data."]
304
- * ```
305
- *
306
- * @example
307
- * ```typescript
308
- * // Search configuration values for environment-specific settings
309
- * const config = new Map<string, string>([
310
- * ["api_url", "https://api.production.example.com"],
311
- * ["db_host", "production-db.example.com"],
312
- * ["cache_url", "redis://production-cache.example.com"],
313
- * ["log_level", "error"],
314
- * ["debug_mode", "false"]
315
- * ]);
316
- *
317
- * const productionValues = searchMapValues(config, "production");
318
- * // Returns: ["https://api.production.example.com",
319
- * // "production-db.example.com",
320
- * // "redis://production-cache.example.com"]
321
- * ```
322
- *
323
- * @remarks
324
- * - The search is case-sensitive; "John" will not match "john"
325
- * - Returns an empty array if no matches are found
326
- * - The function iterates through all Map entries, so performance scales with Map size
327
- * - Only works with Maps that have string values; other value types will cause runtime errors
328
- * - Values are returned in the order they appear in the Map iteration
329
- *
330
- * @see {@link getMapKeys} Function to get keys whose values contain a substring
331
- */
332
- const searchMapValues = (map, partialValue) => {
333
- const values = [];
334
- for (const [, value] of Array.from(map.entries())) {
335
- if (value.includes(partialValue)) {
336
- values.push(value);
337
- }
338
- }
339
- return values;
340
- };
341
- exports.searchMapValues = searchMapValues;
342
- /**
343
- * Gets a value from a nested object using a dot-notation path string.
344
- *
345
- * This function safely traverses a deeply nested object structure using a string path
346
- * with dot notation. It handles both object properties and array indices within the path.
347
- *
348
- * @template T - Type of the value to be returned.
349
- * @param {InfiniteObject} obj - The object to retrieve the value from.
350
- * @param {string} path - The dot-notation path to the desired value.
351
- * - Use dots to navigate through nested objects: 'user.profile.address'
352
- * - Use array notation for accessing array elements: 'items[0]' or 'users[2].name'
353
- * @returns {T | undefined} - The value at the specified path, or undefined if:
354
- * - Any part of the path doesn't exist
355
- * - An array index is out of bounds
356
- * - The path format is invalid
357
- *
358
- * @example
359
- * ```typescript
360
- * // Simple nested object property
361
- * const user = {
362
- * profile: {
363
- * name: "John Doe",
364
- * contact: { email: "john@example.com" }
365
- * }
366
- * };
367
- * const email = getValueByPath<string>(user, "profile.contact.email");
368
- * // Returns: "john@example.com"
369
- * ```
370
- *
371
- * @example
372
- * ```typescript
373
- * // Accessing array elements
374
- * const data = {
375
- * users: [
376
- * { id: 1, name: "Alice" },
377
- * { id: 2, name: "Bob" }
378
- * ]
379
- * };
380
- * const name = getValueByPath<string>(data, "users[1].name");
381
- * // Returns: "Bob"
382
- * ```
383
- */
384
- const getValueByPath = (obj, path) => {
385
- const keys = path.split("."); // Split the path into an array of keys
386
- let value = obj;
387
- for (const key of keys) {
388
- // noinspection RegExpRedundantEscape
389
- const regExp = /^(\w+)\[(\d+)\]$/;
390
- const isArrayIndex = regExp.exec(key); // Check if the key is an array index
391
- if (isArrayIndex) {
392
- const arrayKey = isArrayIndex[1];
393
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
394
- const index = Number(isArrayIndex[2]);
395
- if (arrayKey && Array.isArray(value[arrayKey]) && index >= 0 && index < value[arrayKey].length) {
396
- value = value[arrayKey][index]; // Update the value to the array element
397
- }
398
- else {
399
- return undefined; // Return undefined if the array index is invalid
400
- }
401
- }
402
- else if (value && typeof value === "object" && key in value) {
403
- value = value[key]; // Update the value to the nested property
404
- }
405
- else {
406
- return undefined; // Return undefined if any key is not found
407
- }
408
- }
409
- return value;
410
- };
411
- exports.getValueByPath = getValueByPath;
412
- /**
413
- * Converts a nested object into a flattened PathValueSet representation.
414
- *
415
- * This function transforms a hierarchical object structure into a flat key-value map
416
- * where keys represent paths to values in the original object using dot notation.
417
- *
418
- * The function recursively traverses the object and flattens nested properties,
419
- * converting object hierarchies like `{ user: { name: 'John' } }` into
420
- * path-based entries like `{ 'user.name': 'John' }`.
421
- *
422
- * @param {LiteralObject} obj - The nested object to flatten
423
- * @returns {PathValueSet} - A flattened representation where:
424
- * - Keys are dot-notation paths to values in the original object
425
- * - Values are primitive values (strings, numbers, booleans) from the original object
426
- *
427
- * @remarks
428
- * - Array values will be preserved as-is (not flattened into separate paths)
429
- * - Only primitive values (string, number, boolean) are supported as leaf values
430
- * - Circular references are not handled and will cause a stack overflow
431
- *
432
- * @see {@link pathValueSetToObject} The inverse operation to convert a PathValueSet back to a nested object
433
- * @see {@link PathValueSet} The interface for the returned flattened object
434
- *
435
- * @example
436
- * ```typescript
437
- * // Flatten a nested user object
438
- * const user = {
439
- * id: 123,
440
- * name: "John Doe",
441
- * isActive: true,
442
- * profile: {
443
- * age: 30,
444
- * address: {
445
- * city: "New York",
446
- * zip: "10001"
447
- * }
448
- * }
449
- * };
450
- *
451
- * const flattened = objectToPathValueSet(user);
452
- *
453
- * // Result:
454
- * // {
455
- * // "id": 123,
456
- * // "name": "John Doe",
457
- * // "isActive": true,
458
- * // "profile.age": 30,
459
- * // "profile.address.city": "New York",
460
- * // "profile.address.zip": "10001"
461
- * // }
462
- * ```
463
- */
464
- function objectToPathValueSet(obj) {
465
- const result = {};
466
- function traverse(obj, path = []) {
467
- for (const key in obj) {
468
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
469
- const value = obj[key];
470
- if (typeof value === "object" && !Array.isArray(value)) {
471
- traverse(value, [...path, key]);
472
- }
473
- else {
474
- result[[...path, key].join(".")] = value;
475
- }
476
- }
477
- }
478
- }
479
- traverse(obj);
480
- return result;
481
- }
482
- /**
483
- * Converts a flattened PathValueSet back into a nested object structure.
484
- *
485
- * This function is the inverse of `objectToPathValueSet`. It takes a flat map of
486
- * dot-notation paths to values and reconstructs a hierarchical object structure.
487
- *
488
- * Each key in the input PathValueSet represents a path through the object hierarchy,
489
- * with dots separating each level. The function builds a nested object structure by
490
- * parsing these paths and placing values at the appropriate locations.
491
- *
492
- * @template R - The type of the returned object (defaults to object)
493
- * @param {PathValueSet} pathValueSet - A flattened object with dot-notation path keys
494
- * @returns {R} - A reconstructed nested object with the original hierarchy
495
- *
496
- * @remarks
497
- * - Paths are validated for safety to prevent injection attacks
498
- * - Invalid paths are silently skipped (not included in the result)
499
- * - Path components should contain only alphanumeric characters, underscores, hyphens, and dots
500
- *
501
- * @see {@link objectToPathValueSet} The inverse operation to convert an object to PathValueSet
502
- * @see {@link PathValueSet} The interface for the input flattened object
503
- *
504
- * @example
505
- * ```typescript
506
- * // Convert a flat PathValueSet to a nested object
507
- * const flatData = {
508
- * "id": 123,
509
- * "name": "John Doe",
510
- * "profile.age": 30,
511
- * "profile.address.city": "New York",
512
- * "profile.address.zip": "10001"
513
- * };
514
- *
515
- * const nestedObject = pathValueSetToObject(flatData);
516
- *
517
- * Result:
518
- * // {
519
- * // id: 123,
520
- * // name: "John Doe",
521
- * // profile: {
522
- * // age: 30,
523
- * // address: {
524
- * // city: "New York",
525
- * // zip: "10001"
526
- * // }
527
- * // }
528
- * // }
529
- * ```
530
- *
531
- * @example
532
- * ```typescript
533
- * // Typed return value
534
- * interface User {
535
- * id: number;
536
- * name: string;
537
- * profile: {
538
- * age: number;
539
- * address: {
540
- * city: string;
541
- * zip: string;
542
- * };
543
- * };
544
- * }
545
- *
546
- * const userData = pathValueSetToObject<User>(flatData);
547
- * // Returns object with User type
548
- * ```
549
- */
550
- function pathValueSetToObject(pathValueSet) {
551
- const object = {};
552
- // Helper function to validate paths
553
- const isValidPath = (path) => {
554
- const regex = /^[a-zA-Z0-9_.-]+$/;
555
- return path.split(".").every(part => regex.test(part));
556
- };
557
- // Helper function to set nested properties
558
- const setObjectValue = (obj, keys, value) => {
559
- const [firstKey, ...remainingKeys] = keys;
560
- if (remainingKeys.length === 0) {
561
- obj[firstKey] = value; // Set value at the final key
562
- return;
563
- }
564
- // Initialize the key if it doesn't exist
565
- obj[firstKey] = obj[firstKey] || {};
566
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
567
- setObjectValue(obj[firstKey], remainingKeys, value); // Recurse for the rest of the keys
568
- // TODO: v2.0 Fix type for above
569
- };
570
- for (const path in pathValueSet) {
571
- if (Object.prototype.hasOwnProperty.call(pathValueSet, path)) {
572
- if (!isValidPath(path)) {
573
- continue; // Skip invalid paths
574
- }
575
- const value = pathValueSet[path];
576
- const keys = path.split("."); // Split path into keys
577
- setObjectValue(object, keys, value); // Use helper to populate the object
578
- }
579
- }
580
- return object;
581
- }
582
- /**
583
- * Creates a new object by omitting specified properties and undefined values from the original object.
584
- *
585
- * This utility function performs a selective copy of an object, excluding both explicitly
586
- * specified properties and any properties with undefined values. The function modifies
587
- * the original object in-place by deleting the unwanted properties, making it useful for
588
- * cleaning up objects before serialization, API calls, or data processing.
589
- *
590
- * This is particularly useful for preparing data for API requests where you need to remove
591
- * sensitive fields, clean up form data, or exclude certain properties from being sent to
592
- * external services. It's also helpful for removing undefined values that might cause
593
- * issues in JSON serialization or database operations.
594
- *
595
- * @template T - The type of the object being processed
596
- * @param {Partial<T>} obj - The object to process and remove properties from.
597
- * This object will be modified in-place.
598
- * @param {(keyof T)[]} [keys] - Optional array of property keys to omit from the object.
599
- * If not provided, only undefined properties will be removed.
600
- * @returns {Partial<T>} The same object reference with specified properties and undefined values removed
601
- *
602
- * @example
603
- * ```typescript
604
- * // Remove specific properties and undefined values
605
- * interface User {
606
- * id: number;
607
- * name: string;
608
- * email: string;
609
- * role: string;
610
- * address?: string;
611
- * phone?: string;
612
- * }
613
- *
614
- * const user: Partial<User> = {
615
- * id: 123,
616
- * name: "John Doe",
617
- * email: "john@example.com",
618
- * role: "admin",
619
- * address: undefined,
620
- * phone: "555-0123"
621
- * };
622
- *
623
- * const cleanUser = omit(user, ["role"]);
624
- * // Returns: { id: 123, name: "John Doe", email: "john@example.com", phone: "555-0123" }
625
- * // Note: 'role' and 'address' (undefined) are removed
626
- * ```
627
- *
628
- * @example
629
- * ```typescript
630
- * // Remove only undefined values (no specific keys)
631
- * const formData = {
632
- * firstName: "John",
633
- * lastName: "Doe",
634
- * middleName: undefined,
635
- * email: "john@example.com",
636
- * phone: undefined
637
- * };
638
- *
639
- * const cleanFormData = omit(formData);
640
- * // Returns: { firstName: "John", lastName: "Doe", email: "john@example.com" }
641
- * ```
642
- *
643
- * @example
644
- * ```typescript
645
- * // Prepare data for API request by removing sensitive fields
646
- * interface UserProfile {
647
- * id: number;
648
- * username: string;
649
- * email: string;
650
- * password: string;
651
- * internalNotes: string;
652
- * createdAt: Date;
653
- * }
654
- *
655
- * const userProfile: Partial<UserProfile> = {
656
- * id: 1,
657
- * username: "johndoe",
658
- * email: "john@example.com",
659
- * password: "secret123",
660
- * internalNotes: "VIP customer",
661
- * createdAt: new Date()
662
- * };
663
- *
664
- * const publicProfile = omit(userProfile, ["password", "internalNotes"]);
665
- * // Safe to send to client: { id: 1, username: "johndoe", email: "john@example.com", createdAt: Date }
666
- * ```
667
- *
668
- * @remarks
669
- * - This function modifies the original object in-place rather than creating a copy
670
- * - Both explicitly specified keys and undefined properties are removed
671
- * - If the input object is falsy (null, undefined), it's returned as-is
672
- * - The function uses delete operator, which may affect object performance in some JavaScript engines
673
- * - Properties with null values are preserved (only undefined values are automatically removed)
674
- *
675
- * @see {@link prune} Function for more comprehensive object cleaning including null and empty values
676
- */
677
- const omit = (obj, keys) => {
678
- if (obj) {
679
- Object.keys(obj).forEach(key => {
680
- return (obj[key] === undefined || keys?.includes(key)) && delete obj[key];
681
- });
682
- }
683
- return obj;
684
- };
685
- exports.omit = omit;
686
- /**
687
- * Creates a deep copy of an object with all empty, null, undefined, and optionally prototype properties removed.
688
- *
689
- * This utility function recursively traverses an object and creates a clean copy by excluding
690
- * properties that are null, undefined, or empty strings. It performs a deep cleaning operation,
691
- * processing nested objects recursively to ensure that the entire object hierarchy is pruned
692
- * of unwanted values.
693
- *
694
- * This is particularly useful for preparing data for serialization, cleaning up API responses,
695
- * removing empty form fields, or ensuring that only meaningful data is processed or stored.
696
- * The function is especially valuable when working with deeply nested objects that may contain
697
- * sparse data or when you need to eliminate all traces of empty values from complex data structures.
698
- *
699
- * @template T - The type of the object being pruned
700
- * @param {PartialWithNull<T>} obj - The object to prune. Can contain null, undefined, or empty values.
701
- * @param {boolean} [omitPrototype=false] - Whether to exclude inherited properties from the prototype chain.
702
- * If true, only own properties will be included in the result.
703
- * If false (default), inherited enumerable properties will be processed.
704
- * @returns {T} A new object with all empty, null, and undefined properties recursively removed
705
- *
706
- * @example
707
- * ```typescript
708
- * // Clean up a user profile with nested empty values
709
- * interface UserProfile {
710
- * id: number;
711
- * name: string;
712
- * contact: {
713
- * email: string;
714
- * phone?: string;
715
- * address?: {
716
- * street: string;
717
- * city: string;
718
- * zip?: string;
719
- * };
720
- * };
721
- * preferences: {
722
- * theme: string;
723
- * notifications?: boolean;
724
- * };
725
- * }
726
- *
727
- * const userProfile = {
728
- * id: 123,
729
- * name: "John Doe",
730
- * contact: {
731
- * email: "john@example.com",
732
- * phone: "",
733
- * address: {
734
- * street: "123 Main St",
735
- * city: "New York",
736
- * zip: null
737
- * }
738
- * },
739
- * preferences: {
740
- * theme: "dark",
741
- * notifications: undefined
742
- * }
743
- * };
744
- *
745
- * const cleanProfile = prune(userProfile);
746
- * // Returns:
747
- * // {
748
- * // id: 123,
749
- * // name: "John Doe",
750
- * // contact: {
751
- * // email: "john@example.com",
752
- * // address: {
753
- * // street: "123 Main St",
754
- * // city: "New York"
755
- * // }
756
- * // },
757
- * // preferences: {
758
- * // theme: "dark"
759
- * // }
760
- * // }
761
- * ```
762
- *
763
- * @example
764
- * ```typescript
765
- * // Clean up form data before submission
766
- * const formData = {
767
- * firstName: "John",
768
- * lastName: "Doe",
769
- * middleName: "",
770
- * email: "john@example.com",
771
- * phone: null,
772
- * address: {
773
- * street: "123 Main St",
774
- * apartment: "",
775
- * city: "New York",
776
- * state: undefined,
777
- * zip: "10001"
778
- * },
779
- * emergencyContact: {
780
- * name: "",
781
- * phone: null
782
- * }
783
- * };
784
- *
785
- * const cleanFormData = prune(formData);
786
- * // Returns only fields with meaningful values:
787
- * // {
788
- * // firstName: "John",
789
- * // lastName: "Doe",
790
- * // email: "john@example.com",
791
- * // address: {
792
- * // street: "123 Main St",
793
- * // city: "New York",
794
- * // zip: "10001"
795
- * // }
796
- * // }
797
- * // Note: emergencyContact object is completely removed as all its properties were empty
798
- * ```
799
- *
800
- * @example
801
- * ```typescript
802
- * // Control prototype property inclusion
803
- * class BaseUser {
804
- * role = "user";
805
- * }
806
- *
807
- * class ExtendedUser extends BaseUser {
808
- * name = "John";
809
- * email = "";
810
- * }
811
- *
812
- * const user = new ExtendedUser();
813
- *
814
- * const prunedWithPrototype = prune(user, false);
815
- * // Includes inherited 'role' property: { role: "user", name: "John" }
816
- *
817
- * const prunedOwnOnly = prune(user, true);
818
- * // Only own properties: { name: "John" }
819
- * ```
820
- *
821
- * @remarks
822
- * - Creates a new object rather than modifying the original (unlike the omit function)
823
- * - Recursively processes nested objects to ensure deep cleaning
824
- * - Removes properties with values: null, undefined, or empty string ("")
825
- * - Preserves properties with falsy but meaningful values like 0, false
826
- * - If a nested object becomes empty after pruning, it will be excluded from the result
827
- * - Non-object types are returned as an empty object of the target type
828
- * - The function preserves the type structure while removing empty values
829
- *
830
- * @see {@link omit} Function for selective property removal without deep cleaning
831
- */
832
- const prune = (obj, omitPrototype) => {
833
- const objClone = {};
834
- if (typeof obj !== "object") {
835
- return objClone;
836
- }
837
- for (const key in obj) {
838
- if (!omitPrototype || Object.prototype.hasOwnProperty.call(obj, key)) {
839
- if (obj[key] !== null && typeof obj[key] === "object") {
840
- objClone[key] = (0, exports.prune)(obj[key], omitPrototype);
841
- }
842
- else if (obj[key] !== null && obj[key] !== undefined && obj[key] !== "") {
843
- objClone[key] = obj[key];
844
- }
845
- }
846
- }
847
- return objClone;
848
- };
849
- exports.prune = prune;
850
- /**
851
- * Checks if an object has all specified properties as its own properties (not inherited).
852
- *
853
- * This utility function verifies that an object contains all the properties listed in the
854
- * provided array as direct (own) properties, not inherited from the prototype chain.
855
- * It uses the modern Object.hasOwn() method for reliable property existence checking,
856
- * making it safer than using hasOwnProperty() directly.
857
- *
858
- * This is particularly useful for validating object structures, ensuring required properties
859
- * exist before processing, implementing type guards, or verifying that objects conform to
860
- * expected interfaces. It's commonly used in data validation, API request validation,
861
- * and ensuring objects have all necessary properties before performing operations.
862
- *
863
- * @param {Record<PropertyKey, unknown>} obj - The object to check for property existence
864
- * @param {PropertyKey[]} props - Array of property keys to verify exist on the object.
865
- * PropertyKey includes string, number, and symbol types.
866
- * @returns {boolean} True if the object has all specified properties as own properties, false otherwise
867
- *
868
- * @example
869
- * ```typescript
870
- * // Validate that a user object has required properties
871
- * interface User {
872
- * id: number;
873
- * name: string;
874
- * email: string;
875
- * }
876
- *
877
- * const user = {
878
- * id: 123,
879
- * name: "John Doe",
880
- * email: "john@example.com",
881
- * role: "admin"
882
- * };
883
- *
884
- * const hasRequiredProps = hasOwnAll(user, ["id", "name", "email"]);
885
- * // Returns: true
886
- *
887
- * const hasAllProps = hasOwnAll(user, ["id", "name", "email", "phone"]);
888
- * // Returns: false (missing 'phone' property)
889
- * ```
890
- *
891
- * @example
892
- * ```typescript
893
- * // Validate API request payload
894
- * function processUserUpdate(payload: unknown): User | null {
895
- * if (typeof payload === 'object' && payload !== null) {
896
- * const requiredFields = ['id', 'name', 'email'];
897
- *
898
- * if (hasOwnAll(payload as Record<string, unknown>, requiredFields)) {
899
- * // Safe to process as we know all required fields exist
900
- * return updateUser(payload as User);
901
- * }
902
- * }
903
- *
904
- * throw new Error('Invalid payload: missing required fields');
905
- * }
906
- * ```
907
- *
908
- * @example
909
- * ```typescript
910
- * // Check configuration object completeness
911
- * interface DatabaseConfig {
912
- * host: string;
913
- * port: number;
914
- * database: string;
915
- * username: string;
916
- * password: string;
917
- * }
918
- *
919
- * const config = {
920
- * host: "localhost",
921
- * port: 5432,
922
- * database: "myapp",
923
- * username: "admin"
924
- * // password is missing
925
- * };
926
- *
927
- * const requiredConfigKeys = ['host', 'port', 'database', 'username', 'password'];
928
- * const isConfigComplete = hasOwnAll(config, requiredConfigKeys);
929
- * // Returns: false
930
- *
931
- * if (!isConfigComplete) {
932
- * throw new Error('Database configuration is incomplete');
933
- * }
934
- * ```
935
- *
936
- * @example
937
- * ```typescript
938
- * // Work with symbol properties
939
- * const symbolKey = Symbol('secret');
940
- * const obj = {
941
- * name: "test",
942
- * [symbolKey]: "hidden value"
943
- * };
944
- *
945
- * const hasSymbolProp = hasOwnAll(obj, ['name', symbolKey]);
946
- * // Returns: true
947
- * ```
948
- *
949
- * @remarks
950
- * - Uses Object.hasOwn() which is more reliable than hasOwnProperty()
951
- * - Only checks for own properties, not inherited properties from the prototype chain
952
- * - Returns false immediately if any property is missing (short-circuit evaluation)
953
- * - Works with string, number, and symbol property keys
954
- * - Returns true for an empty properties array (vacuous truth)
955
- * - Does not check property values, only existence
956
- *
957
- * @see {@link Object.hasOwn} The underlying method used for property checking
958
- */
959
- function hasOwnAll(obj, props) {
960
- return props.every((prop) => Object.hasOwn(obj, prop));
961
- }
962
- /**
963
- * Extracts all values from a TypeScript enum, handling both string and numeric enum types correctly.
964
- *
965
- * This utility function safely retrieves the actual values from TypeScript enums, accounting for
966
- * the different internal representations of string and numeric enums. For numeric enums, TypeScript
967
- * creates reverse mappings (value → key), so this function filters out the reverse mapping entries
968
- * to return only the actual enum values. For string enums, it returns all values directly.
969
- *
970
- * This is particularly useful when you need to iterate over enum values, validate input against
971
- * enum values, create dropdown options from enums, or perform any operation that requires access
972
- * to the actual enum values rather than the keys.
973
- *
974
- * @template T - The enum type extending object
975
- * @param {T} e - The enum object to extract values from
976
- * @returns {T[keyof T][]} An array containing all the actual values of the enum
977
- *
978
- * @example
979
- * ```typescript
980
- * // String enum example
981
- * enum Color {
982
- * RED = "red",
983
- * GREEN = "green",
984
- * BLUE = "blue"
985
- * }
986
- *
987
- * const colorValues = getEnumValues(Color);
988
- * // Returns: ["red", "green", "blue"]
989
- *
990
- * // Use for validation
991
- * function isValidColor(value: string): value is Color {
992
- * return getEnumValues(Color).includes(value as Color);
993
- * }
994
- * ```
995
- *
996
- * @example
997
- * ```typescript
998
- * // Numeric enum example
999
- * enum Status {
1000
- * PENDING, // 0
1001
- * APPROVED, // 1
1002
- * REJECTED // 2
1003
- * }
1004
- *
1005
- * const statusValues = getEnumValues(Status);
1006
- * // Returns: [0, 1, 2] (not ["PENDING", "APPROVED", "REJECTED", 0, 1, 2])
1007
- *
1008
- * // Use for creating select options
1009
- * const statusOptions = getEnumValues(Status).map(value => ({
1010
- * value,
1011
- * label: Status[value] // Get the key name for display
1012
- * }));
1013
- * ```
1014
- *
1015
- * @example
1016
- * ```typescript
1017
- * // Mixed numeric enum example
1018
- * enum HttpStatus {
1019
- * OK = 200,
1020
- * NOT_FOUND = 404,
1021
- * SERVER_ERROR = 500
1022
- * }
1023
- *
1024
- * const httpStatusValues = getEnumValues(HttpStatus);
1025
- * // Returns: [200, 404, 500]
1026
- *
1027
- * // Use for status code validation
1028
- * function isValidHttpStatus(code: number): code is HttpStatus {
1029
- * return getEnumValues(HttpStatus).includes(code as HttpStatus);
1030
- * }
1031
- * ```
1032
- *
1033
- * @example
1034
- * ```typescript
1035
- * // Creating dropdown options from enum
1036
- * enum UserRole {
1037
- * ADMIN = "admin",
1038
- * USER = "user",
1039
- * MODERATOR = "moderator"
1040
- * }
1041
- *
1042
- * const roleOptions = getEnumValues(UserRole).map(role => ({
1043
- * value: role,
1044
- * label: role.charAt(0).toUpperCase() + role.slice(1)
1045
- * }));
1046
- * // Returns: [
1047
- * // { value: "admin", label: "Admin" },
1048
- * // { value: "user", label: "User" },
1049
- * // { value: "moderator", label: "Moderator" }
1050
- * // ]
1051
- * ```
1052
- *
1053
- * @remarks
1054
- * - Automatically detects numeric vs string enums and handles them appropriately
1055
- * - For numeric enums, filters out reverse mapping entries that TypeScript automatically creates
1056
- * - For string enums, returns all values as-is since there are no reverse mappings
1057
- * - Works with mixed numeric enums (enums with explicit numeric values)
1058
- * - The returned array maintains the order of enum declaration
1059
- * - Type-safe: the return type is correctly inferred as an array of the enum's value types
1060
- *
1061
- * @see {@link Object.values} The underlying method used to extract enum entries
1062
- */
1063
- function getEnumValues(e) {
1064
- const values = Object.values(e);
1065
- // In numeric enums, values can appear as keys (reverse mapping)
1066
- const isNumericEnum = values.some(v => typeof v === "number");
1067
- return isNumericEnum ? values.filter(v => typeof v !== "string") : values;
1068
- }
1069
- //# sourceMappingURL=object.utils.js.map