@nejs/basic-extensions 2.21.0 → 2.22.6

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 (270) hide show
  1. package/.idea/markdown.xml +8 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/ne-basic-extensions.iml +8 -0
  4. package/.idea/vcs.xml +6 -0
  5. package/CODE_STYLE.md +393 -0
  6. package/CODING_PHILOSOPHY.md +36 -0
  7. package/DOCUMENTATION_GUIDELINES.md +221 -0
  8. package/README.md +78 -4
  9. package/dist/@nejs/basic-extensions.bundle.2.22.6.js +25 -0
  10. package/dist/@nejs/basic-extensions.bundle.2.22.6.js.map +7 -0
  11. package/dist/cjs/classes/index.cjs +11129 -0
  12. package/dist/cjs/classes/index.cjs.map +7 -0
  13. package/dist/cjs/index.cjs +15191 -0
  14. package/dist/cjs/index.cjs.map +7 -0
  15. package/dist/cjs/utils/index.cjs +3954 -0
  16. package/dist/cjs/utils/index.cjs.map +7 -0
  17. package/dist/esm/basic-extensions.mjs +25 -0
  18. package/dist/esm/basic-extensions.mjs.map +7 -0
  19. package/package.json +16 -22
  20. package/repl.bootstrap.js +4 -7
  21. package/repl.history +30 -30
  22. package/src/big.int.extension.js +171 -45
  23. package/src/classes/enumeration.js +466 -0
  24. package/src/classes/index.js +5 -1
  25. package/src/index.js +5 -1
  26. package/src/math.extension.js +73 -0
  27. package/src/number.extension.js +18 -0
  28. package/src/regular.expression.extensions.js +0 -35
  29. package/src/utils/toolkit.js +699 -516
  30. package/tests/arrayextensions.test.js +3 -3
  31. package/tests/index.test.js +3 -1
  32. package/tests/newClasses/asyncIterable.test.js +3 -3
  33. package/tests/newClasses/deferred.test.js +3 -3
  34. package/tests/newClasses/descriptor.test.js +3 -3
  35. package/tests/newClasses/iterable.test.js +3 -3
  36. package/tests/newClasses/refmap.test.js +3 -3
  37. package/tests/newClasses/refset.test.js +3 -3
  38. package/tests/objectextensions.test.js +3 -3
  39. package/tests/setextensions.test.js +3 -3
  40. package/tests/stringextensions.test.js +3 -2
  41. package/tests/utils/descriptor.utils.test.js +1 -1
  42. package/tests/utils/toolkit.test.js +429 -163
  43. package/.esdoc.json +0 -9
  44. package/.vscode/settings.json +0 -5
  45. package/bin/build +0 -27
  46. package/bin/clean +0 -14
  47. package/bin/esbuild +0 -91
  48. package/bin/fixup +0 -13
  49. package/bin/repl.basics.js +0 -584
  50. package/bin/repl.signature.js +0 -63
  51. package/bin/version +0 -100
  52. package/dist/@nejs/basic-extensions.bundle.2.21.0.js +0 -25
  53. package/dist/@nejs/basic-extensions.bundle.2.21.0.js.map +0 -7
  54. package/dist/cjs/array.extensions.d.ts +0 -39
  55. package/dist/cjs/array.extensions.js +0 -477
  56. package/dist/cjs/array.extensions.js.map +0 -1
  57. package/dist/cjs/big.int.extension.d.ts +0 -31
  58. package/dist/cjs/big.int.extension.js +0 -165
  59. package/dist/cjs/big.int.extension.js.map +0 -1
  60. package/dist/cjs/classes/asyncIterable.d.ts +0 -126
  61. package/dist/cjs/classes/asyncIterable.js +0 -209
  62. package/dist/cjs/classes/asyncIterable.js.map +0 -1
  63. package/dist/cjs/classes/deferred.d.ts +0 -146
  64. package/dist/cjs/classes/deferred.js +0 -291
  65. package/dist/cjs/classes/deferred.js.map +0 -1
  66. package/dist/cjs/classes/descriptor.d.ts +0 -334
  67. package/dist/cjs/classes/descriptor.js +0 -537
  68. package/dist/cjs/classes/descriptor.js.map +0 -1
  69. package/dist/cjs/classes/enum.d.ts +0 -50
  70. package/dist/cjs/classes/enum.js +0 -405
  71. package/dist/cjs/classes/enum.js.map +0 -1
  72. package/dist/cjs/classes/index.d.ts +0 -15
  73. package/dist/cjs/classes/index.js +0 -63
  74. package/dist/cjs/classes/index.js.map +0 -1
  75. package/dist/cjs/classes/introspector.d.ts +0 -20
  76. package/dist/cjs/classes/introspector.js +0 -130
  77. package/dist/cjs/classes/introspector.js.map +0 -1
  78. package/dist/cjs/classes/iterable.d.ts +0 -169
  79. package/dist/cjs/classes/iterable.js +0 -268
  80. package/dist/cjs/classes/iterable.js.map +0 -1
  81. package/dist/cjs/classes/param.parser.d.ts +0 -221
  82. package/dist/cjs/classes/param.parser.js +0 -242
  83. package/dist/cjs/classes/param.parser.js.map +0 -1
  84. package/dist/cjs/classes/pluggable.proxy.d.ts +0 -153
  85. package/dist/cjs/classes/pluggable.proxy.js +0 -444
  86. package/dist/cjs/classes/pluggable.proxy.js.map +0 -1
  87. package/dist/cjs/classes/property.d.ts +0 -79
  88. package/dist/cjs/classes/property.js +0 -284
  89. package/dist/cjs/classes/property.js.map +0 -1
  90. package/dist/cjs/classes/refmap.d.ts +0 -238
  91. package/dist/cjs/classes/refmap.js +0 -421
  92. package/dist/cjs/classes/refmap.js.map +0 -1
  93. package/dist/cjs/classes/refset.d.ts +0 -186
  94. package/dist/cjs/classes/refset.js +0 -370
  95. package/dist/cjs/classes/refset.js.map +0 -1
  96. package/dist/cjs/classes/symkeys.d.ts +0 -349
  97. package/dist/cjs/classes/symkeys.js +0 -510
  98. package/dist/cjs/classes/symkeys.js.map +0 -1
  99. package/dist/cjs/classes/type.d.ts +0 -56
  100. package/dist/cjs/classes/type.js +0 -405
  101. package/dist/cjs/classes/type.js.map +0 -1
  102. package/dist/cjs/function.extensions.d.ts +0 -12
  103. package/dist/cjs/function.extensions.js +0 -758
  104. package/dist/cjs/function.extensions.js.map +0 -1
  105. package/dist/cjs/global.this.d.ts +0 -2
  106. package/dist/cjs/global.this.js +0 -300
  107. package/dist/cjs/global.this.js.map +0 -1
  108. package/dist/cjs/index.d.ts +0 -31
  109. package/dist/cjs/index.js +0 -226
  110. package/dist/cjs/index.js.map +0 -1
  111. package/dist/cjs/json.extensions.d.ts +0 -2
  112. package/dist/cjs/json.extensions.js +0 -109
  113. package/dist/cjs/json.extensions.js.map +0 -1
  114. package/dist/cjs/map.extensions.d.ts +0 -3
  115. package/dist/cjs/map.extensions.js +0 -143
  116. package/dist/cjs/map.extensions.js.map +0 -1
  117. package/dist/cjs/number.extension.d.ts +0 -44
  118. package/dist/cjs/number.extension.js +0 -261
  119. package/dist/cjs/number.extension.js.map +0 -1
  120. package/dist/cjs/object.extensions.d.ts +0 -33
  121. package/dist/cjs/object.extensions.js +0 -1091
  122. package/dist/cjs/object.extensions.js.map +0 -1
  123. package/dist/cjs/package.json +0 -3
  124. package/dist/cjs/proxy.extensions.d.ts +0 -2
  125. package/dist/cjs/proxy.extensions.js +0 -207
  126. package/dist/cjs/proxy.extensions.js.map +0 -1
  127. package/dist/cjs/reflect.extensions.d.ts +0 -14
  128. package/dist/cjs/reflect.extensions.js +0 -316
  129. package/dist/cjs/reflect.extensions.js.map +0 -1
  130. package/dist/cjs/regular.expression.extensions.d.ts +0 -2
  131. package/dist/cjs/regular.expression.extensions.js +0 -423
  132. package/dist/cjs/regular.expression.extensions.js.map +0 -1
  133. package/dist/cjs/set.extensions.d.ts +0 -40
  134. package/dist/cjs/set.extensions.js +0 -355
  135. package/dist/cjs/set.extensions.js.map +0 -1
  136. package/dist/cjs/string.extensions.d.ts +0 -23
  137. package/dist/cjs/string.extensions.js +0 -704
  138. package/dist/cjs/string.extensions.js.map +0 -1
  139. package/dist/cjs/symbol.extensions.d.ts +0 -11
  140. package/dist/cjs/symbol.extensions.js +0 -735
  141. package/dist/cjs/symbol.extensions.js.map +0 -1
  142. package/dist/cjs/utils/copy.object.d.ts +0 -408
  143. package/dist/cjs/utils/copy.object.js +0 -720
  144. package/dist/cjs/utils/copy.object.js.map +0 -1
  145. package/dist/cjs/utils/descriptor.utils.d.ts +0 -298
  146. package/dist/cjs/utils/descriptor.utils.js +0 -889
  147. package/dist/cjs/utils/descriptor.utils.js.map +0 -1
  148. package/dist/cjs/utils/index.d.ts +0 -75
  149. package/dist/cjs/utils/index.js +0 -61
  150. package/dist/cjs/utils/index.js.map +0 -1
  151. package/dist/cjs/utils/stdout.d.ts +0 -742
  152. package/dist/cjs/utils/stdout.js +0 -1042
  153. package/dist/cjs/utils/stdout.js.map +0 -1
  154. package/dist/cjs/utils/toolkit.d.ts +0 -1898
  155. package/dist/cjs/utils/toolkit.js +0 -1378
  156. package/dist/cjs/utils/toolkit.js.map +0 -1
  157. package/dist/cjs/weakref.extensions.d.ts +0 -2
  158. package/dist/cjs/weakref.extensions.js +0 -19
  159. package/dist/cjs/weakref.extensions.js.map +0 -1
  160. package/dist/mjs/array.extensions.d.ts +0 -39
  161. package/dist/mjs/array.extensions.js +0 -474
  162. package/dist/mjs/array.extensions.js.map +0 -1
  163. package/dist/mjs/big.int.extension.d.ts +0 -31
  164. package/dist/mjs/big.int.extension.js +0 -162
  165. package/dist/mjs/big.int.extension.js.map +0 -1
  166. package/dist/mjs/classes/asyncIterable.d.ts +0 -126
  167. package/dist/mjs/classes/asyncIterable.js +0 -204
  168. package/dist/mjs/classes/asyncIterable.js.map +0 -1
  169. package/dist/mjs/classes/deferred.d.ts +0 -146
  170. package/dist/mjs/classes/deferred.js +0 -287
  171. package/dist/mjs/classes/deferred.js.map +0 -1
  172. package/dist/mjs/classes/descriptor.d.ts +0 -334
  173. package/dist/mjs/classes/descriptor.js +0 -533
  174. package/dist/mjs/classes/descriptor.js.map +0 -1
  175. package/dist/mjs/classes/enum.d.ts +0 -50
  176. package/dist/mjs/classes/enum.js +0 -400
  177. package/dist/mjs/classes/enum.js.map +0 -1
  178. package/dist/mjs/classes/index.d.ts +0 -15
  179. package/dist/mjs/classes/index.js +0 -46
  180. package/dist/mjs/classes/index.js.map +0 -1
  181. package/dist/mjs/classes/introspector.d.ts +0 -20
  182. package/dist/mjs/classes/introspector.js +0 -126
  183. package/dist/mjs/classes/introspector.js.map +0 -1
  184. package/dist/mjs/classes/iterable.d.ts +0 -169
  185. package/dist/mjs/classes/iterable.js +0 -263
  186. package/dist/mjs/classes/iterable.js.map +0 -1
  187. package/dist/mjs/classes/param.parser.d.ts +0 -221
  188. package/dist/mjs/classes/param.parser.js +0 -238
  189. package/dist/mjs/classes/param.parser.js.map +0 -1
  190. package/dist/mjs/classes/pluggable.proxy.d.ts +0 -153
  191. package/dist/mjs/classes/pluggable.proxy.js +0 -438
  192. package/dist/mjs/classes/pluggable.proxy.js.map +0 -1
  193. package/dist/mjs/classes/property.d.ts +0 -79
  194. package/dist/mjs/classes/property.js +0 -280
  195. package/dist/mjs/classes/property.js.map +0 -1
  196. package/dist/mjs/classes/refmap.d.ts +0 -238
  197. package/dist/mjs/classes/refmap.js +0 -417
  198. package/dist/mjs/classes/refmap.js.map +0 -1
  199. package/dist/mjs/classes/refset.d.ts +0 -186
  200. package/dist/mjs/classes/refset.js +0 -366
  201. package/dist/mjs/classes/refset.js.map +0 -1
  202. package/dist/mjs/classes/symkeys.d.ts +0 -349
  203. package/dist/mjs/classes/symkeys.js +0 -506
  204. package/dist/mjs/classes/symkeys.js.map +0 -1
  205. package/dist/mjs/classes/type.d.ts +0 -56
  206. package/dist/mjs/classes/type.js +0 -401
  207. package/dist/mjs/classes/type.js.map +0 -1
  208. package/dist/mjs/function.extensions.d.ts +0 -12
  209. package/dist/mjs/function.extensions.js +0 -755
  210. package/dist/mjs/function.extensions.js.map +0 -1
  211. package/dist/mjs/global.this.d.ts +0 -2
  212. package/dist/mjs/global.this.js +0 -264
  213. package/dist/mjs/global.this.js.map +0 -1
  214. package/dist/mjs/index.d.ts +0 -31
  215. package/dist/mjs/index.js +0 -204
  216. package/dist/mjs/index.js.map +0 -1
  217. package/dist/mjs/json.extensions.d.ts +0 -2
  218. package/dist/mjs/json.extensions.js +0 -106
  219. package/dist/mjs/json.extensions.js.map +0 -1
  220. package/dist/mjs/map.extensions.d.ts +0 -3
  221. package/dist/mjs/map.extensions.js +0 -140
  222. package/dist/mjs/map.extensions.js.map +0 -1
  223. package/dist/mjs/number.extension.d.ts +0 -44
  224. package/dist/mjs/number.extension.js +0 -258
  225. package/dist/mjs/number.extension.js.map +0 -1
  226. package/dist/mjs/object.extensions.d.ts +0 -33
  227. package/dist/mjs/object.extensions.js +0 -1088
  228. package/dist/mjs/object.extensions.js.map +0 -1
  229. package/dist/mjs/package.json +0 -3
  230. package/dist/mjs/proxy.extensions.d.ts +0 -2
  231. package/dist/mjs/proxy.extensions.js +0 -204
  232. package/dist/mjs/proxy.extensions.js.map +0 -1
  233. package/dist/mjs/reflect.extensions.d.ts +0 -14
  234. package/dist/mjs/reflect.extensions.js +0 -313
  235. package/dist/mjs/reflect.extensions.js.map +0 -1
  236. package/dist/mjs/regular.expression.extensions.d.ts +0 -2
  237. package/dist/mjs/regular.expression.extensions.js +0 -420
  238. package/dist/mjs/regular.expression.extensions.js.map +0 -1
  239. package/dist/mjs/set.extensions.d.ts +0 -40
  240. package/dist/mjs/set.extensions.js +0 -352
  241. package/dist/mjs/set.extensions.js.map +0 -1
  242. package/dist/mjs/string.extensions.d.ts +0 -23
  243. package/dist/mjs/string.extensions.js +0 -701
  244. package/dist/mjs/string.extensions.js.map +0 -1
  245. package/dist/mjs/symbol.extensions.d.ts +0 -11
  246. package/dist/mjs/symbol.extensions.js +0 -732
  247. package/dist/mjs/symbol.extensions.js.map +0 -1
  248. package/dist/mjs/utils/copy.object.d.ts +0 -408
  249. package/dist/mjs/utils/copy.object.js +0 -702
  250. package/dist/mjs/utils/copy.object.js.map +0 -1
  251. package/dist/mjs/utils/descriptor.utils.d.ts +0 -298
  252. package/dist/mjs/utils/descriptor.utils.js +0 -875
  253. package/dist/mjs/utils/descriptor.utils.js.map +0 -1
  254. package/dist/mjs/utils/index.d.ts +0 -75
  255. package/dist/mjs/utils/index.js +0 -45
  256. package/dist/mjs/utils/index.js.map +0 -1
  257. package/dist/mjs/utils/stdout.d.ts +0 -742
  258. package/dist/mjs/utils/stdout.js +0 -1037
  259. package/dist/mjs/utils/stdout.js.map +0 -1
  260. package/dist/mjs/utils/toolkit.d.ts +0 -1898
  261. package/dist/mjs/utils/toolkit.js +0 -1373
  262. package/dist/mjs/utils/toolkit.js.map +0 -1
  263. package/dist/mjs/weakref.extensions.d.ts +0 -2
  264. package/dist/mjs/weakref.extensions.js +0 -16
  265. package/dist/mjs/weakref.extensions.js.map +0 -1
  266. package/jsdoc-config.json +0 -31
  267. package/tsconfig.base.json +0 -28
  268. package/tsconfig.cjs.json +0 -8
  269. package/tsconfig.esm.json +0 -8
  270. package/vitest.config.js +0 -7
@@ -0,0 +1,466 @@
1
+ import { Extension } from '@nejs/extension'
2
+ import { is } from '../utils/toolkit.js'
3
+
4
+ /**
5
+ * Allows the creation of static, complex, enumeration values
6
+ * that can be used and identified at runtime (as opposed to
7
+ * TypeScript enum values).
8
+ *
9
+ * @example
10
+ * class Color extends Enumeration {
11
+ * static {
12
+ * Color.define('red', {r: 255, g: 0, b: 0})
13
+ * Color.define('green', {r: 0, g:255, b: 0})
14
+ * Color.define('blue', {r: 0, g: 0, b: 255})
15
+ *
16
+ * // Color.rgb instances that have no associated
17
+ * // r,g,b values will return a value of 0 for
18
+ * // all r, g, and b values, dynamically.
19
+ * Color.define('rgb', undefined, {
20
+ * get value() {
21
+ * const { r, g, b } = this;
22
+ * return { r: r ?? 0, g: g ?? 0, b: b ?? 0 };
23
+ * }
24
+ * })
25
+ * }
26
+ * }
27
+ *
28
+ * let thing = {name: 'pole', color: Color.rgb.associate({
29
+ * r: 200,
30
+ * g: 200,
31
+ * b: 200
32
+ * })}
33
+ *
34
+ * switch (thing.key) {
35
+ * case Color.red.key:
36
+ * case Color.green.key:
37
+ * case Color.blue.key:
38
+ * return thing.value;
39
+ *
40
+ * case Color.rgb.key: {
41
+ * // Uses SubscriptProxy to get at custom associations
42
+ * const { r, g, b } = thing
43
+ *
44
+ * return { r, g, b }
45
+ * }
46
+ * }
47
+ *
48
+ */
49
+ export class Enumeration {
50
+ /**
51
+ * The case name for this {@link Enumeration} instance.
52
+ *
53
+ * @type {string|symbol}
54
+ */
55
+ key;
56
+
57
+ /**
58
+ * The value for this case name, defaults to the same as the
59
+ * case name unless specifically supplied.
60
+ *
61
+ * @type {any}
62
+ */
63
+ value;
64
+
65
+ /**
66
+ * For {@link Enumeration} instances that have instance level associated
67
+ * values. This is uncommon but is modeled after Swift's enums with
68
+ * associated values. This object is `null` if there are no associations.
69
+ *
70
+ * @type {object}
71
+ */
72
+ associations;
73
+
74
+ /**
75
+ * Creates a new simple {@link Enumeration} case with a key (case name)
76
+ * and associated value of any type. If no value is supplied, it will be
77
+ * set to the value of the key unless `acceptUndefinedValue` is set to
78
+ * true.
79
+ *
80
+ * @param {string|number|symbol} key the case name represented by this
81
+ * instance of {@link Enumeration}.
82
+ * @param {any} value any value for this enumeration case. If this is
83
+ * `undefined` and `acceptUndefinedValue` is set to false (the default)
84
+ * then the value will be identical to the `key`.
85
+ * @param {boolean} [acceptUndefinedValue=false] a flag that allows the
86
+ * rare case of setting a case's value explicitly to `undefined`
87
+ * @returns {Enumeration} a new {@link Enumeration} value, or instance of
88
+ * whatever child class has extended `Enumeration`.
89
+ */
90
+ constructor(key, value, acceptUndefinedValue = false) {
91
+ if (value === undefined && !acceptUndefinedValue) value = key;
92
+
93
+ Object.assign(this, { key, value });
94
+ Object.defineProperty(this, 'associations', {
95
+ value: null,
96
+ configurable: true,
97
+ enumerable: false,
98
+ writable: true,
99
+ });
100
+
101
+ /**
102
+ * Enables a subscript proxy on the instances of each {@link Enumeration}
103
+ * derived instance such that if there is no actual relevant property of
104
+ * the same name, and the instance has associated values, then it returns
105
+ * the value of the property of the same name on the associations object
106
+ * within.
107
+ *
108
+ * @example
109
+ * class Shapes extends Enumeration {
110
+ * static {
111
+ * Shapes.define('other')
112
+ * }
113
+ * }
114
+ *
115
+ * let customShape = Shapes.other.associate({name: 'Dodecahedron'})
116
+ *
117
+ * console.log(customShape.name) // "Dodecahedron"
118
+ *
119
+ * @note if there is an existing property of the same name, such as the
120
+ * `.key` or `.value`, it is safer to use the {@link Enumeration#associated}
121
+ * function retrieve the value. You have been warned.
122
+ */
123
+ Object.setPrototypeOf(
124
+ this.constructor.prototype,
125
+ SubscriptProxy(
126
+ Object.create(Object.getPrototypeOf(this.constructor.prototype))
127
+ )
128
+ );
129
+
130
+ return this;
131
+ }
132
+
133
+ /**
134
+ * Creates a duplicate of this enumeration case, and assigns instance
135
+ * level associated key/value pairs on the copy. It is still of the
136
+ * same enum class type, but has instance level associated value.
137
+ *
138
+ * @param {...(object|string|number|symbol|[string|number|symbol,any])} entries
139
+ * a variadic list of objects (whose key/value pairs will be flattened
140
+ * and added to the associations), a key (string|number|symbol) whose
141
+ * value will be the same as the key, or an Object entry (i.e. an array with
142
+ * the first value being the key and the second value being the value).
143
+ * @returns {*} an instance of this class
144
+ */
145
+ associate(...entries) {
146
+ const associations = { };
147
+
148
+ for (const entry of entries) {
149
+ if (is.object(entry, Object)) {
150
+ Object.assign(associations, ...entries);
151
+ }
152
+ else if (is.array(entry)) {
153
+ const key = is.objectKey(entry[0]);
154
+
155
+ associations[key] = entry[1];
156
+ }
157
+ else if (is.objectKey(entry)) {
158
+ associations[entry] = entry;
159
+ }
160
+ }
161
+
162
+ if (this.hasAssociatedValues) {
163
+ Object.assign(this.associations, associations);
164
+
165
+ return this;
166
+ }
167
+
168
+ // Create a duplicate of this case instance
169
+ const variantCase = Object.create(this);
170
+
171
+ // Assign the associations object we created
172
+ variantCase.associations = associations;
173
+
174
+ return variantCase;
175
+ }
176
+
177
+ /**
178
+ * Shorthand for retrieving an internal associated value
179
+ *
180
+ * @param {string|number|symbol} key a key into the internal
181
+ * associations store. Typically, this value is null.
182
+ * @returns {any|null} null if there is no such named association
183
+ * or if there are no associations stored on this enum value.
184
+ */
185
+ associated(key) {
186
+ return this.associations?.[key];
187
+ }
188
+
189
+ /**
190
+ * Returns true if there is an associated value for this enumeration case.
191
+ *
192
+ * @returns {boolean} true if associations exist, denoting this is as
193
+ * a variant case; false otherwise.
194
+ */
195
+ get hasAssociatedValues() {
196
+ return this.associations !== null;
197
+ }
198
+
199
+ /**
200
+ * Checks to see if this object is, or is loosely, the same as
201
+ * the supplied `someCase` value. This is determined by comparing
202
+ * the `.key` property.
203
+ *
204
+ * @param {any} someCase some object value that might have a
205
+ * matching (double equals) key property value
206
+ * @returns {boolean} true if the objects are loosely equal (==)
207
+ * or if each of `.key` values are loosely equal (==)
208
+ */
209
+ is(someCase) {
210
+ // noinspection EqualityComparisonWithCoercionJS
211
+ return this == someCase || this?.key == someCase?.key;
212
+ }
213
+
214
+ /**
215
+ * Define the string representation of any given {@link Enumeration}
216
+ * instance to be its `.key` value.
217
+ *
218
+ * @returns {string} the value of the `.key` property wrapped in
219
+ * a call to `String()` to ensure conversion.
220
+ */
221
+ toString() {
222
+ return String(this.key);
223
+ }
224
+
225
+ /**
226
+ * Returns a combination of the this class' name followed by this
227
+ * instances key value. This can be more explicit than just using
228
+ * the `.key` property.
229
+ *
230
+ * @example
231
+ * class Shape extends Enumeration {
232
+ * static {
233
+ * Shape.define('circle')
234
+ * Shape.define('square')
235
+ * }
236
+ * }
237
+ *
238
+ * console.log(Shape.circle.case) // 'Shape.circle'
239
+ *
240
+ * // ['Shape.circle', 'Shape.square']
241
+ * console.log([...Shape.values()].map(s => s.case))
242
+ *
243
+ * @type {string}
244
+ */
245
+ get case() {
246
+ return `${this.constructor.name}.${String(this.key)}`
247
+ }
248
+
249
+ /**
250
+ * Define the result of a call to {@link #valueOf} to always be
251
+ * the contents of the `.value` property.
252
+ *
253
+ * @returns {any} the contents of the `.value` property
254
+ */
255
+ valueOf() {
256
+ return this.value
257
+ }
258
+
259
+ /**
260
+ * Returns the `.key` value as a primitive, unless a conversion to
261
+ * number is requested. In which case, if the `.value` propert is
262
+ * of type {@link Number} then it will be returned. In all other
263
+ * cases the result will be `String(this.key)`.
264
+ *
265
+ * @returns {string|number|NaN} returns a {@link String} representation
266
+ * of the `.key` property unless a number is requested. See above
267
+ * for custom logic pertaining to number coercion.
268
+ */
269
+ [Symbol.toPrimitive](hint) {
270
+ switch (hint) {
271
+ default:
272
+ case 'string':
273
+ return String(this.key);
274
+
275
+ case 'number':
276
+ return isNumber(this.value)
277
+ ? this.value
278
+ : Number(this.key)
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Generates a custom tag name that matches this instances class name.
284
+ *
285
+ * @example
286
+ * class Shape extends Enumeration {
287
+ * static { Shape.define('circle') }
288
+ * }
289
+ *
290
+ * console.log(Shape.circle[Symbol.toStringTag]) // 'Shape'
291
+ */
292
+ get [Symbol.toStringTag]() {
293
+ return this.constructor.name;
294
+ }
295
+
296
+ /**
297
+ * Static variant of {@link Enumeration#is} that takes a left and
298
+ * right hand value, then checks to see if both objects are, or are
299
+ * loosely, the same as each other's `.key` value.
300
+ *
301
+ * @param {any} leftCase some object value that might have a
302
+ * matching (double equals) key property value
303
+ * @param {any} rightCase some object value that might have a
304
+ * matching (double equals) key property value
305
+ * @returns {boolean} true if the objects are loosely equal (==)
306
+ * or if each of `.key` values are loosely equal (==)
307
+ */
308
+ static is(leftCase, rightCase) {
309
+ // noinspection EqualityComparisonWithCoercionJS
310
+ return leftCase == rightCase || leftCase?.key == rightCase?.key;
311
+ }
312
+
313
+ /**
314
+ * Used when creating a static instance of {@link Enumeration}. Generally
315
+ * this is done as follows:
316
+ *
317
+ * @example
318
+ * class Shape extends Enumeration {
319
+ * static {
320
+ * Shape.define('cylinder')
321
+ * Shape.define('cube')
322
+ * Shade.define('other')
323
+ * }
324
+ * }
325
+ *
326
+ * @param {string|number|symbol} key the case name of the this particular
327
+ * enumeration instance.
328
+ * @param {any|[string|number|symbol, any]} value the value of the newly
329
+ * defined {@link Enumeration} instance.
330
+ * @param {function|object} [customizeInstance=undefined] defaults to
331
+ * `undefined`, but when it is passed in as a function, the signature
332
+ * would be to take an instance of this Enumeration class and return
333
+ * one (presumably after modification), or in the form of an object whose
334
+ * property descriptors are copied onto the defined instance. This later
335
+ * approach retains getter and setter application as well as other rare
336
+ * descriptor modifications.
337
+ * @returns {*} an instance of this {@link Enumeration} class type.
338
+ */
339
+ static define(key, value, customizeInstance) {
340
+ if (!is.objectKey(key)) {
341
+ throw new TypeError(
342
+ 'Enumeration.define() must have a string/number/symbol key'
343
+ );
344
+ }
345
+
346
+ let caseName = key
347
+ let caseValue = value
348
+
349
+ if (is.objectEntry(value)) {
350
+ ([caseName, caseValue] = value);
351
+ }
352
+
353
+ let instance = new this(key, value);
354
+
355
+ if (customizeInstance instanceof Function) {
356
+ const newInstance = customizeInstance(instance);
357
+
358
+ if (newInstance instanceof this)
359
+ instance = newInstance;
360
+ }
361
+ else if (is.object(customizeInstance)) {
362
+ const descriptors = Object.getOwnPropertyDescriptors(customizeInstance)
363
+ Object.defineProperties(instance, descriptors)
364
+ }
365
+
366
+ Object.defineProperty(this, key, {
367
+ get() {
368
+ return instance;
369
+ },
370
+ configurable: true,
371
+ enumerable: true,
372
+ });
373
+ }
374
+
375
+ /**
376
+ * Creates an iterator of all {@link Enumeration} derived instances that
377
+ * are statically assigned to this class. Generally this is only useful
378
+ * if applied to child class of `Enumeration`.
379
+ *
380
+ * @returns {Generator<string, void, *>} an iterator that walks instances
381
+ * of derived {@link Enumeration} classes and returns their `.key` values
382
+ */
383
+ static *cases() {
384
+ for (let [key, _] of this) yield key;
385
+ }
386
+
387
+ /**
388
+ * Creates an iterator of all {@link Enumeration} derived instances that
389
+ * are statically assigned to this class. Generally this is only useful
390
+ * if applied to child class of `Enumeration`.
391
+ *
392
+ * @returns {Generator<string, void, *>} an iterator that walks instances
393
+ * of derived {@link Enumeration} classes and returns their `.value` values
394
+ */
395
+ static *values() {
396
+ for (let [_, value] of this) yield value;
397
+ }
398
+
399
+ /**
400
+ * Creates an iterator of all {@link Enumeration} derived instances that
401
+ * are statically assigned to this class. Generally this is only useful
402
+ * if applied to child class of `Enumeration`.
403
+ *
404
+ * @returns {Generator<string, void, *>} an iterator that walks instances
405
+ * of derived {@link Enumeration} classes and returns each key/value pair
406
+ * as arrays. **This is the same as `Object.entries(ChildEnumerationClass)`
407
+ * and then filter the results for pairs whose values are instances of
408
+ * `ChildEnumerationClass`**
409
+ */
410
+ static *[Symbol.iterator]() {
411
+ const keys = Object.keys(this);
412
+
413
+ for (const key of keys) {
414
+ const value = this[key];
415
+
416
+ if (value instanceof this) yield [key, value];
417
+ }
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Creates a prototype safe {@link Proxy} object, specific to
423
+ * {@link Enumeration} instances, that if a getter tries to reach a named
424
+ * property that doesn't exist on the enum, **and** `.hasAssociatedValues`
425
+ * is `true`, then it will attempt to return `this.associations[property]`
426
+ * instead.
427
+ *
428
+ * @param {object} proxied the object to the behavior to; typically this is
429
+ * the prototype of the class itself. See the constructor in Enumeration for
430
+ * usage.
431
+ * @returns {*|null|object} a {@link Proxy} instance around the supplied
432
+ * object to be proxied with the above defined behavior.
433
+ */
434
+ export function SubscriptProxy(proxied) {
435
+ return new Proxy(proxied, {
436
+ get(target, property, receiver) {
437
+ if (!Reflect.has(target, property) && receiver.hasAssociatedValues) {
438
+ return receiver.associated(property);
439
+ }
440
+
441
+ return Reflect.get(target, property, receiver);
442
+ },
443
+
444
+ set(target, property, newValue, receiver) {
445
+ if (
446
+ !Reflect.has(target, property) &&
447
+ receiver.hasAssociatedValues &&
448
+ Reflect.has(receiver.associations, property)
449
+ ) {
450
+ receiver.associations[property] = newValue
451
+ return
452
+ }
453
+
454
+ return Reflect.set(target, property, newValue, receiver);
455
+ },
456
+ });
457
+ }
458
+
459
+ /**
460
+ * A Extension instance wrapping Enumeration
461
+ *
462
+ * @type {Extension}
463
+ */
464
+ export const EnumerationExtension = new Extension(Enumeration)
465
+
466
+ export default Enumeration
@@ -12,6 +12,9 @@ export * from './descriptor.js'
12
12
  import { Enum } from './enum.js'
13
13
  export * from './enum.js'
14
14
 
15
+ import { Enumeration } from './enumeration.js'
16
+ export * from './enumeration.js'
17
+
15
18
  import { Introspector } from './introspector.js'
16
19
  export * from './introspector.js'
17
20
 
@@ -45,6 +48,7 @@ export const NewClassesExtension = new Patch(globalThis, {
45
48
  Deferred,
46
49
  Descriptor,
47
50
  Enum,
51
+ Enumeration,
48
52
  Introspector,
49
53
  Iterable,
50
54
  Iterator,
@@ -56,4 +60,4 @@ export const NewClassesExtension = new Patch(globalThis, {
56
60
  RefSet,
57
61
  Symkeys,
58
62
  Type,
59
- })
63
+ })
package/src/index.js CHANGED
@@ -4,6 +4,7 @@ import { FunctionExtensions, FunctionPrototypeExtensions } from './function.exte
4
4
  import { GlobalFunctionsAndProps } from './global.this.js'
5
5
  import { JSONExtensions } from './json.extensions.js'
6
6
  import { MapExtensions, MapPrototypeExtensions } from './map.extensions.js'
7
+ import { MathExtensions } from './math.extension.js'
7
8
  import { NumberExtensions, NumberPrototypeExtensions } from './number.extension.js'
8
9
  import { ObjectExtensions, ObjectPrototypeExtensions } from './object.extensions.js'
9
10
  import { ReflectExtensions } from './reflect.extensions.js'
@@ -15,6 +16,7 @@ import { SymbolExtensions, SymbolPrototypeExtensions } from './symbol.extensions
15
16
  import { DeferredExtension } from './classes/deferred.js'
16
17
  import { DescriptorExtensions, Descriptor } from './classes/descriptor.js'
17
18
  import { EnumExtension, Enum } from './classes/enum.js'
19
+ import { EnumerationExtension, Enumeration } from './classes/enumeration.js'
18
20
  import { IntrospectorExtensions } from './classes/introspector.js'
19
21
  import { IteratorExtensions, IterableExtensions } from './classes/iterable.js'
20
22
  import { ParamParserExtensions } from './classes/param.parser.js'
@@ -53,6 +55,7 @@ const StaticPatches = [
53
55
  [Function, FunctionExtensions, Function.name],
54
56
  [JSON, JSONExtensions, 'JSON'], // Missing a .name property
55
57
  [Map, MapExtensions, Map.name],
58
+ [Math, MathExtensions, 'Math'],
56
59
  [Number, NumberExtensions, Number.name],
57
60
  [Object, ObjectExtensions, Object.name],
58
61
  [Reflect, ReflectExtensions, 'Reflect'], // Missing a .name property
@@ -88,6 +91,7 @@ const Extensions = {
88
91
  [DeferredExtension.key]: DeferredExtension,
89
92
  [DescriptorExtensions.key]: DescriptorExtensions,
90
93
  [EnumExtension.key]: EnumExtension,
94
+ [EnumerationExtension.key]: EnumerationExtension,
91
95
  [IntrospectorExtensions.key]: IntrospectorExtensions,
92
96
  [IterableExtensions.key]: IterableExtensions,
93
97
  [IteratorExtensions.key]: IteratorExtensions,
@@ -261,4 +265,4 @@ function toFilterFn(filter = ([owner, extension]) => true) {
261
265
  }
262
266
 
263
267
  return filterFn
264
- }
268
+ }
@@ -0,0 +1,73 @@
1
+ import { Patch } from '@nejs/extension'
2
+
3
+ /**
4
+ * `Math` extensions. Building better worlds...actually just
5
+ * providing some parity between Number and BigInt types for
6
+ * now, but later, better worlds.
7
+ *
8
+ * @type {Patch}
9
+ * @example
10
+ * import { MathExtensions } from 'math.extension.js'
11
+ *
12
+ * MathExtensions.apply()
13
+ * // Now the `Math` class has additional methods available
14
+ */
15
+ export const MathExtensions = new Patch(Math, {
16
+ [Patch.kMutablyHidden]: {
17
+ /**
18
+ * The Math.min() static method returns the smallest of the numbers given
19
+ * as input parameters, or Infinity if there are no parameters.
20
+ *
21
+ * @param {bigint|number} values value1, …, valueN – Zero or more numbers
22
+ * among which the lowest value will be selected and returned.
23
+ * @returns {bigint|number|NaN|Infinity} The smallest of the given numbers.
24
+ * Returns NaN if any of the parameters is or is converted into NaN or if
25
+ * the types of numbers are mismatched (i.e., bigint vs. number types).
26
+ * Returns Infinity if no parameters are provided.
27
+ */
28
+ min(...values) {
29
+ const sorter = (l,r) => l < r ? -1 : l > r ? 1 : 0
30
+
31
+ if (!values.length)
32
+ return Infinity
33
+
34
+ if (values.every(n => typeof n === 'bigint')) {
35
+ return values.toSorted(sorter).at(0)
36
+ }
37
+ else if (values.every(n => typeof n === 'number')) {
38
+ return values.toSorted(sorter).at(0)
39
+ }
40
+ else {
41
+ return NaN
42
+ }
43
+ },
44
+
45
+ /**
46
+ * The Math.max() static method returns the largest of the numbers given
47
+ * as input parameters, or Infinity if there are no parameters.
48
+ *
49
+ * @param {bigint|number} values value1, …, valueN – Zero or more numbers
50
+ * among which the largest value will be selected and returned.
51
+ * @returns {bigint|number|NaN|Infinity} The largest of the given numbers.
52
+ * Returns NaN if any of the parameters is or is converted into NaN or if
53
+ * the types of numbers are mismatched (i.e., bigint vs. number types).
54
+ * Returns Infinity if no parameters are provided.
55
+ */
56
+ max(...values) {
57
+ const sorter = (l,r) => l < r ? -1 : l > r ? 1 : 0
58
+
59
+ if (!values.length)
60
+ return Infinity
61
+
62
+ if (values.every(n => typeof n === 'bigint')) {
63
+ return values.toSorted(sorter).at(-1)
64
+ }
65
+ else if (values.every(n => typeof n === 'number')) {
66
+ return values.toSorted(sorter).at(-1)
67
+ }
68
+ else {
69
+ return NaN
70
+ }
71
+ },
72
+ },
73
+ })
@@ -258,6 +258,24 @@ export const NumberPrototypeExtensions = new Patch(Number.prototype, {
258
258
  ifNumber(thenValue, elseValue) {
259
259
  return pIfNumber(this, thenValue, elseValue)
260
260
  },
261
+
262
+ /**
263
+ * Provides a way when dealing with numbers to determine if
264
+ * a given number is within a range of values. By default, if
265
+ * no parameters are supplied, it always returns true since
266
+ * the default range is -Infinity to +Infinity. Additionally,
267
+ * by default, the number will always be less than the supplied
268
+ * max unless inclusive is set to true.
269
+ *
270
+ * @param min the lower range value, defaults to -Infinity
271
+ * @param max the upper range value, defaults to +Infinity
272
+ * @param inclusive defaults to false, set to true if you
273
+ * want the max value to less than and equals to
274
+ * @returns {boolean} true if within the range, false otherwise
275
+ */
276
+ within(min = -Infinity, max = Infinity, inclusive = false) {
277
+ return this >= min && (inclusive ? this <= max : this < max)
278
+ }
261
279
  }
262
280
  })
263
281
 
@@ -149,32 +149,6 @@ export const RegExpExtensions = new Patch(RegExp, {
149
149
  return `(?:${string})?`
150
150
  },
151
151
 
152
- /**
153
- * Escapes special characters in a string for use in a regular expression.
154
- *
155
- * This method checks if the `RegExp.escape` method is available. If it is,
156
- * it uses that method to escape the string. If it's not, it uses a polyfill
157
- * method to escape the string.
158
- *
159
- * The polyfill method replaces all special characters in the string with
160
- * their escaped equivalents. The special characters are defined by the
161
- * regular expression `/[-[\]{}()*+?.,\\^$|#\s]/g`.
162
- *
163
- * @param {string} string - The string to be escaped.
164
- * @returns {string} - The escaped string.
165
- *
166
- * @example
167
- * // Suppose we have a string with special characters
168
- * const str = 'Hello, [World]!'
169
- *
170
- * // Using `escape` or `escapePolyfill`
171
- * console.log(RegExp[RegExp.escape ? 'escapePolyfill' : 'escape'](str))
172
- * // Output: 'Hello\\, \\[World\\]\\!'
173
- */
174
- escape(string) {
175
- return RegExpEscape(string)
176
- },
177
-
178
152
  /**
179
153
  * Getter method that returns a string 'null'.
180
154
  *
@@ -438,14 +412,5 @@ export const RegExpExtensions = new Patch(RegExp, {
438
412
  return `,${this.whitespace}`
439
413
  },
440
414
  }
441
- }, {
442
- conditions: {
443
- ['escape']() { return !Reflect.has(RegExp, 'escape' )},
444
- }
445
415
  })
446
416
 
447
- // Simple variant that does not require external dependencies; may not cover
448
- // all known use cases.
449
- function RegExpEscape(string) {
450
- return string.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
451
- }