@nejs/basic-extensions 2.7.0 → 2.9.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.
Files changed (271) hide show
  1. package/.vscode/settings.json +5 -0
  2. package/README.md +6250 -1574
  3. package/bin/version +100 -0
  4. package/dist/@nejs/basic-extensions.bundle.2.8.0.js +19 -0
  5. package/dist/@nejs/basic-extensions.bundle.2.8.0.js.map +7 -0
  6. package/dist/cjs/array.extensions.d.ts +39 -0
  7. package/dist/cjs/array.extensions.js +303 -0
  8. package/dist/cjs/array.extensions.js.map +1 -0
  9. package/dist/cjs/big.int.extension.d.ts +31 -0
  10. package/dist/cjs/big.int.extension.js +164 -0
  11. package/dist/cjs/big.int.extension.js.map +1 -0
  12. package/dist/cjs/{newClasses → classes}/asyncIterable.js +32 -44
  13. package/dist/cjs/classes/asyncIterable.js.map +1 -0
  14. package/dist/cjs/{newClasses → classes}/deferred.js +66 -138
  15. package/dist/cjs/classes/deferred.js.map +1 -0
  16. package/dist/cjs/{newClasses → classes}/descriptor.js +56 -90
  17. package/dist/cjs/classes/descriptor.js.map +1 -0
  18. package/dist/cjs/classes/index.d.ts +13 -0
  19. package/dist/cjs/classes/index.js +57 -0
  20. package/dist/cjs/classes/index.js.map +1 -0
  21. package/dist/cjs/classes/introspector.d.ts +20 -0
  22. package/dist/cjs/classes/introspector.js +130 -0
  23. package/dist/cjs/classes/introspector.js.map +1 -0
  24. package/dist/cjs/{newClasses → classes}/iterable.js +42 -63
  25. package/dist/cjs/classes/iterable.js.map +1 -0
  26. package/dist/cjs/classes/param.parser.d.ts +227 -0
  27. package/dist/cjs/classes/param.parser.js +242 -0
  28. package/dist/cjs/classes/param.parser.js.map +1 -0
  29. package/dist/cjs/classes/pluggable.proxy.d.ts +152 -0
  30. package/dist/cjs/classes/pluggable.proxy.js +444 -0
  31. package/dist/cjs/classes/pluggable.proxy.js.map +1 -0
  32. package/dist/cjs/{newClasses → classes}/refmap.js +18 -30
  33. package/dist/cjs/classes/refmap.js.map +1 -0
  34. package/dist/cjs/{newClasses → classes}/refset.js +28 -47
  35. package/dist/cjs/classes/refset.js.map +1 -0
  36. package/dist/cjs/classes/symkeys.d.ts +292 -0
  37. package/dist/cjs/classes/symkeys.js +424 -0
  38. package/dist/cjs/classes/symkeys.js.map +1 -0
  39. package/dist/cjs/classes/type.d.ts +56 -0
  40. package/dist/cjs/classes/type.js +405 -0
  41. package/dist/cjs/classes/type.js.map +1 -0
  42. package/dist/cjs/function.extensions.js +757 -0
  43. package/dist/cjs/function.extensions.js.map +1 -0
  44. package/dist/cjs/global.this.js +261 -0
  45. package/dist/cjs/global.this.js.map +1 -0
  46. package/dist/cjs/index.d.ts +4 -3
  47. package/dist/cjs/index.js +62 -32
  48. package/dist/cjs/index.js.map +1 -1
  49. package/dist/cjs/json.extensions.d.ts +2 -0
  50. package/dist/cjs/json.extensions.js +108 -0
  51. package/dist/cjs/json.extensions.js.map +1 -0
  52. package/dist/{mjs/mapextensions.d.ts → cjs/map.extensions.d.ts} +1 -0
  53. package/dist/cjs/map.extensions.js +142 -0
  54. package/dist/cjs/map.extensions.js.map +1 -0
  55. package/dist/cjs/number.extension.d.ts +44 -0
  56. package/dist/cjs/number.extension.js +260 -0
  57. package/dist/cjs/number.extension.js.map +1 -0
  58. package/dist/cjs/object.extensions.d.ts +62 -0
  59. package/dist/cjs/object.extensions.js +1128 -0
  60. package/dist/cjs/object.extensions.js.map +1 -0
  61. package/dist/cjs/proxy.extensions.d.ts +2 -0
  62. package/dist/cjs/proxy.extensions.js +207 -0
  63. package/dist/cjs/proxy.extensions.js.map +1 -0
  64. package/dist/cjs/reflect.extensions.js +316 -0
  65. package/dist/cjs/reflect.extensions.js.map +1 -0
  66. package/dist/cjs/regular.expression.extensions.d.ts +2 -0
  67. package/dist/cjs/regular.expression.extensions.js +423 -0
  68. package/dist/cjs/regular.expression.extensions.js.map +1 -0
  69. package/dist/cjs/set.extensions.d.ts +40 -0
  70. package/dist/cjs/{setextensions.js → set.extensions.js} +150 -2
  71. package/dist/cjs/set.extensions.js.map +1 -0
  72. package/dist/cjs/string.extensions.js +661 -0
  73. package/dist/cjs/string.extensions.js.map +1 -0
  74. package/dist/{mjs/symbolextensions.d.ts → cjs/symbol.extensions.d.ts} +1 -0
  75. package/dist/cjs/symbol.extensions.js +380 -0
  76. package/dist/cjs/symbol.extensions.js.map +1 -0
  77. package/dist/cjs/{weakrefextensions.js → weakref.extensions.js} +1 -1
  78. package/dist/cjs/weakref.extensions.js.map +1 -0
  79. package/dist/mjs/array.extensions.d.ts +39 -0
  80. package/dist/mjs/array.extensions.js +300 -0
  81. package/dist/mjs/array.extensions.js.map +1 -0
  82. package/dist/mjs/big.int.extension.d.ts +31 -0
  83. package/dist/mjs/big.int.extension.js +161 -0
  84. package/dist/mjs/big.int.extension.js.map +1 -0
  85. package/dist/mjs/classes/asyncIterable.js.map +1 -0
  86. package/dist/mjs/classes/deferred.js.map +1 -0
  87. package/dist/mjs/{newClasses → classes}/descriptor.js +7 -4
  88. package/dist/mjs/classes/descriptor.js.map +1 -0
  89. package/dist/mjs/classes/index.d.ts +13 -0
  90. package/dist/mjs/classes/index.js +40 -0
  91. package/dist/mjs/classes/index.js.map +1 -0
  92. package/dist/mjs/classes/introspector.d.ts +20 -0
  93. package/dist/mjs/classes/introspector.js +126 -0
  94. package/dist/mjs/classes/introspector.js.map +1 -0
  95. package/dist/mjs/classes/iterable.js.map +1 -0
  96. package/dist/mjs/classes/param.parser.d.ts +227 -0
  97. package/dist/mjs/classes/param.parser.js +238 -0
  98. package/dist/mjs/classes/param.parser.js.map +1 -0
  99. package/dist/mjs/classes/pluggable.proxy.d.ts +152 -0
  100. package/dist/mjs/classes/pluggable.proxy.js +438 -0
  101. package/dist/mjs/classes/pluggable.proxy.js.map +1 -0
  102. package/dist/mjs/{newClasses → classes}/refmap.js +3 -3
  103. package/dist/mjs/classes/refmap.js.map +1 -0
  104. package/dist/mjs/classes/refset.js.map +1 -0
  105. package/dist/mjs/classes/symkeys.d.ts +292 -0
  106. package/dist/mjs/classes/symkeys.js +420 -0
  107. package/dist/mjs/classes/symkeys.js.map +1 -0
  108. package/dist/mjs/classes/type.d.ts +56 -0
  109. package/dist/mjs/classes/type.js +401 -0
  110. package/dist/mjs/classes/type.js.map +1 -0
  111. package/dist/mjs/function.extensions.js +754 -0
  112. package/dist/mjs/function.extensions.js.map +1 -0
  113. package/dist/mjs/global.this.js +258 -0
  114. package/dist/mjs/global.this.js.map +1 -0
  115. package/dist/mjs/index.d.ts +4 -3
  116. package/dist/mjs/index.js +49 -19
  117. package/dist/mjs/index.js.map +1 -1
  118. package/dist/mjs/json.extensions.d.ts +2 -0
  119. package/dist/mjs/json.extensions.js +105 -0
  120. package/dist/mjs/json.extensions.js.map +1 -0
  121. package/dist/{cjs/mapextensions.d.ts → mjs/map.extensions.d.ts} +1 -0
  122. package/dist/mjs/map.extensions.js +139 -0
  123. package/dist/mjs/map.extensions.js.map +1 -0
  124. package/dist/mjs/number.extension.d.ts +44 -0
  125. package/dist/mjs/number.extension.js +257 -0
  126. package/dist/mjs/number.extension.js.map +1 -0
  127. package/dist/mjs/object.extensions.d.ts +62 -0
  128. package/dist/mjs/object.extensions.js +1124 -0
  129. package/dist/mjs/object.extensions.js.map +1 -0
  130. package/dist/mjs/proxy.extensions.d.ts +2 -0
  131. package/dist/mjs/proxy.extensions.js +204 -0
  132. package/dist/mjs/proxy.extensions.js.map +1 -0
  133. package/dist/mjs/reflect.extensions.js +313 -0
  134. package/dist/mjs/reflect.extensions.js.map +1 -0
  135. package/dist/mjs/regular.expression.extensions.d.ts +2 -0
  136. package/dist/mjs/regular.expression.extensions.js +420 -0
  137. package/dist/mjs/regular.expression.extensions.js.map +1 -0
  138. package/dist/mjs/set.extensions.d.ts +40 -0
  139. package/dist/mjs/{setextensions.js → set.extensions.js} +149 -1
  140. package/dist/mjs/set.extensions.js.map +1 -0
  141. package/dist/mjs/string.extensions.js +658 -0
  142. package/dist/mjs/string.extensions.js.map +1 -0
  143. package/dist/{cjs/symbolextensions.d.ts → mjs/symbol.extensions.d.ts} +1 -0
  144. package/dist/mjs/symbol.extensions.js +377 -0
  145. package/dist/mjs/symbol.extensions.js.map +1 -0
  146. package/dist/mjs/{weakrefextensions.js → weakref.extensions.js} +1 -1
  147. package/dist/mjs/weakref.extensions.js.map +1 -0
  148. package/docs/index.html +24452 -5692
  149. package/package.json +7 -5
  150. package/src/array.extensions.js +322 -0
  151. package/src/big.int.extension.js +163 -0
  152. package/src/{newClasses → classes}/descriptor.js +16 -12
  153. package/src/classes/index.js +51 -0
  154. package/src/classes/introspector.js +167 -0
  155. package/src/classes/param.parser.js +253 -0
  156. package/src/classes/pluggable.proxy.js +485 -0
  157. package/src/{newClasses → classes}/refmap.js +5 -3
  158. package/src/classes/symkeys.js +464 -0
  159. package/src/classes/type.js +427 -0
  160. package/src/function.extensions.js +818 -0
  161. package/src/global.this.js +304 -0
  162. package/src/index.js +56 -23
  163. package/src/json.extensions.js +109 -0
  164. package/src/map.extensions.js +144 -0
  165. package/src/number.extension.js +273 -0
  166. package/src/object.extensions.js +1237 -0
  167. package/src/proxy.extensions.js +229 -0
  168. package/src/reflect.extensions.js +346 -0
  169. package/src/regular.expression.extensions.js +451 -0
  170. package/src/{setextensions.js → set.extensions.js} +151 -2
  171. package/src/string.extensions.js +734 -0
  172. package/src/symbol.extensions.js +389 -0
  173. package/tests/newClasses/refmap.test.js +3 -2
  174. package/tsconfig.base.json +5 -3
  175. package/tsconfig.cjs.json +2 -2
  176. package/tsconfig.esm.json +2 -2
  177. package/dist/@nejs/basic-extensions.bundle.2.6.0.js +0 -17
  178. package/dist/@nejs/basic-extensions.bundle.2.6.0.js.map +0 -7
  179. package/dist/cjs/arrayextensions.d.ts +0 -10
  180. package/dist/cjs/arrayextensions.js +0 -73
  181. package/dist/cjs/arrayextensions.js.map +0 -1
  182. package/dist/cjs/functionextensions.js +0 -202
  183. package/dist/cjs/functionextensions.js.map +0 -1
  184. package/dist/cjs/globals.js +0 -166
  185. package/dist/cjs/globals.js.map +0 -1
  186. package/dist/cjs/mapextensions.js +0 -32
  187. package/dist/cjs/mapextensions.js.map +0 -1
  188. package/dist/cjs/newClasses/asyncIterable.js.map +0 -1
  189. package/dist/cjs/newClasses/deferred.js.map +0 -1
  190. package/dist/cjs/newClasses/descriptor.js.map +0 -1
  191. package/dist/cjs/newClasses/iterable.js.map +0 -1
  192. package/dist/cjs/newClasses/refmap.js.map +0 -1
  193. package/dist/cjs/newClasses/refset.js.map +0 -1
  194. package/dist/cjs/objectextensions.d.ts +0 -11
  195. package/dist/cjs/objectextensions.js +0 -232
  196. package/dist/cjs/objectextensions.js.map +0 -1
  197. package/dist/cjs/reflectextensions.js +0 -111
  198. package/dist/cjs/reflectextensions.js.map +0 -1
  199. package/dist/cjs/setextensions.d.ts +0 -2
  200. package/dist/cjs/setextensions.js.map +0 -1
  201. package/dist/cjs/stringextensions.js +0 -158
  202. package/dist/cjs/stringextensions.js.map +0 -1
  203. package/dist/cjs/symbolextensions.js +0 -69
  204. package/dist/cjs/symbolextensions.js.map +0 -1
  205. package/dist/cjs/weakrefextensions.js.map +0 -1
  206. package/dist/mjs/arrayextensions.d.ts +0 -10
  207. package/dist/mjs/arrayextensions.js +0 -70
  208. package/dist/mjs/arrayextensions.js.map +0 -1
  209. package/dist/mjs/functionextensions.js +0 -199
  210. package/dist/mjs/functionextensions.js.map +0 -1
  211. package/dist/mjs/globals.js +0 -163
  212. package/dist/mjs/globals.js.map +0 -1
  213. package/dist/mjs/mapextensions.js +0 -29
  214. package/dist/mjs/mapextensions.js.map +0 -1
  215. package/dist/mjs/newClasses/asyncIterable.js.map +0 -1
  216. package/dist/mjs/newClasses/deferred.js.map +0 -1
  217. package/dist/mjs/newClasses/descriptor.js.map +0 -1
  218. package/dist/mjs/newClasses/iterable.js.map +0 -1
  219. package/dist/mjs/newClasses/refmap.js.map +0 -1
  220. package/dist/mjs/newClasses/refset.js.map +0 -1
  221. package/dist/mjs/objectextensions.d.ts +0 -11
  222. package/dist/mjs/objectextensions.js +0 -229
  223. package/dist/mjs/objectextensions.js.map +0 -1
  224. package/dist/mjs/reflectextensions.js +0 -108
  225. package/dist/mjs/reflectextensions.js.map +0 -1
  226. package/dist/mjs/setextensions.d.ts +0 -2
  227. package/dist/mjs/setextensions.js.map +0 -1
  228. package/dist/mjs/stringextensions.js +0 -155
  229. package/dist/mjs/stringextensions.js.map +0 -1
  230. package/dist/mjs/symbolextensions.js +0 -66
  231. package/dist/mjs/symbolextensions.js.map +0 -1
  232. package/dist/mjs/weakrefextensions.js.map +0 -1
  233. package/src/arrayextensions.js +0 -75
  234. package/src/functionextensions.js +0 -225
  235. package/src/globals.js +0 -196
  236. package/src/mapextensions.js +0 -32
  237. package/src/objectextensions.js +0 -256
  238. package/src/reflectextensions.js +0 -118
  239. package/src/stringextensions.js +0 -166
  240. package/src/symbolextensions.js +0 -69
  241. /package/dist/cjs/{newClasses → classes}/asyncIterable.d.ts +0 -0
  242. /package/dist/cjs/{newClasses → classes}/deferred.d.ts +0 -0
  243. /package/dist/cjs/{newClasses → classes}/descriptor.d.ts +0 -0
  244. /package/dist/cjs/{newClasses → classes}/iterable.d.ts +0 -0
  245. /package/dist/cjs/{newClasses → classes}/refmap.d.ts +0 -0
  246. /package/dist/cjs/{newClasses → classes}/refset.d.ts +0 -0
  247. /package/dist/cjs/{functionextensions.d.ts → function.extensions.d.ts} +0 -0
  248. /package/dist/cjs/{globals.d.ts → global.this.d.ts} +0 -0
  249. /package/dist/cjs/{reflectextensions.d.ts → reflect.extensions.d.ts} +0 -0
  250. /package/dist/cjs/{stringextensions.d.ts → string.extensions.d.ts} +0 -0
  251. /package/dist/cjs/{weakrefextensions.d.ts → weakref.extensions.d.ts} +0 -0
  252. /package/dist/mjs/{newClasses → classes}/asyncIterable.d.ts +0 -0
  253. /package/dist/mjs/{newClasses → classes}/asyncIterable.js +0 -0
  254. /package/dist/mjs/{newClasses → classes}/deferred.d.ts +0 -0
  255. /package/dist/mjs/{newClasses → classes}/deferred.js +0 -0
  256. /package/dist/mjs/{newClasses → classes}/descriptor.d.ts +0 -0
  257. /package/dist/mjs/{newClasses → classes}/iterable.d.ts +0 -0
  258. /package/dist/mjs/{newClasses → classes}/iterable.js +0 -0
  259. /package/dist/mjs/{newClasses → classes}/refmap.d.ts +0 -0
  260. /package/dist/mjs/{newClasses → classes}/refset.d.ts +0 -0
  261. /package/dist/mjs/{newClasses → classes}/refset.js +0 -0
  262. /package/dist/mjs/{functionextensions.d.ts → function.extensions.d.ts} +0 -0
  263. /package/dist/mjs/{globals.d.ts → global.this.d.ts} +0 -0
  264. /package/dist/mjs/{reflectextensions.d.ts → reflect.extensions.d.ts} +0 -0
  265. /package/dist/mjs/{stringextensions.d.ts → string.extensions.d.ts} +0 -0
  266. /package/dist/mjs/{weakrefextensions.d.ts → weakref.extensions.d.ts} +0 -0
  267. /package/src/{newClasses → classes}/asyncIterable.js +0 -0
  268. /package/src/{newClasses → classes}/deferred.js +0 -0
  269. /package/src/{newClasses → classes}/iterable.js +0 -0
  270. /package/src/{newClasses → classes}/refset.js +0 -0
  271. /package/src/{weakrefextensions.js → weakref.extensions.js} +0 -0
@@ -0,0 +1,1237 @@
1
+ import { Patch } from '@nejs/extension'
2
+
3
+ import { SymbolExtensions } from './symbol.extensions.js';
4
+ import { Descriptor } from './classes/descriptor.js';
5
+
6
+ const { keys: symkeys } = SymbolExtensions.patches
7
+
8
+ // Avoid circular dependencies; rewrite here for brevity
9
+ const isFn = o => typeof o === 'function' || o instanceof Function
10
+ const isStr = o => typeof o === 'string' || o instanceof String
11
+ const isBool = o => typeof o === 'boolean'
12
+ const isTrue = o => isBool(o) && o === true
13
+ const isTruthy = o => isTrue(!!o)
14
+ const isFalse = o => isBool(o) && o === false
15
+ const isFalsy = o => isFalse(!!o)
16
+
17
+ /**
18
+ * `ObjectExtensions` is a constant that applies a patch to the global
19
+ * `Object` constructor. This patch extends the `Object` with additional
20
+ * methods and properties, enhancing its functionality.
21
+ *
22
+ * The `Patch` function takes two arguments: the target object to be patched
23
+ * (in this case, `Object`), and an object containing the methods and
24
+ * properties to be added to the target object.
25
+ *
26
+ * @type {Patch}
27
+ * @memberof module:object.extensions
28
+ */
29
+ export const ObjectExtensions = new Patch(Object, {
30
+ [Patch.kMutablyHidden]: {
31
+ /**
32
+ * Creates a shallow copy of the provided object(s).
33
+ *
34
+ * This method uses the `copyObject` function with the `deep` parameter
35
+ * set to `false`, indicating a shallow copy. It takes a destination
36
+ * object and any number of source objects, and copies the properties
37
+ * from the source objects to the destination object. If a property
38
+ * already exists on the destination object, it will be overwritten.
39
+ *
40
+ * Note: This method does not copy nested objects or arrays. They are
41
+ * copied by reference, not by value. To create a deep copy, use the
42
+ * `deepCopy` method instead.
43
+ *
44
+ * @param {object} destination - The object to which properties will be
45
+ * copied.
46
+ * @param {...object} sources - The source object(s) from which
47
+ * properties will be copied.
48
+ * @returns {object} The destination object with the copied properties.
49
+ *
50
+ * @example
51
+ * const obj1 = { a: 1, b: 2 };
52
+ * const obj2 = { b: 3, c: 4 };
53
+ * const result = ObjectExtensions.copy(obj1, obj2);
54
+ * console.log(result); // Output: { a: 1, b: 3, c: 4 }
55
+ */
56
+ copy(destination, ...sources) {
57
+ return copyObject(false, destination, ...sources)
58
+ },
59
+
60
+ /**
61
+ * Creates a deep copy of the provided object(s).
62
+ *
63
+ * This method uses the `copyObject` function with the `deep` parameter
64
+ * set to `true`, indicating a deep copy. It takes a destination
65
+ * object and any number of source objects, and copies the properties
66
+ * from the source objects to the destination object. If a property
67
+ * already exists on the destination object, it will be overwritten.
68
+ *
69
+ * Note: This method copies nested objects or arrays by value, not by
70
+ * reference. To create a shallow copy, use the `copy` method instead.
71
+ *
72
+ * @param {object} destination - The object to which properties will be
73
+ * copied.
74
+ * @param {...object} sources - The source object(s) from which
75
+ * properties will be copied.
76
+ * @returns {object} The destination object with the copied properties.
77
+ *
78
+ * @example
79
+ * const obj1 = { a: 1, b: { c: 2 } };
80
+ * const obj2 = { b: { d: 3 }, e: 4 };
81
+ * const result = ObjectExtensions.deepCopy(obj1, obj2);
82
+ * console.log(result); // Output: { a: 1, b: { d: 3 }, e: 4 }
83
+ */
84
+ deepCopy(destination, ...sources) {
85
+ return copyObject(true, destination, ...sources)
86
+ },
87
+
88
+ /**
89
+ * A getter property that provides access to the definition types used
90
+ * for object property definitions. These types are used to control the
91
+ * visibility and mutability of object properties.
92
+ *
93
+ * @returns {Object} An object with getter properties for each definition
94
+ * type. The properties are:
95
+ * - `mutablyHidden`: A symbol representing a mutably hidden property,
96
+ * non-enumerable, but configurable.
97
+ * - `mutablyVisible`: A symbol representing a mutably visible property,
98
+ * enumerable, configurable
99
+ * - `immutablyHidden`: A symbol representing an immutably hidden property,
100
+ * non-enumerable, non-configurable.
101
+ * - `immutablyVisible`: A symbol representing an immutably visible
102
+ * property, enumerable, non-configurable.
103
+ *
104
+ * @example
105
+ * // Get the symbol for a mutably hidden property
106
+ * const hiddenSymbol = Object.definitionType.mutablyHidden;
107
+ *
108
+ * // Define a new mutably hidden property on an object
109
+ * Object.define(myObject, 'myProperty', myValue, hiddenSymbol);
110
+ */
111
+ get definitionType() {
112
+ return {
113
+ get mutablyHidden() { return Patch.kMutablyHidden },
114
+ get mutablyVisible() { return Patch.kMutablyVisible },
115
+ get immutablyHidden() { return Patch.kImmutablyHidden },
116
+ get immutablyVisible() { return Patch.kImmutablyVisible },
117
+ }
118
+ },
119
+
120
+ /**
121
+ * Defines a new property on an object with a specified value and
122
+ * visibility/mutability flag. The flag determines the visibility and
123
+ * mutability of the property. By default, the property is defined as
124
+ * mutably hidden.
125
+ *
126
+ * @param {object} object - The object on which to define the property.
127
+ * @param {string} key - The name of the property to be defined.
128
+ * @param {any} value - The value of the property to be defined.
129
+ * @param {symbol} [flag=Object.definitionType.mutablyHidden] - The
130
+ * visibility/mutability flag for the property. This should be one of the
131
+ * symbols available in `ObjectExtensions.definitionType`.
132
+ * @returns {object} The object with the newly defined property.
133
+ *
134
+ * @example
135
+ * // Define a new mutably hidden property on an object
136
+ * const myObject = {};
137
+ * const myValue = 'Hello, world!';
138
+ * const hiddenSymbol = Object.definitionType.mutablyHidden;
139
+ * Object.define(myObject, 'myProperty', myValue, hiddenSymbol);
140
+ * // myObject now has a mutably hidden property 'myProperty' with value
141
+ * // 'Hello, world!'
142
+ */
143
+ define(object, key, value, flag = Object.definitionType.mutablyHidden) {
144
+ const properties = Patch.getDescriptorOverridesFromSymbol(flag)
145
+ return Object.defineProperty(object, key, { ...properties, value })
146
+ },
147
+
148
+ /**
149
+ * Defines a new accessor property on an object with specified getter and
150
+ * setter functions and a visibility/mutability flag. The flag determines
151
+ * the visibility and mutability of the property. By default, the property
152
+ * is defined as mutably hidden.
153
+ *
154
+ * @param {object} object - The object on which to define the property.
155
+ * @param {string} key - The name of the property to be defined.
156
+ * @param {function} get - The getter function for the property.
157
+ * @param {function} set - The setter function for the property.
158
+ * @param {symbol} [flag=Object.definitionType.mutablyHidden] - The
159
+ * visibility/mutability flag for the property. This should be one of the
160
+ * symbols available in `ObjectExtensions.definitionType`.
161
+ * @returns {object} The object with the newly defined property.
162
+ *
163
+ * @example
164
+ * // Define a new mutably hidden accessor property on an object
165
+ * const myObject = {};
166
+ * const hiddenSymbol = ObjectExtensions.definitionType.mutablyHidden;
167
+ * ObjectExtensions.defineAccessor(
168
+ * myObject,
169
+ * 'myProperty',
170
+ * () => 'Hello, world!',
171
+ * (value) => console.log(`Setting value: ${value}`),
172
+ * hiddenSymbol
173
+ * );
174
+ * // myObject now has a mutably hidden property 'myProperty' with getter
175
+ * // and setter functions
176
+ */
177
+ defineAccessor(
178
+ object, key, get, set, flag = Object.definitionType.mutablyHidden
179
+ ) {
180
+ const properties = Patch.getDescriptorOverridesFromSymbol(flag)
181
+ return Object.defineProperty(object, key, { ...properties, get, set })
182
+ },
183
+
184
+ addAccessor(to, key, getter, setter, storage) {
185
+ const store = storage ?? (!getter && !setter) ? true : undefined;
186
+ return this.add({ to, key, get: getter, set: setter, storage: store })
187
+ },
188
+
189
+ addData(to, key, value) {
190
+ return this.add({ to, key, value })
191
+ },
192
+
193
+ add(...args) {
194
+ const { isDescriptor } = Descriptor
195
+ const { isObject: isObj } = this
196
+ const { kDescriptorStore } = this
197
+
198
+ let obj, key, value, _get, _set, storage, storageKey
199
+ let _type, _flag, _desc
200
+
201
+ // Check to see if we received multiple arguments or an object
202
+ if (args.length && isObj(args[0])) {
203
+ ({
204
+ to: obj,
205
+ key,
206
+ value,
207
+ get: _get,
208
+ set: _set,
209
+ storage,
210
+ storageKey,
211
+ type: _type = ['accessor', 'data'][1],
212
+ flag: _flag = undefined,
213
+ descriptorBase: _desc = undefined,
214
+ } = args[0])
215
+ }
216
+ else if (args.length > 1) {
217
+ ([
218
+ to,
219
+ _type,
220
+ key,
221
+ getOrValue,
222
+ _set,
223
+ storage,
224
+ storageKey,
225
+ _flag,
226
+ _desc,
227
+ ] = args)
228
+
229
+ obj = to
230
+ _type = (
231
+ ['accessor', 'data'].includes(_type.toLowerCase())
232
+ ? _type.toLowerCase() : 'data'
233
+ )
234
+ _get = _type === 'accessor' ? getOrValue : undefined
235
+ _value = _type === 'data' ? getOrValue : undefined
236
+ }
237
+
238
+ if (!this.isObject(obj)) {
239
+ console.warn('Object.add() must receive an object for `toObject`')
240
+ return obj;
241
+ }
242
+
243
+ const more = isDescriptor(_desc) ? _desc : {}
244
+ const flag = _flag || Object.definitionType.mutablyVisible
245
+ const props = { ...Patch.getDescriptorOverridesFromSymbol(flag), ...more }
246
+ const type = (['accessor', 'data'].includes(_type)
247
+ ? String(_type).toLowerCase() : 'data'
248
+ )
249
+
250
+ switch (type) {
251
+ case 'accessor':
252
+ let store = storage;
253
+ let storeKey = storageKey || key
254
+ let makeStore = false
255
+ let get = _get;
256
+ let set = _set;
257
+
258
+ if (!isTruthy(get) && !isFn(get)) { get = undefined }
259
+ if (!isTruthy(set) && !isFn(set)) { set = undefined }
260
+
261
+ if (isObj(store) || isTrue(store) || isFn(store)) {
262
+ makeStore = isTrue(store)
263
+ store = isFn(store) ? store() : store
264
+ store = isObj(store) ? store : (makeStore && {} || undefined)
265
+ }
266
+ // store should be defined by here: object or undefined
267
+
268
+ if ((!get && !set) && makeStore) {
269
+ // being lazy here, someone has defined we make an accessor but
270
+ // wants the default accessor behaviors with an associated store
271
+ // made by us.
272
+ Object.defineProperty(obj, kDescriptorStore, {
273
+ value: symkeys.add('descriptor.store', store),
274
+ configurable: true,
275
+ enumerable: false,
276
+ writable: true,
277
+ })
278
+
279
+ get = () => this[kDescriptorStore]?.data?.[storeKey]
280
+ set = (value) => { this[kDescriptorStore].data[storeKey] = value }
281
+ }
282
+
283
+ else if (get?.length && set?.length > 1 && store) {
284
+ // if we received a get or set that takes more arguments than
285
+ // expected, assume the last argument should be the store variable
286
+ // so we execute the supplied function with the storage and its
287
+ // results or byproducts are the result to the get/set we define
288
+ const innerGet = get
289
+ const innerSet = set
290
+ get = () => innerGet(store)
291
+ set = (value) => innerSet(value, store)
292
+ }
293
+ // get and set should be in their final state by here
294
+
295
+ Object.defineProperty(obj, key, { ...props, get, set })
296
+ break
297
+
298
+ case 'data':
299
+ Object.defineProperty(obj, key, { ...props, value })
300
+ break
301
+ }
302
+
303
+ return obj
304
+ },
305
+
306
+ /**
307
+ * Creates a new object from an array of key-value pairs (entries), with an
308
+ * optional prototype and reducer function. If no prototype is provided,
309
+ * the default Object.prototype is used. If no reducer is provided, a
310
+ * default reducer is used that assigns each value to its corresponding key.
311
+ *
312
+ * @param {Array} entries - An array of key-value pairs. Each entry should
313
+ * be an array where the first element is the key and the second element is
314
+ * the value. Non-conforming entries are ignored.
315
+ * @param {object} [prototype=Object.prototype] - The prototype to use for
316
+ * the new object. If not provided, Object.prototype is used.
317
+ * @param {Function} [reducer] - An optional reducer function to use when
318
+ * creating the new object. If not provided, a default reducer is used that
319
+ * assigns each value to its corresponding key.
320
+ * @returns {object|undefined} - The new object created from the entries, or
321
+ * undefined if the entries array is not valid or contains no valid entries.
322
+ *
323
+ * @example
324
+ * // Create an object with a custom prototype and reducer
325
+ * const myPrototype = { foo: 'bar' };
326
+ * const myReducer = (obj, [key, value]) => {
327
+ * obj[key] = value.toUpperCase();
328
+ * return obj;
329
+ * };
330
+ *
331
+ * const myEntries = [['name', 'John'], ['age', '30']];
332
+ * const myObject = Object.fromEntriesUsing(
333
+ * myEntries, myPrototype, myReducer
334
+ * );
335
+ *
336
+ * // myObject is now { name: 'JOHN', age: '30' }
337
+ * // with prototype { foo: 'bar' }
338
+ */
339
+ fromEntriesUsing(entries, prototype = Object.prototype, reducer = undefined) {
340
+ if (!Array.isArray(entries)) {
341
+ return undefined;
342
+ }
343
+
344
+ const entriesToUse = entries.filter(
345
+ entry => Array.isArray(entry) && entry.length >= 2
346
+ );
347
+
348
+ if (!entriesToUse.length) {
349
+ return undefined;
350
+ }
351
+
352
+ const useReducer = reducer instanceof Function
353
+ ? reducer
354
+ : (accumulator, [key, value]) => {
355
+ accumulator[key] = value;
356
+ return accumulator;
357
+ };
358
+
359
+ return entriesToUse.reduce(
360
+ useReducer, Object.create(prototype ?? Object.prototype)
361
+ );
362
+ },
363
+
364
+ /**
365
+ * Retrieves the prototype chain entries of a given object.
366
+ *
367
+ * This method traverses the prototype chain of the provided object and
368
+ * collects an array of entries. Each entry is a pair consisting of the
369
+ * prototype object and its property descriptors.
370
+ *
371
+ * The property descriptors are obtained using the `Reflect.ownKeys`
372
+ * method and the `Object.getOwnPropertyDescriptor` function.
373
+ *
374
+ * @param {Object} object - The object whose prototype chain entries are
375
+ * to be retrieved.
376
+ * @returns {Array} An array of entries, where each entry is a pair
377
+ * consisting of a prototype object and its property descriptors.
378
+ *
379
+ * @example
380
+ * const obj = Object.create({ foo: 'bar' });
381
+ * console.log(getPrototypeChainEntries(obj));
382
+ * // Output: [[{ foo: 'bar' }, { foo: { value: 'bar', writable: true,
383
+ * // enumerable: true, configurable: true } }], [Object.prototype, { ... }]]
384
+ */
385
+ getPrototypeChainEntries(object) {
386
+ const entries = []
387
+
388
+ let prototype = Object.getPrototypeOf(object)
389
+ while (prototype) {
390
+ const descriptors = Reflect.ownKeys(prototype).reduce((acc, key) => {
391
+ acc[key] = Object.getOwnPropertyDescriptor(prototype, key)
392
+ return acc
393
+ }, {})
394
+
395
+ entries.push([prototype, descriptors])
396
+
397
+ prototype = Object.getPrototypeOf(prototype)
398
+ }
399
+
400
+ return entries
401
+ },
402
+
403
+ /**
404
+ * Retrieves the string tag of an object. The string tag is a representation
405
+ * of the object's type, as defined by its `Object.prototype.toString`
406
+ * method. This utility method is helpful for getting a more descriptive
407
+ * type of an object than what is returned by the `typeof` operator,
408
+ * especially for custom objects.
409
+ *
410
+ * @param {*} value - The object whose string tag is to be retrieved.
411
+ * @param {boolean} strict - if this is set to true, undefined will be
412
+ * returned whenever a supplied object does not have a
413
+ * `Symbol.toStringTag` defined, period. if false, the default,
414
+ * @returns {string} - The string tag of the object, indicating its type.
415
+ */
416
+ getStringTag(value, strict = false) {
417
+ if (Object.hasStringTag(value)) {
418
+ return value[Symbol.toStringTag]
419
+ }
420
+
421
+ if (strict) {
422
+ return undefined
423
+ }
424
+
425
+ if (value && (typeof value === 'function')) {
426
+ return value.name
427
+ }
428
+
429
+ return /\s(.+)]/.exec(Object.prototype.toString.call(value))[1];
430
+ },
431
+
432
+ /**
433
+ * Determines the type of the given value based on its string tag. This method
434
+ * uses `Object.getStringTag` to obtain the string tag of the value, which
435
+ * represents its more specific type (e.g., Array, Map, Set) rather than just
436
+ * 'object'. The method then maps this string tag to the corresponding type
437
+ * present in the provided `owner` object, which defaults to `globalThis`.
438
+ * This utility method is especially useful for identifying the specific
439
+ * constructor or class of an object, beyond the basic types identified by
440
+ * the `typeof` operator.
441
+ *
442
+ * @param {any} value - The value whose type is to be determined.
443
+ * @param {object} [owner=globalThis] - The object in which to look up the
444
+ * constructor corresponding to the string tag. Defaults to `globalThis`,
445
+ * which covers global constructors like `Array`, `Object`, etc.
446
+ * @returns {Function|object|null|undefined} - Returns the constructor or
447
+ * type of the value based on its string tag. For 'Null' and 'Undefined',
448
+ * it returns `null` and `undefined`, respectively. For other types, it
449
+ * returns the corresponding constructor (e.g., `Array` for arrays) if
450
+ * available in the `owner` object.
451
+ */
452
+ getType(value, owner = globalThis) {
453
+ const stringTag = Object.getStringTag(value)
454
+
455
+ switch (stringTag) {
456
+ case 'Null': return null
457
+ case 'Undefined': return undefined
458
+ default:
459
+ return owner[stringTag]
460
+ }
461
+ },
462
+
463
+ /**
464
+ * Checks to see if the supplied `value` is both an object, and has the
465
+ * appropriate symbol defined.
466
+ *
467
+ * @param {any} value the value to determine if it contains a defined
468
+ * `Symbol.toStringTag` defined.
469
+ * @returns true if the symbol is defined, false otherwise
470
+ */
471
+ hasStringTag(value) {
472
+ return Object.isObject(value) && Reflect.has(value, Symbol.toStringTag)
473
+ },
474
+
475
+ /**
476
+ * The function checks if a value is either `undefined` or `null`.
477
+ *
478
+ * @param {any} value - The parameter "value" is a variable that can hold
479
+ * any value.
480
+ * @returns {boolean} `true` if the value is either `undefined` or `null`,
481
+ * and `false` otherwise.
482
+ */
483
+ isNullDefined(value) {
484
+ return value === undefined || value === null
485
+ },
486
+
487
+ /**
488
+ * The `ifNullDefined` function checks if a given value is either `null` or
489
+ * `undefined` and returns one of two provided values based on the result.
490
+ * This function is a convenience method for performing conditional
491
+ * operations based on the type of a value.
492
+ *
493
+ * @param {any} value - The value to be checked. If this is either `null`
494
+ * or `undefined`, `thenValue` is returned, otherwise `elseValue`
495
+ * is returned.
496
+ * @param {function | any} thenValue - The value to be returned if `value`
497
+ * is either `null` or `undefined`.
498
+ * @param {function | any} elseValue - The value to be returned if `value`
499
+ * is not either `null` or `undefined`.
500
+ * @returns {*} Returns `thenValue` if `value` is either `null` or
501
+ * `undefined`, otherwise returns `elseValue`.
502
+ *
503
+ * @example
504
+ * // Suppose we have a null value and a defined value
505
+ * let nullValue = null;
506
+ * let definedValue = "I'm defined";
507
+ *
508
+ * // Using ifNullDefined
509
+ * // Output: 'Null or Undefined'
510
+ * console.log(
511
+ * Object.ifNullDefined(nullValue, 'Null or Undefined', 'Defined')
512
+ * );
513
+ *
514
+ * // Output: 'Defined'
515
+ * console.log(
516
+ * Object.ifNullDefined(definedValue, 'Null or Undefined', 'Defined')
517
+ * );
518
+ */
519
+ ifNullDefined(value, thenValue, elseValue) {
520
+ return isThenElse(this.isNullDefined(value), thenValue, elseValue);
521
+ },
522
+
523
+ /**
524
+ * Checks if the provided value is an object.
525
+ *
526
+ * This function checks if the provided value is an instance of an Object
527
+ * or if the value is truthy and its type is 'object'. This is used to
528
+ * determine if a value can have properties and methods like an object.
529
+ *
530
+ * @param {any} value - The value to be checked.
531
+ * @returns {boolean} Returns `true` if the value is an object, `false`
532
+ * otherwise.
533
+ *
534
+ * @example
535
+ * // Using a string
536
+ * console.log(isObject('Hello, world!')); // Output: false
537
+ *
538
+ * // Using an object
539
+ * console.log(isObject({ key: 'value' })); // Output: true
540
+ *
541
+ * // Using null
542
+ * console.log(isObject(null)); // Output: false
543
+ */
544
+ isObject(value) {
545
+ return value instanceof Object || value && typeof value === 'object'
546
+ },
547
+
548
+ /**
549
+ * Determines if the provided value is an object. This method checks whether
550
+ * the value is an instance of `Object` or if its type is 'object'. It's a
551
+ * utility method for type-checking, ensuring that a value is an object
552
+ * before performing operations that are specific to objects.
553
+ *
554
+ * @param {*} value - The value to be checked.
555
+ * @returns {boolean} - Returns `true` if the value is an object,
556
+ * otherwise `false`.
557
+ },
558
+ isObject(value) {
559
+ return value && (value instanceof Object || typeof value === 'object');
560
+ },
561
+
562
+ /**
563
+ * Checks to see if the supplied value is a primitive value.
564
+ *
565
+ * @param {any} value the value to test to see if it is a primitive value type
566
+ * @returns true if the object is considered widely to be a primitive value,
567
+ * false otherwise.
568
+ */
569
+ isPrimitive(value) {
570
+ // Check for null as a special case because typeof null
571
+ // is 'object'
572
+ if (value === null) {
573
+ return true;
574
+ }
575
+
576
+ // Check for other primitives
577
+ switch (typeof value) {
578
+ case 'string':
579
+ case 'number':
580
+ case 'bigint':
581
+ case 'boolean':
582
+ case 'undefined':
583
+ case 'symbol':
584
+ return true;
585
+ default:
586
+ return false;
587
+ }
588
+ },
589
+
590
+ /**
591
+ * Executes a conditional function based on whether the provided value is
592
+ * primitive or not. This method first checks if the value is primitive
593
+ * using the `isPrimitive` method. If it is, it returns the `thenValue`,
594
+ * otherwise it returns the `elseValue`.
595
+ *
596
+ * @param {any} value - The value to be checked.
597
+ * @param {function | any} thenValue - The value to return if `value` is
598
+ * primitive.
599
+ * @param {function | any} elseValue - The value to return if `value` is
600
+ * not primitive.
601
+ * @returns {*} - Returns `thenValue` if the value is primitive, otherwise
602
+ * `elseValue`.
603
+ *
604
+ * @example
605
+ * // returns 1
606
+ * ifPrimitive('hello', 1, 2)
607
+ * // returns 2
608
+ * ifPrimitive({a: 'hello'}, 1, 2)
609
+ */
610
+ ifPrimitive(value, thenValue, elseValue) {
611
+ return isThenElse(this.isPrimitive(value), thenValue, elseValue)
612
+ },
613
+
614
+ /**
615
+ * Checks if the given value is a valid key for an object. In JavaScript, a
616
+ * valid key can be either a string or a symbol. This method is useful for
617
+ * validating object keys before using them in operations like setting or
618
+ * getting object properties.
619
+ *
620
+ * @param {*} value - The value to be checked.
621
+ * @returns {boolean} - Returns `true` if the value is a valid object key
622
+ * (string or symbol), otherwise `false`.
623
+ */
624
+ isValidKey(value) {
625
+ return (typeof value === 'string' || typeof value === 'symbol');
626
+ },
627
+
628
+ /**
629
+ * Executes a conditional function based on whether the provided
630
+ * value is a valid key for an object. This method first checks if
631
+ * the value is a valid key using the `isValidKey` method. If it is,
632
+ * it returns the `thenValue`, otherwise it returns the `elseValue`.
633
+ *
634
+ * @param {any} value - The value to be checked.
635
+ * @param {function | any} thenValue - The value to return if
636
+ * `value` is a valid key.
637
+ * @param {function | any} elseValue - The value to return if
638
+ * `value` is not a valid key.
639
+ * @returns {any} - Returns `thenValue` if the value is a valid key,
640
+ * otherwise `elseValue`.
641
+ *
642
+ * @example
643
+ * // returns 1
644
+ * ifValidKey('name', 1, 2)
645
+ * // returns 2
646
+ * ifValidKey(123, 1, 2)
647
+ */
648
+ ifValidKey(value, thenValue, elseValue) {
649
+ return isThenElse(this.isValidKey(value), thenValue, elseValue)
650
+ },
651
+
652
+ /**
653
+ * A symbol constant defined on Object that can be used to reference
654
+ * storage for an accessor descriptor created with Object.add() or
655
+ * other descriptor assigning and creation methods used by this extension.
656
+ *
657
+ * The value assigned here is actually another symbol but one generated
658
+ * by {@link Symkeys} for uniqueness and has access to data storage.
659
+ *
660
+ * @returns {Symbol} - Returns a symbol for the descriptor storage.
661
+ *
662
+ * @example
663
+ * // returns Symbol(@nejs.object.descriptor.storage)
664
+ * kDescriptorStore
665
+ *
666
+ * // add descriptor value to an object
667
+ * const object = {}
668
+ * Object.add({object, key: 'name', type: 'accessor'})
669
+ * object.name = 'Jane Doe'
670
+ *
671
+ * // Value assigned here is another symbol with its own storage generated
672
+ * // by Symkeys. Description might be '@nejs.descriptor.store #234sdafj'
673
+ * object[Object.kDescriptorStore]
674
+ *
675
+ * // But its nested data can be accessed using the '.data' getter
676
+ * object[Object.kDescriptorStore].data // { name: 'Jane Doe' }
677
+ */
678
+ get kDescriptorStore() {
679
+ return Symbol.for('@nejs.object.descriptor.storage')
680
+ },
681
+
682
+ /**
683
+ * Creates an object with predefined keys and descriptors. This method is
684
+ * useful for creating objects with specific properties and behaviors.
685
+ *
686
+ * @param {Array|Object} keys - An array of keys or an object where keys
687
+ * are the object's own properties. If an array is provided, each key will
688
+ * be assigned the `defaultValue`. If an object is provided, its own
689
+ * properties will be used as keys and their corresponding values as values.
690
+ * @param {*} [defaultValue=undefined] - The default value for each key.
691
+ * @param {string} [definedAs='data'] - Defines how the properties are
692
+ * defined. If 'data', properties are defined with a value. If 'accessor',
693
+ * properties are defined with get and set accessors.
694
+ * @param {Object} [accessorMeta={ get: undefined, set: undefined,
695
+ * thisArg: undefined }] - An object containing the get and set accessors
696
+ * and the `thisArg` to bind to the accessors.
697
+ * @param {Object} [descriptorBase={ enumerable: true, configurable: true }]
698
+ * - The base descriptor for the properties.
699
+ * @param {Object} [extraDescriptors=undefined] - Extra descriptors to be
700
+ * added to the object.
701
+ * @param {Object} [prototype=Object.prototype] - The prototype of the
702
+ * created object.
703
+ * @returns {Object} - Returns the created object.
704
+ *
705
+ * @example
706
+ * // returns { name: undefined }
707
+ * prekeyed(['name'])
708
+ * // returns { name: 'John' }
709
+ * prekeyed({ name: 'John' })
710
+ * // returns { name: 'John' }
711
+ * prekeyed(['name'], 'John')
712
+ */
713
+ prekeyed(
714
+ keys,
715
+ defaultValue = undefined,
716
+ definedAs = ['data', 'accessor'][0],
717
+ accessorMeta = { get: undefined, set: undefined, thisArg: undefined },
718
+ descriptorBase = { enumerable: true, configurable: true },
719
+ extraDescriptors = undefined,
720
+ prototype = Object.prototype
721
+ ) {
722
+ const object = Object.create(prototype, extraDescriptors)
723
+ let mapped = {}
724
+
725
+ if (Array.isArray(keys)) {
726
+ mapped = keys.reduce((a, k) => ({ ...a, [k]: defaultValue }), {})
727
+ }
728
+ else if (keys && typeof keys === 'object') {
729
+ Object.assign(mapped, keys)
730
+ }
731
+ else {
732
+ console.warn('skipping')
733
+ return object
734
+ }
735
+
736
+ for (const [key, value] of Object.entries(mapped)) {
737
+ let symKey = Symbol.for(`${key}#${Math.random().toString(36).slice(2)}`)
738
+ let suppliedValue = mapped[key] ?? defaultValue
739
+ if (definedAs === 'accessor' && accessorMeta.get === undefined) {
740
+ Object.defineProperty(
741
+ object, symKey, {
742
+ value: suppliedValue, enumerable: false, configurable: true
743
+ }
744
+ )
745
+ accessorMeta.thisArg = object
746
+ }
747
+
748
+ let descriptorRest = definedAs === 'data'
749
+ ? { value: value ?? defaultValue, writable: true }
750
+ : {
751
+ get: accessorMeta.get ?? function() { return this[symKey] },
752
+ set: accessorMeta.set ?? function(v) { this[symKey] = v }
753
+ }
754
+
755
+ if (accessorMeta.thisArg) {
756
+ descriptorRest.get = descriptorRest.get.bind(accessorMeta.thisArg)
757
+ descriptorRest.set = descriptorRest.set.bind(accessorMeta.thisArg)
758
+ }
759
+
760
+ Object.defineProperty(
761
+ object, key, { ...descriptorBase, ...descriptorRest }
762
+ )
763
+ }
764
+
765
+ return object
766
+ },
767
+
768
+ /**
769
+ * Strips an object down to only the keys specified. Optionally, any
770
+ * accessors can be made to retain their context on the source object.
771
+ *
772
+ * @param {object} object the object to pare down
773
+ * @param {Array<string|symbol>} keys the keys that should appear in the
774
+ * final reduced object
775
+ * @param {boolean} [bindAccessors = true] if this value is true then any
776
+ * accessors from the source object will continue to have their `this`
777
+ * value bound to the source. If the getter or setter on that object is
778
+ * defined using an arrow function, this will not work as intended.
779
+ * @returns {object} an object containing only the keys and symbols
780
+ * specified in the `keys` parameter.
781
+ */
782
+ stripTo(object, keys, bindAccessors = true) {
783
+ if (!object || typeof object !== 'object') {
784
+ throw new TypeError(
785
+ 'Object.stripTo requires an object to strip. Received',
786
+ object
787
+ );
788
+ }
789
+
790
+ const result = {};
791
+
792
+ if (!Array.isArray(keys)) {
793
+ return result;
794
+ }
795
+
796
+ for (let key of keys) {
797
+ if (Reflect.has(object, key)) {
798
+ const originalDescriptor = Object.getOwnPropertyDescriptor(object, key);
799
+ const descriptor = { ...originalDescriptor };
800
+
801
+ if (
802
+ typeof descriptor.get === 'function' ||
803
+ typeof descriptor.set === 'function'
804
+ ) {
805
+ if (bindAccessors) {
806
+ descriptor.get = descriptor.get?.bind(object);
807
+ descriptor.set = descriptor.set?.bind(object);
808
+ }
809
+ }
810
+
811
+ Object.defineProperty(result, key, descriptor);
812
+ }
813
+ }
814
+
815
+ return result;
816
+ },
817
+ },
818
+ });
819
+
820
+ const {
821
+ isObject: pIsObject, ifObject: pIfObject,
822
+ isNullDefined: pIsNullDefined, ifNullDefined: pIfNullDefined,
823
+ isPrimitive: pIsPrimitive, ifPrimitive: pIfPrimitive,
824
+ isValidKey: pIsValidKey, ifValidKey: pIfValidKey,
825
+ hasStringTag: pHasStringTag, getStringTag: pGetStringTag,
826
+ stripTo: pStripTo,
827
+
828
+ } = ObjectExtensions.patches;
829
+
830
+ /**
831
+ * `ObjectPrototypeExtensions` is a constant that applies a patch to the
832
+ * Object prototype. This patch extends the Object prototype with additional
833
+ * methods and properties, enhancing its functionality.
834
+ *
835
+ * The `Patch` function takes two arguments: the target object to be patched
836
+ * (in this case, `Object.prototype`), and an object containing the methods
837
+ * and properties to be added to the target object.
838
+ *
839
+ * @example
840
+ * // Using a method added by ObjectPrototypeExtensions
841
+ * const obj = {};
842
+ * console.log(obj.isObject()); // Output: true
843
+ *
844
+ * @const
845
+ * @type {Patch}
846
+ * @memberof module:object.extensions
847
+ */
848
+ export const ObjectPrototypeExtensions = new Patch(Object.prototype, {
849
+ [Patch.kMutablyHidden]: {
850
+ /**
851
+ * Retrieves the prototype chain entries of the current object.
852
+ *
853
+ * This method traverses the prototype chain of the current object
854
+ * (`this`) and collects an array of entries. Each entry is a pair
855
+ * consisting of the prototype object and its property descriptors.
856
+ *
857
+ * The property descriptors are obtained using the `Reflect.ownKeys`
858
+ * method and the `Object.getOwnPropertyDescriptor` function.
859
+ *
860
+ * @returns {Array} An array of entries, where each entry is a pair
861
+ * consisting of a prototype object and its property descriptors.
862
+ *
863
+ * @example
864
+ * const obj = Object.create({ foo: 'bar' });
865
+ * console.log(obj.getPrototypeChainEntries());
866
+ * // Output: [[{ foo: 'bar' }, { foo: { value: 'bar', writable: true, enumerable: true, configurable: true } }], [Object.prototype, { ... }]]
867
+ */
868
+ getPrototypeChainEntries() {
869
+ return ObjectExtensions.patches.getPrototypeChainEntries(this)
870
+ },
871
+
872
+ /**
873
+ * Determines if the current value is an object.
874
+ *
875
+ * This method checks whether the current value is an object,
876
+ * excluding null. It is a convenience wrapper around the
877
+ * `pIsObject` function from the `ObjectExtensions` patch.
878
+ *
879
+ * @name isObject
880
+ * @type {function}
881
+ * @memberof Object.prototype
882
+ * @returns {boolean} `true` if the current value is an object
883
+ * (excluding null), `false` otherwise.
884
+ *
885
+ * @example
886
+ * const obj = {};
887
+ * console.log(obj.isObject());
888
+ * // Output: true
889
+ *
890
+ * const str = "hello";
891
+ * console.log(str.isObject());
892
+ * // Output: false
893
+ *
894
+ * console.log(null.isObject());
895
+ * // Output: false
896
+ */
897
+ get isObject() {
898
+ return pIsObject(this)
899
+ },
900
+
901
+ /**
902
+ * Checks if the current value is an object and returns one of two
903
+ * provided values based on the result. This function is a convenience
904
+ * method for performing conditional operations based on the type of
905
+ * the current value.
906
+ *
907
+ * @name ifObject
908
+ * @type {function}
909
+ * @memberof Object.prototype
910
+ * @param {function | any} thenValue - The value to be returned if the
911
+ * current value is an object (excluding null).
912
+ * @param {function | any} elseValue - The value to be returned if the
913
+ * current value is not an object or is null.
914
+ * @returns {*} Returns `thenValue` if the current value is an object
915
+ * (excluding null), otherwise returns `elseValue`.
916
+ *
917
+ * @example
918
+ * const obj = {};
919
+ * console.log(obj.ifObject('Object', 'Not an object'));
920
+ * // Output: 'Object'
921
+ *
922
+ * const str = "hello";
923
+ * console.log(str.ifObject('Object', 'Not an object'));
924
+ * // Output: 'Not an object'
925
+ *
926
+ * console.log(null.ifObject('Object', 'Not an object'));
927
+ * // Output: 'Not an object'
928
+ */
929
+ ifObject(thenValue, elseValue) {
930
+ return pIfObject(this, thenValue, elseValue)
931
+ },
932
+
933
+ /**
934
+ * Checks if the current value is either `null` or `undefined`.
935
+ *
936
+ * @name isNullDefined
937
+ * @type {boolean}
938
+ * @memberof Object.prototype
939
+ * @returns {boolean} Returns `true` if the current value is either
940
+ * `null` or `undefined`, `false` otherwise.
941
+ *
942
+ * @example
943
+ * const obj = null;
944
+ * console.log(obj.isNullDefined);
945
+ * // Output: true
946
+ *
947
+ * const str = "hello";
948
+ * console.log(str.isNullDefined);
949
+ * // Output: false
950
+ */
951
+ get isNullDefined() {
952
+ return pIsNullDefined(this)
953
+ },
954
+
955
+ /**
956
+ * Checks if the current value is either `null` or `undefined` and
957
+ * returns one of two provided values based on the result.
958
+ *
959
+ * @name ifNullDefined
960
+ * @type {function}
961
+ * @memberof Object.prototype
962
+ * @param {function | any} thenValue - The value to be returned if the
963
+ * current value is either `null` or `undefined`.
964
+ * @param {function | any} elseValue - The value to be returned if the
965
+ * current value is not `null` or `undefined`.
966
+ * @returns {*} Returns `thenValue` if the current value is either
967
+ * `null` or `undefined`, otherwise returns `elseValue`.
968
+ *
969
+ * @example
970
+ * const obj = null
971
+ * console.log(obj.ifNullDefined('Null or Undefined', 'Defined'))
972
+ * // Output: 'Null or Undefined'
973
+ *
974
+ * const str = "hello"
975
+ * console.log(str.ifNullDefined('Null or Undefined', 'Defined'))
976
+ * // Output: 'Defined'
977
+ */
978
+ ifNullDefined(thenValue, elseValue) {
979
+ return pIfNullDefined(this, thenValue, elseValue)
980
+ },
981
+
982
+ /**
983
+ * Checks if the current value is a primitive type.
984
+ *
985
+ * Primitive types in JavaScript include `string`, `number`,
986
+ * `bigint`, `boolean`, `undefined`, `symbol`, and `null`.
987
+ *
988
+ * @name isPrimitive
989
+ * @type {boolean}
990
+ * @memberof Object.prototype
991
+ * @returns {boolean} Returns `true` if the current value is a
992
+ * primitive type, `false` otherwise.
993
+ *
994
+ * @example
995
+ * const str = "hello"
996
+ * console.log(str.isPrimitive)
997
+ * // Output: true
998
+ *
999
+ * const obj = { key: "value" }
1000
+ * console.log(obj.isPrimitive)
1001
+ * // Output: false
1002
+ */
1003
+ get isPrimitive() {
1004
+ return pIsPrimitive(this)
1005
+ },
1006
+
1007
+ /**
1008
+ * Checks if the current value is a primitive type and returns one
1009
+ * of two provided values based on the result.
1010
+ *
1011
+ * Primitive types in JavaScript include `string`, `number`,
1012
+ * `bigint`, `boolean`, `undefined`, `symbol`, and `null`.
1013
+ *
1014
+ * @name ifPrimitive
1015
+ * @type {function}
1016
+ * @memberof Object.prototype
1017
+ * @param {function | any} thenValue - The value to be returned if
1018
+ * the current value is a primitive type.
1019
+ * @param {function | any} elseValue - The value to be returned if
1020
+ * the current value is not a primitive type.
1021
+ * @returns {*} Returns `thenValue` if the current value is a
1022
+ * primitive type, otherwise returns `elseValue`.
1023
+ *
1024
+ * @example
1025
+ * const str = "hello"
1026
+ * console.log(str.ifPrimitive('Primitive', 'Not Primitive'))
1027
+ * // Output: 'Primitive'
1028
+ *
1029
+ * const obj = { key: "value" }
1030
+ * console.log(obj.ifPrimitive('Primitive', 'Not Primitive'))
1031
+ * // Output: 'Not Primitive'
1032
+ */
1033
+ ifPrimitive(thenValue, elseValue) {
1034
+ return pIfPrimitive(this, thenValue, elseValue)
1035
+ },
1036
+
1037
+ /**
1038
+ * Determines if the current value is a valid key for an object.
1039
+ *
1040
+ * A valid key is either a string or a symbol. This method is a
1041
+ * convenience wrapper around the `pIsValidKey` function from the
1042
+ * `ObjectExtensions` patch.
1043
+ *
1044
+ * @name isValidKey
1045
+ * @type {boolean}
1046
+ * @memberof Object.prototype
1047
+ * @returns {boolean} `true` if the current value is a valid key for
1048
+ * an object (i.e., a string or symbol), `false` otherwise.
1049
+ *
1050
+ * @example
1051
+ * const str = "key"
1052
+ * console.log(str.isValidKey)
1053
+ * // Output: true
1054
+ *
1055
+ * const sym = Symbol("key")
1056
+ * console.log(sym.isValidKey)
1057
+ * // Output: true
1058
+ *
1059
+ * const num = 42
1060
+ * console.log(num.isValidKey)
1061
+ * // Output: false
1062
+ */
1063
+ get isValidKey() {
1064
+ return pIsValidKey(this)
1065
+ },
1066
+
1067
+ /**
1068
+ * Checks if the current value is a valid key for an object and returns
1069
+ * one of two provided values based on the result. This function is a
1070
+ * convenience method for performing conditional operations based on
1071
+ * the type of the current value.
1072
+ *
1073
+ * A valid key is either a string or a symbol.
1074
+ *
1075
+ * @name ifValidKey
1076
+ * @type {function}
1077
+ * @memberof Object.prototype
1078
+ * @param {function | any} thenValue - The value to be returned if the
1079
+ * current value is a valid key for an object.
1080
+ * @param {function | any} elseValue - The value to be returned if the
1081
+ * current value is not a valid key for an object.
1082
+ * @returns {*} Returns `thenValue` if the current value is a valid key
1083
+ * for an object, otherwise returns `elseValue`.
1084
+ *
1085
+ * @example
1086
+ * const str = "key"
1087
+ * console.log(str.ifValidKey('Valid Key', 'Not a Valid Key'))
1088
+ * // Output: 'Valid Key'
1089
+ *
1090
+ * const num = 42
1091
+ * console.log(num.ifValidKey('Valid Key', 'Not a Valid Key'))
1092
+ * // Output: 'Not a Valid Key'
1093
+ */
1094
+ ifValidKey(thenValue, elseValue) {
1095
+ return pIfValidKey(this, thenValue, elseValue)
1096
+ },
1097
+
1098
+ /**
1099
+ * Checks to see if the supplied `value` is both an object, and has the
1100
+ * appropriate symbol defined.
1101
+ *
1102
+ * @param {any} value the value to determine if it contains a defined
1103
+ * `Symbol.toStringTag` defined.
1104
+ * @returns true if the symbol is defined, false otherwise
1105
+ */
1106
+ get hasStringTag() {
1107
+ return pHasStringTag(this)
1108
+ },
1109
+
1110
+ /**
1111
+ * Retrieves the string tag of an object. The string tag is a representation
1112
+ * of the object's type, as defined by its `Object.prototype.toString`
1113
+ * method. This utility method is helpful for getting a more descriptive
1114
+ * type of an object than what is returned by the `typeof` operator,
1115
+ * especially for custom objects.
1116
+ *
1117
+ * @param {*} value - The object whose string tag is to be retrieved.
1118
+ * @param {boolean} strict - if this is set to true, undefined will be
1119
+ * returned whenever a supplied object does not have a
1120
+ * `Symbol.toStringTag` defined, period. if false, the default,
1121
+ * @returns {string} - The string tag of the object, indicating its type.
1122
+ */
1123
+ getStringTag(strict = false) {
1124
+ return pGetStringTag(this, strict)
1125
+ },
1126
+
1127
+ /**
1128
+ * Strips an object down to only the keys specified. Optionally, any
1129
+ * accessors can be made to retain their context on the source object.
1130
+ * This is a passthrough to the static {@link Object.stripTo} function
1131
+ *
1132
+ * @param {Array<string|symbol>} keys the keys that should appear in the
1133
+ * final reduced object
1134
+ * @param {boolean} [bindAccessors = true] if this value is true then any
1135
+ * accessors from the source object will continue to have their `this`
1136
+ * value bound to the source. If the getter or setter on that object is
1137
+ * defined using an arrow function, this will not work as intended.
1138
+ * @returns {object} an object containing only the keys and symbols
1139
+ * specified in the `keys` parameter.
1140
+ */
1141
+ stripTo(keys, bindAccessors = true) {
1142
+ return pStripTo(this, keys, bindAccessors)
1143
+ },
1144
+ },
1145
+ })
1146
+
1147
+ // NOTE to self; this is repeated here otherwise a circular reference from
1148
+ // Object<->Function<->Global occurs. See original source in global.this.js
1149
+ // {@see globalThis.isThenElse}
1150
+ function isThenElse(bv, tv, ev) {
1151
+ if (arguments.length > 1) {
1152
+ var _then = isFunction(tv) ? tv(bv) : tv; if (arguments.length > 2) {
1153
+ var _else = isFunction(ev) ? tv(bv) : ev; return bv ? _then : _else
1154
+ } return bv || _then;
1155
+ } return bv
1156
+ }
1157
+
1158
+ /**
1159
+ * Creates a deep or shallow copy of the provided source objects and merges
1160
+ * them into the destination object. The function uses a Set to keep track
1161
+ * of visited objects to avoid circular references.
1162
+ *
1163
+ * @function
1164
+ * @name copyObject
1165
+ * @param {boolean} deep - If true, performs a deep copy, otherwise performs
1166
+ * a shallow copy.
1167
+ * @param {object} destination - The object to which properties will be copied.
1168
+ * @param {...object} sources - The source object(s) from which properties
1169
+ * will be copied.
1170
+ * @returns {object} The destination object with the copied properties.
1171
+ *
1172
+ * @example
1173
+ * // Shallow copy
1174
+ * const obj1 = { a: 1, b: { c: 2 } };
1175
+ * const obj2 = { b: { d: 3 }, e: 4 };
1176
+ * const result = copyObject(false, obj1, obj2);
1177
+ * console.log(result); // Output: { a: 1, b: { d: 3 }, e: 4 }
1178
+ *
1179
+ * @example
1180
+ * // Deep copy
1181
+ * const obj1 = { a: 1, b: { c: 2 } };
1182
+ * const obj2 = { b: { d: 3 }, e: 4 };
1183
+ * const result = copyObject(true, obj1, obj2);
1184
+ * console.log(result); // Output: { a: 1, b: { c: 2, d: 3 }, e: 4 }
1185
+ */
1186
+ export function copyObject(deep, destination, ...sources) {
1187
+ const visited = new Set()
1188
+
1189
+ for (const source of sources) {
1190
+ if (source === null || typeof source !== 'object' || visited.has(source)) {
1191
+ continue
1192
+ }
1193
+
1194
+ visited.add(source)
1195
+
1196
+ const keys = Reflect.ownKeys(source)
1197
+ for (const key of keys) {
1198
+ let descriptor
1199
+ try {
1200
+ descriptor = Object.getOwnPropertyDescriptor(source, key)
1201
+ } catch (err) {
1202
+ console.warn(`Failed to get descriptor for key "${key}": ${err}`)
1203
+ continue
1204
+ }
1205
+
1206
+ const isDataDesc = Reflect.has(descriptor, 'value')
1207
+ const keyedValue = descriptor?.value
1208
+
1209
+ const conditionsMet = [
1210
+ isDataDesc,
1211
+ keyedValue,
1212
+ typeof keyedValue === 'object',
1213
+ !visited.has(keyedValue)
1214
+ ].every(condition => condition)
1215
+
1216
+ if (conditionsMet) {
1217
+ visited.add(keyedValue)
1218
+
1219
+ const prototype = Object.getPrototypeOf(keyedValue)
1220
+ const descriptors = Object.getOwnPropertyDescriptors(keyedValue)
1221
+ const replacement = Object.create(prototype, descriptors)
1222
+
1223
+ descriptor.value = deep
1224
+ ? copyObject(deep, replacement, keyedValue)
1225
+ : replacement
1226
+ }
1227
+
1228
+ try {
1229
+ Object.defineProperty(destination, key, descriptor)
1230
+ } catch (err) {
1231
+ console.error(`Failed to define property "${key}": ${err}`)
1232
+ }
1233
+ }
1234
+ }
1235
+
1236
+ return destination
1237
+ }