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