@inglorious/utils 5.0.0 → 5.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/utils",
3
- "version": "5.0.0",
3
+ "version": "5.1.0",
4
4
  "description": "A set of general-purpose utility functions designed with functional programming principles in mind.",
5
5
  "author": "Matteo Antony Mistretta <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -137,7 +137,7 @@
137
137
  },
138
138
  "sideEffects": false,
139
139
  "devDependencies": {
140
- "@inglorious/eslint-config": "1.1.2",
140
+ "@inglorious/eslint-config": "1.1.4",
141
141
  "prettier": "^3.9.6",
142
142
  "vite": "^8.2.2",
143
143
  "vitest": "^5.0.0"
@@ -36,24 +36,6 @@ export const eucledianDistance = (a, b) =>
36
36
  */
37
37
  export const manhattanDistance = (a, b) => abs(subtract(a.position, b.position))
38
38
 
39
- /**
40
- * Compares the cost of two nodes.
41
- *
42
- * @param {Node} a - The first node.
43
- * @param {Node} b - The second node.
44
- * @returns {number} - The difference in cost between the two nodes.
45
- */
46
- const compareCost = (a, b) => b.cost - a.cost
47
-
48
- /**
49
- * Compares the total cost of two nodes.
50
- *
51
- * @param {Node} a - The first node.
52
- * @param {Node} b - The second node.
53
- * @returns {number} - The difference in total cost between the two nodes.
54
- */
55
- const compareTotalCost = (a, b) => b.totalCost - a.totalCost
56
-
57
39
  /**
58
40
  * Finds the shortest path in a graph from a start node to an end node using a heuristic.
59
41
  *
@@ -148,3 +130,25 @@ function adaptGraph(graph) {
148
130
  function createFindNode(nodes) {
149
131
  return (id) => nodes.find((node) => node.id === id)
150
132
  }
133
+
134
+ /**
135
+ * Compares the cost of two nodes.
136
+ *
137
+ * @param {Node} a - The first node.
138
+ * @param {Node} b - The second node.
139
+ * @returns {number} - The difference in cost between the two nodes.
140
+ */
141
+ function compareCost(a, b) {
142
+ return b.cost - a.cost
143
+ }
144
+
145
+ /**
146
+ * Compares the total cost of two nodes.
147
+ *
148
+ * @param {Node} a - The first node.
149
+ * @param {Node} b - The second node.
150
+ * @returns {number} - The difference in total cost between the two nodes.
151
+ */
152
+ function compareTotalCost(a, b) {
153
+ return b.totalCost - a.totalCost
154
+ }
@@ -8,57 +8,59 @@ const NEXT_KEY_OFFSET = 1
8
8
  const NEXT_LEVEL = 2
9
9
 
10
10
  /**
11
- * Creates a deep clone of a given value using the structured clone algorithm.
12
- * This is a modern and efficient way to deep-clone objects.
11
+ * Creates a deep clone of a given value.
13
12
  *
14
- * Note: `structuredClone` has some limitations. It cannot clone functions,
15
- * DOM nodes, property descriptors, getters, setters, or certain other
16
- * non-serializable objects. It will throw a `DataCloneError` in such cases.
13
+ * Plain objects, arrays and primitives -- what state is nearly always made of -- are
14
+ * copied by hand, because a general algorithm pays for being one: across thousands of
15
+ * small objects, `structuredClone` takes about twice as long and a JSON round trip
16
+ * longer still. Vectors stay vectors, which neither of those keeps: the tag that marks
17
+ * one is not enumerable, so anything that copies only what it can see drops it.
17
18
  *
18
- * For more details, see:
19
- * https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
19
+ * Anything else is handed to `structuredClone`, so a `Date` or a `Map` is still copied
20
+ * properly, and DOM nodes or class instances with non-cloneable internals still throw a
21
+ * `DataCloneError`. Functions are kept as they are rather than copied, since there is
22
+ * nothing in one to copy.
20
23
  *
21
24
  * This is similar to Lodash's `_.cloneDeep`.
22
25
  *
23
26
  * @template T
24
- * @param {T} obj - The value to clone.
27
+ * @param {T} value - The value to clone.
25
28
  * @returns {T} A deep clone of the input value.
26
29
  */
27
- export function clone(obj) {
28
- return deserialize(serialize(obj))
29
- }
30
+ export function clone(value) {
31
+ // A vector holds numbers and nothing else, so it needs no copying below the top -- and
32
+ // building it in one go is noticeably cheaper than copying it and tagging the copy.
33
+ if (value?.__isVector__) {
34
+ return v(...value)
35
+ }
30
36
 
31
- /**
32
- * Recursively merges properties from a source object into a target object.
33
- * This is a helper function for `merge` and `extend`.
34
- *
35
- * @param {Object} target - The target object to merge into.
36
- * @param {Object} source - The source object to merge from.
37
- * @param {Function} [merger] - An optional function to customize merging behavior for specific keys.
38
- * @returns {Object} - The modified target object.
39
- */
40
- function deepMerge(target, source, merger) {
41
- for (const [key, value] of Object.entries(source)) {
42
- if (isFunction(merger)) {
43
- const mergedValue = merger(target[key], value)
44
- if (mergedValue !== undefined) {
45
- target[key] = mergedValue
46
- continue
47
- }
48
- }
37
+ if (isArray(value)) {
38
+ const copy = new Array(value.length)
49
39
 
50
- if (isArray(value)) {
51
- target[key] = value
52
- } else if (isObject(value)) {
53
- if (!isObject(target[key])) {
54
- target[key] = {}
55
- }
56
- target[key] = deepMerge(target[key], value, merger)
57
- } else {
58
- target[key] = value
40
+ for (let index = 0; index < value.length; index++) {
41
+ copy[index] = clone(value[index])
59
42
  }
43
+
44
+ return copy
60
45
  }
61
- return target
46
+
47
+ if (value === null || typeof value !== "object") {
48
+ return value
49
+ }
50
+
51
+ const prototype = Object.getPrototypeOf(value)
52
+
53
+ if (prototype !== Object.prototype && prototype !== null) {
54
+ return structuredClone(value)
55
+ }
56
+
57
+ const copy = {}
58
+
59
+ for (const key in value) {
60
+ copy[key] = clone(value[key])
61
+ }
62
+
63
+ return copy
62
64
  }
63
65
 
64
66
  /**
@@ -326,7 +328,7 @@ export function mergeWith(merger, target, ...sources) {
326
328
  *
327
329
  * **Important:** Unlike Immer, which uses structural sharing via proxies for
328
330
  * high performance, this implementation performs a full deep clone of the base
329
- * state on every call using `deserialize(serialize())`. This can be
331
+ * state on every call using `clone`. This can be
330
332
  * inefficient for large or complex states. It is intended for simple use cases
331
333
  * where the convenience of the API outweighs the performance cost.
332
334
  *
@@ -480,3 +482,36 @@ ${" ".repeat(indentationLevel)}}`
480
482
 
481
483
  return obj
482
484
  }
485
+
486
+ /**
487
+ * Recursively merges properties from a source object into a target object.
488
+ * This is a helper function for `merge` and `extend`.
489
+ *
490
+ * @param {Object} target - The target object to merge into.
491
+ * @param {Object} source - The source object to merge from.
492
+ * @param {Function} [merger] - An optional function to customize merging behavior for specific keys.
493
+ * @returns {Object} - The modified target object.
494
+ */
495
+ function deepMerge(target, source, merger) {
496
+ for (const [key, value] of Object.entries(source)) {
497
+ if (isFunction(merger)) {
498
+ const mergedValue = merger(target[key], value)
499
+ if (mergedValue !== undefined) {
500
+ target[key] = mergedValue
501
+ continue
502
+ }
503
+ }
504
+
505
+ if (isArray(value)) {
506
+ target[key] = value
507
+ } else if (isObject(value)) {
508
+ if (!isObject(target[key])) {
509
+ target[key] = {}
510
+ }
511
+ target[key] = deepMerge(target[key], value, merger)
512
+ } else {
513
+ target[key] = value
514
+ }
515
+ }
516
+ return target
517
+ }
@@ -71,9 +71,10 @@ export type DefaultsResult<T extends object, D extends object> = Omit<
71
71
  }
72
72
 
73
73
  /**
74
- * Creates a deep clone of a value.
74
+ * Creates a deep clone of a value. Plain data is copied by hand and vectors stay
75
+ * vectors; anything else goes through `structuredClone`.
75
76
  */
76
- export function clone<T>(obj: T): T
77
+ export function clone<T>(value: T): T
77
78
 
78
79
  /**
79
80
  * Assigns default properties to a target in place.