@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
@@ -1,15 +1,27 @@
1
1
  import { Extension } from '@nejs/extension'
2
- import { ObjectExtensions } from './objectextensions.js'
3
- import { StringExtensions } from './stringextensions.js'
4
- import { ReflectExtensions } from './reflectextensions.js'
2
+ import { ObjectExtensions } from '../objectextensions.js'
3
+ import { ReflectExtensions } from '../reflectextensions.js'
5
4
 
6
- const isObject = ObjectExtensions.patchEntries?.isObject?.computed
7
- const isValidKey = ObjectExtensions.patchEntries?.isValidKey?.computed
8
- const isString = StringExtensions.patchEntries?.isString?.computed
9
- const hasSome = ReflectExtensions.patchEntries?.hasSome?.computed
5
+ const { isObject, isValidKey } = ObjectExtensions.patches
6
+ const { hasSome } = ReflectExtensions.patches
10
7
 
11
- class Descriptor {
12
- #desc = Descriptor.enigmatic
8
+ export class Descriptor {
9
+ /**
10
+ * The default private descriptor value is that of `enigmatic`
11
+ *
12
+ * @private
13
+ * @type {object}
14
+ */
15
+ #desc = undefined
16
+
17
+ /**
18
+ * An optionally associated object, usually the parent from which
19
+ * the descriptor was taken, or undefined if none was able to be
20
+ * derived.
21
+ *
22
+ * @type {object}
23
+ */
24
+ #object = undefined
13
25
 
14
26
  /**
15
27
  * Creates a new instance of Descriptor either from another object or
@@ -21,13 +33,21 @@ class Descriptor {
21
33
  * on the aforesupplied object.
22
34
  */
23
35
  constructor(object, key) {
24
- this.#desc = object
36
+ if ((object ?? key) === undefined) {
37
+ this.#desc = Descriptor.enigmatic
38
+ }
25
39
 
26
- if (isObject(object) && isValidKey(key)) {
40
+ if (Descriptor.isDescriptor(object)) {
41
+ this.#desc = object
42
+ this.#object = undefined
43
+ }
44
+ else if (isObject(object) && isValidKey(key)) {
27
45
  this.#desc = Object.getOwnPropertyDescriptor(object, key)
46
+ this.#object = object
28
47
  }
29
48
 
30
49
  if (!this.isDescriptor) {
50
+ console.error('Current descriptor:', this.#desc)
31
51
  throw new Error(`Not a valid descriptor:`, this.#desc)
32
52
  }
33
53
  }
@@ -160,6 +180,18 @@ class Descriptor {
160
180
  return this.#desc?.get
161
181
  }
162
182
 
183
+ /**
184
+ * Retrieves the {@link get} function for this accessor and binds it to
185
+ * the object from which the descriptor was derived, if that value is set.
186
+ * Otherwise this method is identical to the {@link get} accessor.
187
+ *
188
+ * @returns {function} the getter if one is defined. If possible this
189
+ * getter will be bound the associated and previously set `object`.
190
+ */
191
+ get boundGet() {
192
+ return (isObject(this.#object) ? this.get?.bind(this.#object) : this.get)
193
+ }
194
+
163
195
  /**
164
196
  * Sets the `get` value of this object. If the internal descriptor
165
197
  * store is invalid, the value is thrown away
@@ -179,7 +211,19 @@ class Descriptor {
179
211
  * is undefined.
180
212
  */
181
213
  get set() {
182
- return this.#desc?.writable
214
+ return (this.#desc || {}).set
215
+ }
216
+
217
+ /**
218
+ * Retrieves the {@link set} function for this accessor and binds it to
219
+ * the object from which the descriptor was derived, if that value is set.
220
+ * Otherwise this method is identical to the {@link set} accessor.
221
+ *
222
+ * @returns {function} the setter if one is defined. If possible this
223
+ * setter will be bound the associated and previously set `object`.
224
+ */
225
+ get boundSet() {
226
+ return (isObject(this.#object) ? this.set?.bind(this.#object) : this.set)
183
227
  }
184
228
 
185
229
  /**
@@ -192,6 +236,52 @@ class Descriptor {
192
236
  (this.#desc || {}).set = value
193
237
  }
194
238
 
239
+ /**
240
+ * The function checks the descriptor's associated object has been set on this
241
+ * instance of `Descriptor`.
242
+ *
243
+ * @returns {boolean} `true` if the `object` property has been set,
244
+ * `false` otherwise
245
+ */
246
+ get hasObject() { return isObject(this.#object) }
247
+
248
+ /**
249
+ * Returns the descriptor's associated `object` value. This is usually the
250
+ * parent object from which the descriptor was derived. If the value is preset
251
+ * it will be returned. Undefined will be returned otherwise
252
+ *
253
+ * @returns {object} the associated object for this descriptor or undefined
254
+ * if it has not yet been set.
255
+ */
256
+ get object() { return this.#object }
257
+
258
+ /**
259
+ * Sets the descriptor's associated `object` value. This is usually the
260
+ * parent object from which the descriptor was derived.
261
+ *
262
+ * @param {object} value sets the object for which this descriptor is to
263
+ * be associated with.
264
+ */
265
+ set object(value) { this.#object = Object(value) }
266
+
267
+ /**
268
+ * The function returns a string representation of a descriptor object with
269
+ * additional information about its type when used in the NodeJS repl.
270
+ *
271
+ * @param {number} depth - The `depth` parameter is used to specify the
272
+ * maximum depth to which nested objects and arrays will be formatted. If
273
+ * the depth is exceeded, the output will be truncated with ellipses.
274
+ * @param {object} options - The `options` parameter is an object that
275
+ * contains various configuration options for the `inspect` function.
276
+ * These options can be used to customize the output of the inspection.
277
+ * @param {function} inspect - The `inspect` parameter is a function that
278
+ * is used to convert an object into a string representation. It is
279
+ * typically used for debugging purposes or when displaying an object's
280
+ * properties.
281
+ * @returns a string that represents a descriptor. The string includes the
282
+ * type of the descriptor (either "Accessor" or "Data") and the result of
283
+ * inspecting the descriptor object using the provided options and depth.
284
+ */
195
285
  [Symbol.for('nodejs.util.inspect.custom')](depth, options, inspect) {
196
286
  const type = this.isAccessor ? ' (Accessor)' : this.isData ? ' (Data)' : ''
197
287
  return `Descriptor${type} ${inspect(this.#desc, {...options, depth})}`
@@ -205,12 +295,15 @@ class Descriptor {
205
295
  * object to return a descriptor for.
206
296
  * @returns an object descriptor for the requested field or null
207
297
  */
208
- static for(object, key) {
209
- if (!isObject(object) && !isValidKey(key)) {
298
+ static for(object, key, wrap = false) {
299
+ if (!isObject(object) || !isValidKey(key) || !Reflect.has(object, key)) {
210
300
  return null
211
301
  }
212
302
 
213
- return Object.getOwnPropertyDescriptor(object, key)
303
+ return (wrap
304
+ ? new Descriptor(Object.getOwnPropertyDescriptor(object, key))
305
+ : Object.getOwnPropertyDescriptor(object, key)
306
+ )
214
307
  }
215
308
 
216
309
  /**
@@ -277,29 +370,25 @@ class Descriptor {
277
370
  * The function `getData` retrieves the value of a property from an object
278
371
  * if it exists and is a data property.
279
372
  *
280
- * @param object - The "object" parameter is the object from which we want to
281
- * retrieve data.
282
- * @param property - The `property` parameter is the name of the property that
283
- * you want to retrieve the data from.
373
+ * @param {object} object - The "object" parameter is the object from which
374
+ * we want to retrieve data.
375
+ * @param {string|symbol} property - The `property` parameter is the name of
376
+ * the property that you want to retrieve the data from.
284
377
  * @returns either the value of the specified property if it exists and is
285
378
  * a data property, or undefined if the property does not exist or is not
286
379
  * a data property.
287
380
  */
288
381
  static getData(object, property) {
289
- if (!isObject(object) || !isString(property)) {
290
- return null;
382
+ if (!isObject(object) || !Reflect.has(object, property)) {
383
+ return undefined;
291
384
  }
292
385
 
293
- const descriptors = Descriptor.all(object)
294
- if (descriptors.has(property)) {
295
- const descriptor = descriptors.get(property)
296
-
297
- if (Descriptor.isData(descriptor)) {
298
- return descriptor.value
299
- }
386
+ const descriptor = Descriptor.for(object, property, true)
387
+ if (!descriptor.isData) {
388
+ return null
300
389
  }
301
390
 
302
- return undefined
391
+ return descriptor.value
303
392
  }
304
393
 
305
394
  /**
@@ -318,41 +407,16 @@ class Descriptor {
318
407
  * returns undefined.
319
408
  */
320
409
  static getAccessor(object, property) {
321
- if (!isObject(object))
322
- return null
323
-
324
- const [GETTER, SETTER, OBJECT] = [0, 1, 2]
325
- const results = [undefined, undefined, undefined]
326
- const descriptors = this.all(object)
327
- const isDescriptor = Descriptor.isDescriptor(object)
328
-
329
- if (descriptors.has(property) || isDescriptor) {
330
- const descriptor = isDescriptor ? object : descriptors.get(property)
331
-
332
- if (Descriptor.isAccessor(descriptor)) {
333
- results[OBJECT] = descriptors.object(property)
334
- results[GETTER] = descriptor?.get
335
- results[SETTER] = descriptor?.set
336
-
337
- Object.assign(results, {
338
- get() { this[GETTER].bind(this[OBJECT])() },
339
- set(value) { this[SETTER].bind(this[OBJECT])(value) },
340
- get accessor() { return true },
341
- get descriptor() { return descriptor },
342
- get boundDescriptor() {
343
- return {
344
- ...descriptor,
345
- get: descriptor.get?.bind(object),
346
- set: descriptor.set?.bind(object),
347
- }
348
- }
349
- })
350
-
351
- return results
352
- }
410
+ if (!isObject(object) || !Reflect.has(object, property)) {
411
+ return undefined;
412
+ }
413
+
414
+ const descriptor = Descriptor.for(object, property, true)
415
+ if (!descriptor.isAccessor) {
416
+ return null
353
417
  }
354
418
 
355
- return undefined
419
+ return descriptor.get.bind(object)()
356
420
  }
357
421
 
358
422
  /**
@@ -433,8 +497,11 @@ class Descriptor {
433
497
  }
434
498
 
435
499
  /**
436
- * The function checks if an object is a valid object descriptor in
437
- * JavaScript.
500
+ * The function checks if an object is a likely an object descriptor in
501
+ * JavaScript. This is determined as an object with some of the known
502
+ * descriptor keys (e.g. enumerable, configurable, value, writable, get,
503
+ * or set). Technically, any object could serve as a descriptor but this
504
+ * function only returns true if known descriptor keys are found.
438
505
  *
439
506
  * @param object - The `object` parameter is the object that we want to
440
507
  * check if it is a descriptor.
@@ -453,6 +520,8 @@ class Descriptor {
453
520
  /**
454
521
  * The function checks if a given property or descriptor is a data property.
455
522
  *
523
+ * brie
524
+ *
456
525
  * @param descriptor_orProp - The `descriptor_orProp` parameter can be
457
526
  * either a descriptor or a property name.
458
527
  * @param object - The `object` parameter is the object that you want to
@@ -472,13 +541,10 @@ class Descriptor {
472
541
  : object_orProp
473
542
  )
474
543
 
475
- const { ACCESSOR_KEYS, DATA_KEYS } = this
544
+ const { DATA_KEYS } = this
476
545
  let validData = false
477
546
 
478
- if (hasSome(descriptor, ACCESSOR_KEYS)) {
479
- validData = false
480
- }
481
- else if (hasSome(descriptor, DATA_KEYS)) {
547
+ if (hasSome(descriptor, DATA_KEYS)) {
482
548
  validData = true
483
549
  }
484
550
 
@@ -507,13 +573,10 @@ class Descriptor {
507
573
  ? Descriptor.for(object_orProp, property)
508
574
  : object_orProp)
509
575
 
510
- const { ACCESSOR_KEYS, DATA_KEYS } = this
576
+ const { ACCESSOR_KEYS } = this
511
577
  let validAccessor = false
512
578
 
513
- if (hasSome(descriptor, DATA_KEYS)) {
514
- validAccessor = false
515
- }
516
- else if (hasSome(descriptor, ACCESSOR_KEYS)) {
579
+ if (hasSome(descriptor, ACCESSOR_KEYS)) {
517
580
  validAccessor = true
518
581
  }
519
582
 
@@ -0,0 +1,221 @@
1
+ import { Extension } from '@nejs/extension'
2
+
3
+ /**
4
+ * The Iterable class is designed to provide a convenient way to create synchronous
5
+ * iterable objects. It can be initialized with either an array or individual elements.
6
+ * This class implements the iterable protocol, allowing instances to be used with
7
+ * `for...of` loops and other standard JavaScript iteration mechanisms.
8
+ */
9
+ export class Iterable {
10
+ /**
11
+ * Private field to store the elements of the iterable.
12
+ * @private
13
+ */
14
+ #elements = [];
15
+
16
+ /**
17
+ * Constructs an instance of Iterable. It can be initialized with either an
18
+ * iterable object (such as an array, Set, Map, string, or any object
19
+ * implementing the iterable protocol) or individual elements. If the first
20
+ * argument is an iterable, the class instance is initialized with the
21
+ * elements from the iterable, followed by any additional arguments. If the
22
+ * first argument is not an iterable, all arguments are treated as individual
23
+ * elements.
24
+ *
25
+ * @param {Iterable|*} elementsOrFirstElement - An iterable object or the
26
+ * first element.
27
+ * @param {...*} moreElements - Additional elements if the first argument is
28
+ * not an iterable.
29
+ */
30
+ constructor(elementsOrFirstElement, ...moreElements) {
31
+ if (
32
+ elementsOrFirstElement != null &&
33
+ typeof elementsOrFirstElement[Symbol.iterator] === 'function'
34
+ ) {
35
+ this.#elements = [...elementsOrFirstElement, ...moreElements];
36
+ } else {
37
+ this.#elements = [elementsOrFirstElement, ...moreElements];
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Implements the iterable protocol. When an instance of Iterable is used
43
+ * in a `for...of` loop or spread syntax, this generator function is invoked
44
+ * to yield the elements one by one in a synchronous manner.
45
+ *
46
+ * @returns {Generator} A generator that yields each element of the iterable.
47
+ */
48
+ *[Symbol.iterator]() {
49
+ for (const element of this.#elements) {
50
+ yield element;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Provides access to the elements as a standard array. Useful for scenarios
56
+ * where array methods and behaviors are needed.
57
+ *
58
+ * @returns {Array} An array containing all the elements of the iterable.
59
+ */
60
+ get asArray() {
61
+ return this.#elements;
62
+ }
63
+
64
+ /**
65
+ * Ensures that the constructor of this object instance's name
66
+ * is returned if the string tag for this instance is queried
67
+ *
68
+ * @returns {string} the name of the class
69
+ */
70
+ get [Symbol.toStringTag]() {
71
+ return this.constructor.name
72
+ }
73
+
74
+ /**
75
+ * Checks if a given value is an iterable. This method determines if the
76
+ * provided value has a `Symbol.iterator` property that is a generator
77
+ * function. It's a precise way to identify if the value conforms to the
78
+ * iterable protocol using a generator function.
79
+ *
80
+ * Note: This method specifically checks for generator functions. Some
81
+ * iterables might use regular functions that return an iterator, which
82
+ * this method won't identify.
83
+ *
84
+ * @param {*} value - The value to be checked for iterability.
85
+ * @returns {boolean} - Returns true if the value is an iterable implemented
86
+ * using a generator function, false otherwise.
87
+ */
88
+ static isIterable(value) {
89
+ const type = Object.prototype.toString.call(value?.[Symbol.iterator]);
90
+ return type === '[object GeneratorFunction]';
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Being able to create a compliant `Iterator` around any type of iterable
96
+ * object. This can be wrapped around any type of object that has a
97
+ * `[Symbol.iterator]` property assigned to a generator function.
98
+ */
99
+ export class Iterator {
100
+ /**
101
+ * A private function that when provided has the following signature:
102
+ * `function mapEach(entry) -> entry`. This allows any changes to be made
103
+ * to each element, conditionally and programmatically, as needed before
104
+ * they are returned to the called code.
105
+ */
106
+ #mapEach = undefined
107
+
108
+ /**
109
+ * Creates a new `Iterator` object instance.
110
+ *
111
+ * @param {object} iterable any object that has a `[Symbol.iterator]`
112
+ * property assigned to a generator function.
113
+ * @param {function} mapEach when provided `mapEach` is a callback that
114
+ * takes an entry as input and receives one as output.
115
+ */
116
+ constructor(iterable, mapEach) {
117
+ if (!iterable || !Reflect.has(iterable, Symbol.iterator)) {
118
+ throw new TypeError(
119
+ 'Value used to instantiate Iterator is not iterable'
120
+ );
121
+ }
122
+
123
+ this.#iterable = iterable;
124
+ this.#iterator = iterable[Symbol.iterator]();
125
+ this.#mapEach = typeof mapEach === 'function' ? mapEach : undefined
126
+ }
127
+
128
+ /**
129
+ * Returns a new `Array` derived from the iterable this object
130
+ * wraps.
131
+ *
132
+ * @returns {array} a new `Array` generated from the wrapped
133
+ * iterable. The method is generated from `Array.from()`
134
+ */
135
+ get asArray() {
136
+ return Array.from(this.#iterable)
137
+ }
138
+
139
+ /**
140
+ * Returns the actual iterable object passed to the constructor that
141
+ * created this instance.
142
+ *
143
+ * @returns {object} the object containing the `[Symbol.iterator]`
144
+ */
145
+ get iterable() {
146
+ return this.#iterable
147
+ }
148
+
149
+ /**
150
+ * The function retrieves the next value in the iterator. If the
151
+ * the iterator has run its course, `reset()` can be invoked to
152
+ * reset the pointer to the beginning of the iteration.
153
+ *
154
+ * @returns {any} the next value
155
+ */
156
+ next() {
157
+ const input = this.#iterator.next();
158
+ let output = input
159
+
160
+ if (output.done) {
161
+ return { value: undefined, done: true };
162
+ }
163
+ else {
164
+ if (this.#mapEach && typeof this.#mapEach === 'function') {
165
+ output.value = this.#mapEach(input.value)
166
+ }
167
+
168
+ return { value: output.value, done: false };
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Resets the iterator to the beginning allowing it to be
174
+ * iterated over again.
175
+ */
176
+ reset() {
177
+ this.#iterator = this.#iterable[Symbol.iterator]();
178
+ }
179
+
180
+ /**
181
+ * The existence of this symbol on the object instances, indicates that
182
+ * it can be used in `for(.. of ..)` loops and its values can be
183
+ * extracted from calls to `Array.from()`
184
+ *
185
+ * @returns {Iterator} this is returned since this object is already
186
+ * conforming to the expected JavaScript Iterator interface
187
+ */
188
+ [Symbol.iterator]() {
189
+ return this;
190
+ }
191
+
192
+ /**
193
+ * Ensures that the constructor of this object instance's name
194
+ * is returned if the string tag for this instance is queried
195
+ *
196
+ * @returns {string} the name of the class
197
+ */
198
+ get [Symbol.toStringTag]() {
199
+ return this.constructor.name
200
+ }
201
+
202
+ /**
203
+ * The object from which its iterator functionality is derived.
204
+ *
205
+ * @type {object}
206
+ * @private
207
+ */
208
+ #iterable = null;
209
+
210
+ /**
211
+ * The results of a call to the iterable's `[Symbol.iterator]`
212
+ * generator function.
213
+ *
214
+ * @type {object}
215
+ * @private
216
+ */
217
+ #iterator = null;
218
+ }
219
+
220
+ export const IterableExtensions = new Extension(Iterable)
221
+ export const IteratorExtensions = new Extension(Iterator)