@nejs/basic-extensions 1.6.1 → 1.7.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 (139) hide show
  1. package/.esdoc.json +9 -0
  2. package/README.md +2025 -11
  3. package/bin/clean +6 -0
  4. package/dist/@nejs/basic-extensions.bundle.1.6.1.js +4 -0
  5. package/dist/@nejs/basic-extensions.bundle.1.6.1.js.map +7 -0
  6. package/dist/cjs/arrayextensions.js +1 -0
  7. package/dist/cjs/arrayextensions.js.map +1 -0
  8. package/dist/cjs/functionextensions.js +1 -0
  9. package/dist/cjs/functionextensions.js.map +1 -0
  10. package/dist/cjs/globals.js +2 -1
  11. package/dist/cjs/globals.js.map +1 -0
  12. package/dist/cjs/index.d.ts +10 -19
  13. package/dist/cjs/index.js +69 -76
  14. package/dist/cjs/index.js.map +1 -0
  15. package/dist/cjs/mapextensions.d.ts +2 -0
  16. package/dist/cjs/mapextensions.js +30 -0
  17. package/dist/cjs/mapextensions.js.map +1 -0
  18. package/dist/cjs/newClasses/asyncIterable.d.ts +123 -0
  19. package/dist/cjs/{asyncIterable.js → newClasses/asyncIterable.js} +7 -4
  20. package/dist/cjs/newClasses/asyncIterable.js.map +1 -0
  21. package/dist/cjs/newClasses/descriptor.d.ts +401 -0
  22. package/dist/cjs/{descriptor.js → newClasses/descriptor.js} +150 -80
  23. package/dist/cjs/newClasses/descriptor.js.map +1 -0
  24. package/dist/cjs/newClasses/iterable.d.ts +125 -0
  25. package/dist/cjs/{iterable.js → newClasses/iterable.js} +36 -10
  26. package/dist/cjs/newClasses/iterable.js.map +1 -0
  27. package/dist/cjs/newClasses/refmap.d.ts +238 -0
  28. package/dist/cjs/newClasses/refmap.js +433 -0
  29. package/dist/cjs/newClasses/refmap.js.map +1 -0
  30. package/dist/cjs/newClasses/refset.d.ts +186 -0
  31. package/dist/cjs/{refset.js → newClasses/refset.js} +4 -2
  32. package/dist/cjs/newClasses/refset.js.map +1 -0
  33. package/dist/cjs/objectextensions.d.ts +7 -6
  34. package/dist/cjs/objectextensions.js +85 -43
  35. package/dist/cjs/objectextensions.js.map +1 -0
  36. package/dist/cjs/reflectextensions.js +16 -12
  37. package/dist/cjs/reflectextensions.js.map +1 -0
  38. package/dist/cjs/stringextensions.js +1 -0
  39. package/dist/cjs/stringextensions.js.map +1 -0
  40. package/dist/cjs/symbolextensions.js +3 -1
  41. package/dist/cjs/symbolextensions.js.map +1 -0
  42. package/dist/cjs/weakrefextensions.js +1 -0
  43. package/dist/cjs/weakrefextensions.js.map +1 -0
  44. package/dist/mjs/arrayextensions.js +1 -0
  45. package/dist/mjs/arrayextensions.js.map +1 -0
  46. package/dist/mjs/functionextensions.js +1 -0
  47. package/dist/mjs/functionextensions.js.map +1 -0
  48. package/dist/mjs/globals.js +2 -1
  49. package/dist/mjs/globals.js.map +1 -0
  50. package/dist/mjs/index.d.ts +10 -19
  51. package/dist/mjs/index.js +67 -60
  52. package/dist/mjs/index.js.map +1 -0
  53. package/dist/mjs/mapextensions.d.ts +2 -0
  54. package/dist/mjs/mapextensions.js +27 -0
  55. package/dist/mjs/mapextensions.js.map +1 -0
  56. package/dist/mjs/newClasses/asyncIterable.d.ts +123 -0
  57. package/dist/mjs/{asyncIterable.js → newClasses/asyncIterable.js} +106 -105
  58. package/dist/mjs/newClasses/asyncIterable.js.map +1 -0
  59. package/dist/mjs/newClasses/descriptor.d.ts +401 -0
  60. package/dist/mjs/{descriptor.js → newClasses/descriptor.js} +129 -67
  61. package/dist/mjs/newClasses/descriptor.js.map +1 -0
  62. package/dist/mjs/newClasses/iterable.d.ts +125 -0
  63. package/dist/mjs/newClasses/iterable.js +199 -0
  64. package/dist/mjs/newClasses/iterable.js.map +1 -0
  65. package/dist/mjs/newClasses/refmap.d.ts +238 -0
  66. package/dist/mjs/newClasses/refmap.js +417 -0
  67. package/dist/mjs/newClasses/refmap.js.map +1 -0
  68. package/dist/mjs/newClasses/refset.d.ts +186 -0
  69. package/dist/mjs/{refset.js → newClasses/refset.js} +3 -2
  70. package/dist/mjs/newClasses/refset.js.map +1 -0
  71. package/dist/mjs/objectextensions.d.ts +7 -6
  72. package/dist/mjs/objectextensions.js +84 -42
  73. package/dist/mjs/objectextensions.js.map +1 -0
  74. package/dist/mjs/reflectextensions.js +16 -12
  75. package/dist/mjs/reflectextensions.js.map +1 -0
  76. package/dist/mjs/stringextensions.js +1 -0
  77. package/dist/mjs/stringextensions.js.map +1 -0
  78. package/dist/mjs/symbolextensions.js +3 -1
  79. package/dist/mjs/symbolextensions.js.map +1 -0
  80. package/dist/mjs/weakrefextensions.js +1 -0
  81. package/dist/mjs/weakrefextensions.js.map +1 -0
  82. package/docs/assets/anchor.js +350 -0
  83. package/docs/assets/bass-addons.css +12 -0
  84. package/docs/assets/bass.css +544 -0
  85. package/docs/assets/fonts/EOT/SourceCodePro-Bold.eot +0 -0
  86. package/docs/assets/fonts/EOT/SourceCodePro-Regular.eot +0 -0
  87. package/docs/assets/fonts/LICENSE.txt +93 -0
  88. package/docs/assets/fonts/OTF/SourceCodePro-Bold.otf +0 -0
  89. package/docs/assets/fonts/OTF/SourceCodePro-Regular.otf +0 -0
  90. package/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf +0 -0
  91. package/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf +0 -0
  92. package/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff +0 -0
  93. package/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff +0 -0
  94. package/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff +0 -0
  95. package/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff +0 -0
  96. package/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 +0 -0
  97. package/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 +0 -0
  98. package/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 +0 -0
  99. package/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 +0 -0
  100. package/docs/assets/fonts/source-code-pro.css +23 -0
  101. package/docs/assets/github.css +123 -0
  102. package/docs/assets/site.js +168 -0
  103. package/docs/assets/split.css +15 -0
  104. package/docs/assets/split.js +782 -0
  105. package/docs/assets/style.css +147 -0
  106. package/docs/index.html +13060 -0
  107. package/jsdoc-config.json +31 -0
  108. package/package.json +12 -3
  109. package/src/globals.js +1 -1
  110. package/src/index.js +75 -82
  111. package/src/mapextensions.js +30 -0
  112. package/src/{asyncIterable.js → newClasses/asyncIterable.js} +117 -117
  113. package/src/{descriptor.js → newClasses/descriptor.js} +137 -74
  114. package/src/newClasses/iterable.js +221 -0
  115. package/src/newClasses/refmap.js +483 -0
  116. package/src/{refset.js → newClasses/refset.js} +2 -2
  117. package/src/objectextensions.js +97 -46
  118. package/src/reflectextensions.js +16 -14
  119. package/src/symbolextensions.js +2 -1
  120. package/tests/index.test.js +1 -1
  121. package/tests/{asyncIterable.test.js → newClasses/asyncIterable.test.js} +3 -4
  122. package/tests/newClasses/descriptor.test.js +252 -0
  123. package/tests/{iterable.test.js → newClasses/iterable.test.js} +2 -4
  124. package/tests/newClasses/refmap.test.js +69 -0
  125. package/tests/{refset.test.js → newClasses/refset.test.js} +2 -4
  126. package/tests/objectextensions.test.js +128 -0
  127. package/tsconfig.base.json +2 -1
  128. package/dist/@nejs/basic-extensions.bundle.1.6.0.js +0 -2
  129. package/dist/@nejs/basic-extensions.bundle.1.6.0.js.map +0 -7
  130. package/dist/cjs/asyncIterable.d.ts +0 -3
  131. package/dist/cjs/descriptor.d.ts +0 -2
  132. package/dist/cjs/iterable.d.ts +0 -3
  133. package/dist/cjs/refset.d.ts +0 -2
  134. package/dist/mjs/asyncIterable.d.ts +0 -3
  135. package/dist/mjs/descriptor.d.ts +0 -2
  136. package/dist/mjs/iterable.d.ts +0 -3
  137. package/dist/mjs/iterable.js +0 -184
  138. package/dist/mjs/refset.d.ts +0 -2
  139. package/src/iterable.js +0 -203
@@ -0,0 +1,483 @@
1
+ import { Extension } from '@nejs/extension'
2
+ import { ObjectExtensions } from '../objectextensions.js'
3
+ import { SymbolExtensions } from '../symbolextensions.js'
4
+ import { WeakRefExtensions } from '../weakrefextensions.js'
5
+ import { Iterable, Iterator } from './iterable.js'
6
+
7
+ const { isObject, isNullDefined, isValidKey } = ObjectExtensions.patches
8
+ const { isRegistered } = SymbolExtensions.patches
9
+ const { isValidReference } = WeakRefExtensions.patches
10
+
11
+ /**
12
+ * RefMap class extends the standard Map object to manage a collection of
13
+ * WeakRef values mapped to strong keys. It provides additional functionality
14
+ * such as objectification of values and various utility methods.
15
+ *
16
+ * Unlike standard Maps or Objects, RefMap stores weak references to objects,
17
+ * allowing them to be garbage-collected if there are no other references to
18
+ * them. This behavior is different from Maps and standard Objects, which
19
+ * maintain strong references to their elements.
20
+ *
21
+ * @extends Map
22
+ */
23
+ export class RefMap extends Map {
24
+ /**
25
+ * Private field to track whether the RefMap should objectify primitive
26
+ * values.
27
+ *
28
+ * @private
29
+ */
30
+ #objectifyValues = false
31
+
32
+ constructor(...args) {
33
+ super(...args)
34
+ }
35
+
36
+ /**
37
+ * Method to control whether the RefMap should objectify its values. When
38
+ * objectifying, primitive values (number, string, boolean, bigint) are
39
+ * converted to their respective object types, which allows them to be used as
40
+ * WeakRef targets.
41
+ *
42
+ * @param {boolean} setObjectification - Flag to enable or disable
43
+ * objectification.
44
+ * @returns {RefMap} - The current RefMap instance to allow method chaining.
45
+ */
46
+ objectifying(setObjectification = true) {
47
+ this.objectifyValues = setObjectification
48
+ return this
49
+ }
50
+
51
+ /**
52
+ * The function converts a JavaScript Map object into a regular JavaScript
53
+ * object, handling invalid keys by converting them to strings.
54
+ *
55
+ * @returns {object} an object; keys that are not either a `String` or a
56
+ * `Symbol` will be converted to a string.
57
+ */
58
+ asObject() {
59
+ const object = {}
60
+
61
+ for (const [key, value] of this) {
62
+ const useKey = isValidKey(key) ? key : String(key)
63
+ const useValue = value?.valueOf() || value
64
+
65
+ object[useKey] = useValue
66
+ }
67
+
68
+ return object
69
+ }
70
+
71
+ /**
72
+ * Returns the state indicating whether or not `RefMap` will attempt to
73
+ * convert non-valid primitives into targets that are valid input for
74
+ * new `WeakRef` object instances. If this value is `false` then no
75
+ * *objectification* will occur.
76
+ *
77
+ * @returns {boolean} The current state of objectifyValues.
78
+ */
79
+ get objectifyValues() {
80
+ return this.#objectifyValues
81
+ }
82
+
83
+
84
+ /**
85
+ * The `get` function retrieves a value from a map and returns it, or returns a
86
+ * default value if the value is null or undefined. The actual retrieved value
87
+ * is a dereferenced `WeakRef`. If the result is `undefined` and this is not the
88
+ * expected response, it is likely the value has been garbage collected.
89
+ *
90
+ * @param {any} key - The key parameter is the key of the value you want to
91
+ * retrieve from the data structure.
92
+ * @param {any} defaultValue - The `defaultValue` parameter is the value that
93
+ * will be returned if the key does not exist in the map or if the value
94
+ * associated with the key has been garbage collected (i.e., it no longer
95
+ * exists).
96
+ * @returns The method is returning the value associated with the given key.
97
+ * If the value is not found or if it has been garbage collected (deref()
98
+ * returns null), then the defaultValue is returned.
99
+ */
100
+ get(key, defaultValue) {
101
+ const value = super.get(key)
102
+
103
+ if (!value || !value?.deref()) {
104
+ return defaultValue
105
+ }
106
+
107
+ return value?.deref()
108
+ }
109
+
110
+ /**
111
+ * Setting this value to true, will cause all set values to the Map to
112
+ * be analyzed for validity as a candidate to be wrapped in a `WeakRef`
113
+ * object. If true, and if possible, the object will be turned into an
114
+ * `Object` variant first.
115
+ *
116
+ * @param {boolean} value - The new state to set for objectifyValues.
117
+ */
118
+ set objectifyValues(value) {
119
+ this.#objectifyValues = !!value
120
+ }
121
+
122
+ /**
123
+ * Overrides the set method of `Map`. Adds a value to the `RefMap`,
124
+ * converting it to a `WeakRef`. Throws an error if the value is not a
125
+ * valid `WeakRef` target (e.g., `null`, `undefined`, or a registered
126
+ * `symbol`). If {@link objectifyValues} is enabled, an attempt to convert
127
+ * primitives to their object variants will be made. These are `numbers`,
128
+ * `strings`, `boolean` values and `bigint`s.
129
+ *
130
+ * @param {*} key - The `key` to be set on the `RefMap`
131
+ * @param {*} value - The value to be associated with the `key`
132
+ * @throws {TypeError} If the value is not a valid WeakRef target.
133
+ */
134
+ set(key, value) {
135
+ let useValue = value
136
+
137
+ // Objectify the value if needed
138
+ if (this.#objectifyValues && (
139
+ typeof useValue === 'number' ||
140
+ typeof useValue === 'string' ||
141
+ typeof useValue === 'boolean' ||
142
+ typeof useValue === 'bigint'
143
+ )) {
144
+ useValue = Object(useValue);
145
+ }
146
+
147
+ // Check if the value is an object, and if it's a symbol, ensure it's not registered
148
+ if (typeof useValue === 'symbol' && Symbol.keyFor(useValue) !== undefined) {
149
+ throw new TypeError('RefMap cannot accept registered symbols as values');
150
+ }
151
+
152
+ if (typeof useValue !== 'object' && typeof useValue !== 'symbol') {
153
+ throw new TypeError(
154
+ 'RefMap values must be objects, non-registered symbols, or objectified primitives'
155
+ );
156
+ }
157
+
158
+ // If the value is null or undefined, throw an error
159
+ if (useValue === null || useValue === undefined) {
160
+ throw new TypeError('RefMap values cannot be null or undefined');
161
+ }
162
+
163
+ const ref = new WeakRef(useValue)
164
+
165
+ super.set(key, ref)
166
+ }
167
+
168
+ /**
169
+ * Sets multiple values at a single time. The format is an array of array
170
+ * or rather an array of {@link Object.entries} (for example,
171
+ * `[[key1,value1], [key2,value2]]`). For each entry pair, if the length
172
+ * is not 2, either missing a key or value, it will be skipped.
173
+ *
174
+ * @param {Iterable} values - An iterable of values to add to the RefMap.
175
+ * @throws {TypeError} If the supplied values are falsey or non-iterable.
176
+ * @returns {RepMap} returns `this` to allow for chaining
177
+ */
178
+ setAll(entries) {
179
+ if (!Iterable.isIterable(entries)) {
180
+ throw new TypeError(
181
+ 'The supplied list of entries must be an array of arrays in the ' +
182
+ 'format [[key1, value1], [key2, value2], ...].'
183
+ )
184
+ }
185
+
186
+ const forEach = entry => {
187
+ const [key, value] = entry
188
+
189
+ if (!key || !isObject(value) || !isRegistered(value)) {
190
+ return
191
+ }
192
+
193
+ this.set(key, value)
194
+ }
195
+
196
+ for (const entry of entries) {
197
+ forEach(entry)
198
+ }
199
+
200
+ return this
201
+ }
202
+
203
+ /**
204
+ * Removes all elements from the RefMap that have been garbage collected
205
+ * (i.e., their WeakRef no longer points to an object).
206
+ *
207
+ * @returns {RefMap} - The current RefMap instance to allow method chaining.
208
+ */
209
+ clean() {
210
+ for (const [key, dereferenced] of this) {
211
+ if (!dereferenced) {
212
+ this.delete(key)
213
+ }
214
+ }
215
+
216
+ return this
217
+ }
218
+
219
+ /**
220
+ * Executes a provided function once for each value in the RefMap. The callback
221
+ * function receives the dereferenced value, the value again (as RefMap doesn't
222
+ * use keys), and the RefMap itself. This method provides a way to iterate over
223
+ * and apply operations to the values stored in the RefMap, taking into account
224
+ * that they are weak references and may have been garbage-collected.
225
+ *
226
+ * @param {Function} forEachFn - Function to execute for each element. It
227
+ * takes three arguments: element, element (again, as RefMap has no key), and
228
+ * the RefMap itself.
229
+ * @param {*} thisArg - Value to use as `this` when executing `forEachFn`.
230
+ */
231
+ entries() {
232
+ const entriesIterator = super.entries()
233
+ const refIterator = new Iterator(entriesIterator, (entry) => {
234
+ if (entry) {
235
+ const [key, ref] = entry
236
+ const value = ref?.deref()
237
+
238
+ return [key, value]
239
+ }
240
+
241
+ return entry
242
+ })
243
+
244
+ return refIterator
245
+ }
246
+
247
+ /**
248
+ * Iterate over the items in the map and pass them to the supplied
249
+ * function ala `Map.prototype.forEach`. Note however, there are no
250
+ * indexes on Maps and as such, the index parameter of the callback
251
+ * will always be the value's key. Subsequently the `array` or third
252
+ * parameter will receive the map instance rather than an array.
253
+ *
254
+ * @param {function} forEachFn the function to use for each element in
255
+ * the set.
256
+ * @param {object} thisArg the `this` argument to be applied to each
257
+ * invocation of the `forEachFn` callback. Note, this value is unable
258
+ * to be applied if the `forEachFn` is a big arrow function
259
+ */
260
+ forEach(forEachFn, thisArg) {
261
+ for (const [key, ref] of super.entries()) {
262
+ const value = ref?.deref()
263
+
264
+ if (!value) {
265
+ continue
266
+ }
267
+
268
+ forEachFn.call(thisArg, value, key, this)
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Returns an iterator for the values in the RefMap. Each value is
274
+ * dereferenced from its WeakRef before being returned. This method allows
275
+ * iterating over he set's values, similar to how one would iterate over
276
+ * values in a standard Map or Array, but with the understanding that the
277
+ * values are weakly referenced and may no longer exist (in which case
278
+ * they are skipped).
279
+ *
280
+ * @returns {Iterator} An iterator for the values.
281
+ */
282
+ values() {
283
+ return new Iterator(super.values(), function perItem(value) {
284
+ const dereferenced = value?.deref()
285
+ return dereferenced || value
286
+ })
287
+ }
288
+
289
+ /**
290
+ * Determines whether an element with the specified value exists in the
291
+ * `RefMap`. For non-objectified sets, this method checks if the dereferenced
292
+ * values of the map include the specified value.
293
+ *
294
+ * For objectified sets, strict is set to false which uses loose
295
+ * equality to allow for things like `Object(5)` to equal `5`. This is important
296
+ * because otherwise primitives could not be weakly referenced. In the grand
297
+ * scheme of things, this is only useful if the objectified value is the
298
+ * one being referenced.
299
+ *
300
+ * @param {*} value - The value to check for presence in the RefMap.
301
+ * @param {boolean} strict - if `true`, the default, then the supplied value
302
+ * is hard compared to the dereferenced value (`===`). If `false`, then a
303
+ * loose comparison is used (`==`)
304
+ * @returns {boolean} - True if an element with the specified value exists
305
+ * in the RefMap, false otherwise.
306
+ */
307
+ hasValue(value, strict = true) {
308
+ if (isNullDefined(value)) {
309
+ return false
310
+ }
311
+
312
+ if (this.#objectifyValues) {
313
+ strict = false
314
+ }
315
+
316
+ for (const [_, dereferenced] of this) {
317
+ if (
318
+ (strict && value === dereferenced) ||
319
+ (!strict && value == dereferenced)
320
+ ) {
321
+ return true
322
+ }
323
+ }
324
+
325
+ return false
326
+ }
327
+
328
+ /**
329
+ * The `filter` function filters the entries of a `RefMap` object based on
330
+ * a given filter function. The dereferenced entries of the values of the map
331
+ * will be passed to the function rather than a `WeakRef` itself.
332
+ *
333
+ * A new resulting entry set will be generated and a new `RefMap` will be made
334
+ * from these entries and returned. Note that this function never returns
335
+ * `null`
336
+ *
337
+ * @param {function} filterFn - The `filterFn` parameter is a function that
338
+ * will be used to filter the entries in the `RefMap`. It will be called with
339
+ * three arguments: the value of the current entry, the key of the current
340
+ * entry, and the `RefMap` itself. The function should return `true`
341
+ * @param {object} thisArg - The `thisArg` parameter is an optional argument
342
+ * that specifies the value to be used as `this` when executing the
343
+ * `filterFn` function. It allows you to explicitly set the context in which
344
+ * the `filterFn` function is called. If `thisArg` is not provided, `this
345
+ * @returns {array} The `filter` method is returning an array of filtered map
346
+ * entries
347
+ */
348
+ filter(filterFn, thisArg) {
349
+ const resultingEntries = []
350
+
351
+ for (const [key, dereferenced] of this) {
352
+ if (filterFn.call(thisArg, dereferenced, key, this)) {
353
+ resultingEntries.push([key, dereferenced])
354
+ }
355
+ }
356
+
357
+ return resultingEntries
358
+ }
359
+
360
+ /**
361
+ * The `find` function iterates over a map and calls a given function on
362
+ * each value, returning the first value for which the function returns
363
+ * a truthy value.
364
+ *
365
+ * The function signature of `findFn` is
366
+ * ```
367
+ * function findFn(value, key, map)
368
+ * ```
369
+ * 'value' is passed to findFn up to two times; first with the `WeakRef`
370
+ * value, second with the result of {@link WeakRef.deref}. If `findFn`
371
+ * returns true for either of these the dereferenced value will be
372
+ * returned from the call to {@link RefMap.find}.
373
+ * `key` represents the key object that the value is mapped to.
374
+ * `map` is simply a reference to `this` map.
375
+ *
376
+ * @param findFn - `findFn` is a function that will be called for each
377
+ * element in the map. It takes three arguments: `ref`, `key`, and `map`;
378
+ * where `ref` is the value of the current element in the map, `key` is
379
+ * the key of the current element, and `map` is a reference to the instance
380
+ * being searched.
381
+ * @param thisArg - The `thisArg` parameter is the value to be used as
382
+ * the `this` value when executing the `findFn` function. It allows you
383
+ * to specify the context in which the `findFn` function should be called.
384
+ * @returns the first dereferenced value that satisfies the condition
385
+ * specified by the `findFn` function. If no value satisfies the condition,
386
+ * it returns `null`.
387
+ */
388
+ find(findFn, thisArg) {
389
+ for (const [key, dereferenced] of this) {
390
+ const ref = super.get(key)
391
+ let result = findFn.call(thisArg, ref, key, map)
392
+
393
+ if (!result) {
394
+ result = findFn.call(thisArg, dereferenced, key, map)
395
+ }
396
+
397
+ if (result) {
398
+ return dereferenced
399
+ }
400
+ }
401
+
402
+ return null
403
+ }
404
+
405
+ /**
406
+ * Creates a new array or `RefMap` with the results of calling a provided
407
+ * function on every element in the calling `RefMap`. This method dereferences
408
+ * each value, applies the `mapFn`, and collects the results. If `toRefMap` is
409
+ * `true`, a new `RefMap` is returned; otherwise, an array. This method
410
+ * differs from `Array.map` in handling weak references and the potential to
411
+ * return a new `RefMap` instead of an array.
412
+ *
413
+ * @param {Function} mapFn - Function that produces an element of the new
414
+ * array or `RefMap`, taking three arguments.
415
+ * @param {*} thisArg - Value to use as this when executing mapFn.
416
+ * @param {boolean} toRefMap - Determines if the output should be a new
417
+ * `RefMap` (`true`) or an array (`false`).
418
+ * @param {boolean} mirrorObjectification - If `true` and `toRefMap` is
419
+ * `true`, the new `RefMap` mirrors the objectification setting of the
420
+ * original.
421
+ * @returns {Array|RefMap} - A new array or `RefMap` with each element being
422
+ * the result of the `mapFn`.
423
+ */
424
+ map(mapFn, thisArg, toRefMap, mirrorObjectification) {
425
+ if (typeof mapFn !== 'function') {
426
+ throw new TypeError('mapFn must be a function! Received', mapFn)
427
+ }
428
+
429
+ const entries = []
430
+ const errors = []
431
+
432
+ let needsObjectification = mirrorObjectification && this.objectifyValues
433
+ let detectNeed = mirrorObjectification === undefined
434
+ let objectify = needsObjectification
435
+
436
+ for (const [key, dereferenced] of this) {
437
+ const [, VALUE] = [0,1]
438
+ const transformed = mapFn.call(thisArg, [key, dereferenced], key, this)
439
+
440
+ if (!isValidReference(transformed[VALUE])) {
441
+ if (isValidReference(Object(transformed[VALUE]))) {
442
+ needsObjectification = true
443
+ if (detectNeed && !objectify) {
444
+ objectify = true
445
+ transformed[VALUE] = Object(transformed[VALUE])
446
+ }
447
+ }
448
+ }
449
+
450
+ entries.push(transformed)
451
+ }
452
+
453
+ if (toRefMap) {
454
+ return new RefMap(entries).objectifying(objectify)
455
+ }
456
+
457
+ return entries
458
+ }
459
+
460
+ /**
461
+ * The function returns an iterator that iterates over the entries of an object,
462
+ * dereferencing any weak references.
463
+ *
464
+ * @returns {Iterator} A new iterator object is being returned.
465
+ */
466
+ *[Symbol.iterator]() {
467
+ for (const [key, ref] of this.entries()) {
468
+ yield [key, ref]
469
+ }
470
+ }
471
+
472
+ /**
473
+ * Ensures that the constructor of this object instance's name
474
+ * is returned if the string tag for this instance is queried
475
+ *
476
+ * @returns {string} the name of the class
477
+ */
478
+ get [Symbol.toStringTag]() {
479
+ return this.constructor.name
480
+ }
481
+ }
482
+
483
+ export const RefMapExtensions = new Extension(RefMap)
@@ -12,7 +12,7 @@ import { Extension } from '@nejs/extension'
12
12
  *
13
13
  * @extends Set
14
14
  */
15
- class RefSet extends Set {
15
+ export class RefSet extends Set {
16
16
  /**
17
17
  * Private field to track whether the RefSet should objectify primitive
18
18
  * values.
@@ -314,7 +314,7 @@ class RefSet extends Set {
314
314
  * @param {*} thisArg - Value to use as this when executing findFn.
315
315
  * @returns {*} - The value of the first element in the RefSet that satisfies
316
316
  * the testing function, or undefined if none found.
317
- * @returns
317
+ * @returns {any} the dereferenced value if found, or undefined otherwise
318
318
  */
319
319
  find(findFn, thisArg) {
320
320
  for (const value of this) {