@nejs/basic-extensions 1.4.1 → 1.5.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 (47) hide show
  1. package/dist/@nejs/basic-extensions.bundle.1.4.1.js +2 -0
  2. package/dist/@nejs/basic-extensions.bundle.1.4.1.js.map +7 -0
  3. package/dist/cjs/asyncIterable.d.ts +3 -0
  4. package/dist/cjs/asyncIterable.js +203 -0
  5. package/dist/cjs/descriptor.d.ts +1 -1
  6. package/dist/cjs/descriptor.js +16 -3
  7. package/dist/cjs/globals.js +55 -67
  8. package/dist/cjs/index.d.ts +9 -2
  9. package/dist/cjs/index.js +27 -10
  10. package/dist/cjs/iterable.d.ts +3 -0
  11. package/dist/cjs/iterable.js +199 -0
  12. package/dist/cjs/objectextensions.js +9 -0
  13. package/dist/cjs/refset.d.ts +2 -0
  14. package/dist/cjs/refset.js +374 -0
  15. package/dist/cjs/symbolextensions.js +43 -0
  16. package/dist/cjs/weakrefextensions.d.ts +2 -0
  17. package/dist/cjs/weakrefextensions.js +18 -0
  18. package/dist/mjs/asyncIterable.d.ts +3 -0
  19. package/dist/mjs/asyncIterable.js +188 -0
  20. package/dist/mjs/descriptor.d.ts +1 -1
  21. package/dist/mjs/descriptor.js +14 -1
  22. package/dist/mjs/globals.js +55 -67
  23. package/dist/mjs/index.d.ts +9 -2
  24. package/dist/mjs/index.js +20 -10
  25. package/dist/mjs/iterable.d.ts +3 -0
  26. package/dist/mjs/iterable.js +184 -0
  27. package/dist/mjs/objectextensions.js +9 -0
  28. package/dist/mjs/refset.d.ts +2 -0
  29. package/dist/mjs/refset.js +352 -0
  30. package/dist/mjs/symbolextensions.js +43 -0
  31. package/dist/mjs/weakrefextensions.d.ts +2 -0
  32. package/dist/mjs/weakrefextensions.js +15 -0
  33. package/package.json +2 -2
  34. package/src/asyncIterable.js +208 -0
  35. package/src/descriptor.js +16 -1
  36. package/src/globals.js +58 -78
  37. package/src/index.js +35 -10
  38. package/src/iterable.js +203 -0
  39. package/src/objectextensions.js +12 -0
  40. package/src/refset.js +414 -0
  41. package/src/symbolextensions.js +46 -0
  42. package/src/weakrefextensions.js +18 -0
  43. package/tests/asyncIterable.test.js +44 -0
  44. package/tests/iterable.test.js +45 -0
  45. package/tests/refset.test.js +58 -0
  46. package/dist/@nejs/basic-extensions.bundle.1.4.0.js +0 -2
  47. package/dist/@nejs/basic-extensions.bundle.1.4.0.js.map +0 -7
@@ -0,0 +1,374 @@
1
+ "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var _RefSet_instances, _RefSet_objectifyValues, _RefSet_validWeakRefTarget;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.RefSetExtensions = void 0;
16
+ const extension_1 = require("@nejs/extension");
17
+ /**
18
+ * RefSet class extends the standard Set object to manage a collection of
19
+ * WeakRef objects. It provides additional functionality such as objectification
20
+ * of values and various utility methods.
21
+ *
22
+ * Unlike standard Sets or Arrays, RefSet stores weak references to objects,
23
+ * allowing them to be garbage-collected if there are no other references to
24
+ * them. This behavior is different from Arrays and standard Sets, which
25
+ * maintain strong references to their elements.
26
+ *
27
+ * @extends Set
28
+ */
29
+ class RefSet extends Set {
30
+ constructor() {
31
+ super(...arguments);
32
+ _RefSet_instances.add(this);
33
+ /**
34
+ * Private field to track whether the RefSet should objectify primitive
35
+ * values.
36
+ *
37
+ * @private
38
+ */
39
+ _RefSet_objectifyValues.set(this, false
40
+ /**
41
+ * Method to control whether the RefSet should objectify its values. When
42
+ * objectifying, primitive values (number, string, boolean, bigint) are
43
+ * converted to their respective object types, which allows them to be used as
44
+ * WeakRef targets.
45
+ *
46
+ * @param {boolean} setObjectification - Flag to enable or disable
47
+ * objectification.
48
+ * @returns {RefSet} - The current RefSet instance to allow method chaining.
49
+ */
50
+ );
51
+ }
52
+ /**
53
+ * Method to control whether the RefSet should objectify its values. When
54
+ * objectifying, primitive values (number, string, boolean, bigint) are
55
+ * converted to their respective object types, which allows them to be used as
56
+ * WeakRef targets.
57
+ *
58
+ * @param {boolean} setObjectification - Flag to enable or disable
59
+ * objectification.
60
+ * @returns {RefSet} - The current RefSet instance to allow method chaining.
61
+ */
62
+ objectifying(setObjectification = true) {
63
+ this.objectifyValues = setObjectification;
64
+ return this;
65
+ }
66
+ /**
67
+ * Returns the state indicating whether or not `RefSet` will attempt to
68
+ * convert non-valid primitives into targets that are valid input for
69
+ * new `WeakRef` object instances. If this value is `false` then no
70
+ * *objectification* will occur.
71
+ *
72
+ * @returns {boolean} The current state of objectifyValues.
73
+ */
74
+ get objectifyValues() {
75
+ return __classPrivateFieldGet(this, _RefSet_objectifyValues, "f");
76
+ }
77
+ /**
78
+ * Setting this value to true, will cause all added values to the Set to
79
+ * be analyzed for validity as a candidate to be wrapped in a `WeakRef`
80
+ * object. If true, and if possible, the object will be turned into an
81
+ * `Object` variant first. This will also enable less rigid variable
82
+ * comparison in the `.has()` method (i.e. `==` instead of `===`).
83
+ *
84
+ * @param {boolean} value - The new state to set for objectifyValues.
85
+ */
86
+ set objectifyValues(value) {
87
+ __classPrivateFieldSet(this, _RefSet_objectifyValues, !!value, "f");
88
+ }
89
+ /**
90
+ * Overrides the add method of Set. Adds a value to the RefSet, converting it
91
+ * to a WeakRef. Throws an error if the value is not a valid WeakRef target
92
+ * (e.g., null, undefined, or a registered symbol). If `objectifyValues` is
93
+ * enabled, an attempt to convert primitives to their object variants will be
94
+ * made. These are numbers, strings, boolean values and big integers.
95
+ *
96
+ * @param {*} value - The value to be added to the RefSet.
97
+ * @throws {TypeError} If the value is not a valid WeakRef target.
98
+ */
99
+ add(value) {
100
+ // Objectify the value if needed
101
+ if (__classPrivateFieldGet(this, _RefSet_objectifyValues, "f") && (typeof value === 'number' ||
102
+ typeof value === 'string' ||
103
+ typeof value === 'boolean' ||
104
+ typeof value === 'bigint')) {
105
+ value = Object(value);
106
+ }
107
+ // Check if the value is an object, and if it's a symbol, ensure it's not registered
108
+ if (typeof value === 'symbol' && Symbol.keyFor(value) !== undefined) {
109
+ throw new TypeError('RefSet cannot accept registered symbols as values');
110
+ }
111
+ if (typeof value !== 'object' && typeof value !== 'symbol') {
112
+ throw new TypeError('RefSet values must be objects, non-registered symbols, or objectified primitives');
113
+ }
114
+ // If the value is null or undefined, throw an error
115
+ if (value === null || value === undefined) {
116
+ throw new TypeError('RefSet values cannot be null or undefined');
117
+ }
118
+ super.add(new WeakRef(value));
119
+ }
120
+ /**
121
+ * Adds multiple values to the RefSet. The supplied `values` should be
122
+ * iterable and truthy. This function defers to `.add()` for its logic so
123
+ * each value from the supplied collection of values will also be subject
124
+ * to the criteria of that function.
125
+ *
126
+ * @param {Iterable} values - An iterable of values to add to the RefSet.
127
+ * @throws {TypeError} If the supplied values are falsey or non-iterable.
128
+ */
129
+ addAll(values) {
130
+ if (!values ||
131
+ (typeof values !== 'object') ||
132
+ !Reflect.has(values, Symbol.iterator)) {
133
+ throw new TypeError('The supplied values are either falsey or non-iterable');
134
+ }
135
+ for (const value of values) {
136
+ this.add(value);
137
+ }
138
+ }
139
+ /**
140
+ * Removes all elements from the RefSet that have been garbage collected
141
+ * (i.e., their WeakRef no longer points to an object).
142
+ *
143
+ * @returns {RefSet} - The current RefSet instance to allow method chaining.
144
+ */
145
+ clean() {
146
+ for (const ref of this) {
147
+ if (!ref.deref()) {
148
+ this.delete(ref);
149
+ }
150
+ }
151
+ return this;
152
+ }
153
+ /**
154
+ * Executes a provided function once for each value in the RefSet. The callback
155
+ * function receives the dereferenced value, the value again (as RefSet doesn't
156
+ * use keys), and the RefSet itself. This method provides a way to iterate over
157
+ * and apply operations to the values stored in the RefSet, taking into account
158
+ * that they are weak references and may have been garbage-collected.
159
+ *
160
+ * @param {Function} forEachFn - Function to execute for each element. It
161
+ * takes three arguments: element, element (again, as RefSet has no key), and
162
+ * the RefSet itself.
163
+ * @param {*} thisArg - Value to use as `this` when executing `forEachFn`.
164
+ */
165
+ entries() {
166
+ const refEntries = super.entries();
167
+ return refEntries
168
+ .map(([_, ref]) => [ref.deref(), ref.deref()])
169
+ .filter(([_, value]) => !!value);
170
+ }
171
+ forEach(forEachFn, thisArg) {
172
+ const set = this;
173
+ super.forEach(function (ref) {
174
+ const value = ref.deref();
175
+ if (!value) {
176
+ return;
177
+ }
178
+ forEachFn.call(thisArg, value, value, set);
179
+ });
180
+ }
181
+ /**
182
+ * Returns an iterator for the values in the RefSet. Each value is
183
+ * dereferenced from its WeakRef before being returned. This method allows
184
+ * iterating over he set's values, similar to how one would iterate over
185
+ * values in a standard Set or Array, but with the understanding that the
186
+ * values are weakly referenced and may no longer exist (in which case
187
+ * they are skipped).
188
+ *
189
+ * @returns {Iterator} An iterator for the values.
190
+ */
191
+ values() {
192
+ const values = [];
193
+ for (const value of this) {
194
+ const dereferenced = value.deref();
195
+ if (dereferenced) {
196
+ values.push(dereferenced);
197
+ }
198
+ }
199
+ return values;
200
+ }
201
+ /**
202
+ * Returns an iterator for the keys of the RefSet. In RefSet, keys and
203
+ * values are identical, so this method behaves the same as `values()`. It
204
+ * provides compatibility with the standard Set interface and allows use in
205
+ * contexts where keys are expected, despite RefSet not differentiating
206
+ * between keys and values.
207
+ *
208
+ * @returns {Iterator} An iterator for the keys.
209
+ */
210
+ keys() {
211
+ return this.values();
212
+ }
213
+ /**
214
+ * Determines whether an element with the specified value exists in the
215
+ * `RefSet`. For non-objectified sets, this method checks if the dereferenced
216
+ * values of the set include the specified value.
217
+ *
218
+ * For objectified sets, it uses the `contains` method which accounts for
219
+ * the objectification. This method differs from standard Set `has` in that
220
+ * it works with weak references and considers objectification settings.
221
+ *
222
+ * @param {*} value - The value to check for presence in the RefSet.
223
+ * @returns {boolean} - True if an element with the specified value exists
224
+ * in the RefSet, false otherwise.
225
+ */
226
+ has(value) {
227
+ if (__classPrivateFieldGet(this, _RefSet_objectifyValues, "f")) {
228
+ return this.contains(value);
229
+ }
230
+ for (const item of this.values()) {
231
+ if (item === value) {
232
+ return true;
233
+ }
234
+ }
235
+ return false;
236
+ }
237
+ /**
238
+ * Checks if the RefSet contains a value that is equal to the specified
239
+ * value. This method is used primarily in objectified RefSets to determine
240
+ * the presence of a value, taking into account objectification. It differs
241
+ * from the `has` method in that it's tailored for sets that have
242
+ * transformed their primitive values into objects, whereas `has` is more
243
+ * general-purpose.
244
+ *
245
+ * @param {*} value - The value to search for in the RefSet.
246
+ * @returns {boolean} - True if the RefSet contains the value, false otherwise.
247
+ */
248
+ contains(value) {
249
+ return !!(Array.from(this.values())
250
+ .filter(dereferencedValue => {
251
+ return value == dereferencedValue;
252
+ })
253
+ .length);
254
+ }
255
+ /**
256
+ * Creates a new array with all elements that pass the test implemented by
257
+ * the provided function. This method iterates over each element,
258
+ * dereferences it, and applies the filter function. Unlike Array `filter`,
259
+ * the callback receives the dereferenced value and not an index or array,
260
+ * reflecting the non-indexed nature of RefSet. Useful for selectively
261
+ * creating arrays from the set based on certain conditions, especially when
262
+ * dealing with weak references.
263
+ *
264
+ * @param {Function} filterFn - Function to test each element of the RefSet.
265
+ * The function receives the dereferenced value.
266
+ * @param {*} thisArg - Value to use as `this` when executing `filterFn`.
267
+ * @returns {Array} - A new array with the elements that pass the test.
268
+ */
269
+ filter(filterFn, thisArg) {
270
+ const results = [];
271
+ for (const value of this) {
272
+ const dereferenced = value?.deref();
273
+ if (dereferenced) {
274
+ const include = filterFn.call(thisArg, dereferenced, NaN, this);
275
+ if (include) {
276
+ results.push(dereferenced);
277
+ }
278
+ }
279
+ }
280
+ return results;
281
+ }
282
+ /**
283
+ * Returns the value of the first element in the RefSet that satisfies the
284
+ * provided testing function. Similar to Array `find`, this method iterates
285
+ * over the RefSet, dereferencing each value and applying the testing
286
+ * function. The non-indexed nature of RefSet is considered, as the
287
+ * callback does not receive an index. This method is useful for finding a
288
+ * specific element based on a condition.
289
+ *
290
+ * @param {*} thisArg - Value to use as this when executing findFn.
291
+ * @returns {*} - The value of the first element in the RefSet that satisfies
292
+ * the testing function, or undefined if none found.
293
+ * @returns
294
+ */
295
+ find(findFn, thisArg) {
296
+ for (const value of this) {
297
+ const dereferenced = value?.deref();
298
+ if (dereferenced) {
299
+ const found = findFn.call(thisArg, dereferenced, NaN, this);
300
+ if (found) {
301
+ return dereferenced;
302
+ }
303
+ }
304
+ }
305
+ return undefined;
306
+ }
307
+ /**
308
+ * Creates a new array or `RefSet` with the results of calling a provided
309
+ * function on every element in the calling `RefSet`. This method dereferences
310
+ * each value, applies the `mapFn`, and collects the results. If `toRefSet` is
311
+ * `true`, a new `RefSet` is returned; otherwise, an array. This method
312
+ * differs from `Array.map` in handling weak references and the potential to
313
+ * return a new `RefSet` instead of an array.
314
+ *
315
+ * @param {Function} mapFn - Function that produces an element of the new
316
+ * array or `RefSet`, taking three arguments.
317
+ * @param {*} thisArg - Value to use as this when executing mapFn.
318
+ * @param {boolean} toRefSet - Determines if the output should be a new
319
+ * `RefSet` (`true`) or an array (`false`).
320
+ * @param {boolean} mirrorObjectification - If `true` and `toRefSet` is
321
+ * `true`, the new `RefSet` mirrors the objectification setting of the
322
+ * original.
323
+ * @returns {Array|RefSet} - A new array or `RefSet` with each element being
324
+ * the result of the `mapFn`.
325
+ */
326
+ map(mapFn, thisArg, toRefSet, mirrorObjectification) {
327
+ const mapped = [];
328
+ let validRefSetOutput = true;
329
+ let validRefSetOutputIfObjectified = true;
330
+ for (const value of this) {
331
+ const dereferenced = value?.deref();
332
+ if (dereferenced) {
333
+ const mappedItem = mapFn.call(thisArg, dereferenced, NaN, this);
334
+ if (validRefSetOutput || validRefSetOutputIfObjectified) {
335
+ const weakReferenceable = __classPrivateFieldGet(this, _RefSet_instances, "m", _RefSet_validWeakRefTarget).call(this, mappedItem);
336
+ if (!weakReferenceable) {
337
+ validRefSetOutput = false;
338
+ if (validRefSetOutputIfObjectified) {
339
+ validRefSetOutputIfObjectified =
340
+ __classPrivateFieldGet(this, _RefSet_instances, "m", _RefSet_validWeakRefTarget).call(this, Object(mappedItem));
341
+ }
342
+ }
343
+ }
344
+ mapped.push(mappedItem);
345
+ }
346
+ }
347
+ if (toRefSet) {
348
+ if (validRefSetOutput) {
349
+ return new RefSet(mapped).objectifying(mirrorObjectification ? this.objectifyValues : false);
350
+ }
351
+ if (validRefSetOutputIfObjectified) {
352
+ return new RefSet(mapped.map(value => {
353
+ return __classPrivateFieldGet(this, _RefSet_instances, "m", _RefSet_validWeakRefTarget).call(this, value) ? value : Object(value);
354
+ })).objectifying();
355
+ }
356
+ }
357
+ return mapped;
358
+ }
359
+ /**
360
+ * Ensures that the constructor of this object instance's name
361
+ * is returned if the string tag for this instance is queried
362
+ *
363
+ * @returns {string} the name of the class
364
+ */
365
+ get [(_RefSet_objectifyValues = new WeakMap(), _RefSet_instances = new WeakSet(), Symbol.toStringTag)]() {
366
+ return this.constructor.name;
367
+ }
368
+ }
369
+ _RefSet_validWeakRefTarget = function _RefSet_validWeakRefTarget(value) {
370
+ return !((typeof value === 'symbol' && Symbol.keyFor(value) === undefined) ||
371
+ (typeof value !== 'object' && typeof value !== 'symbol') ||
372
+ (value === null || value === undefined));
373
+ };
374
+ exports.RefSetExtensions = new extension_1.Extension(RefSet);
@@ -21,4 +21,47 @@ exports.SymbolExtensions = new extension_1.Patch(Symbol, {
21
21
  isSymbol(value) {
22
22
  return value && (typeof value === 'symbol');
23
23
  },
24
+ /**
25
+ * Returns true if the supplied value is a Symbol created using
26
+ * `Symbol.for()`.
27
+ *
28
+ * @param {any} value assumption is that the supplied value is of type
29
+ * 'symbol' however, unless `allowOnlySymbols` is set to `true`, `false`
30
+ * will be returned for any non-symbol values.
31
+ * @param {boolean} allowOnlySymbols true if an error should be thrown
32
+ * if the supplied value is not of type 'symbol'
33
+ * @returns true if the symbol is registered, meaning, none of the spec
34
+ * static symbols (`toStringTag`, `iterator`, etc...), and no symbols
35
+ * created by passing a value directly to the Symbol function, such as
36
+ * `Symbol('name')`
37
+ */
38
+ isRegistered(value, allowOnlySymbols = false) {
39
+ if (!Symbol.isSymbol(value)) {
40
+ if (allowOnlySymbols) {
41
+ throw new TypeError('allowOnlySymbols specified; value is not a symbol');
42
+ }
43
+ return false;
44
+ }
45
+ return Symbol.keyFor(value) !== undefined;
46
+ },
47
+ /**
48
+ * A function that returns true if the symbol is not registered, meaning,
49
+ * any of the spec static symbols (`toStringTag`, `iterator`, etc...), and
50
+ * any symbols created by passing a value directly to the `Symbol` function,
51
+ * such as `Symbol('name')`.
52
+ *
53
+ * @param {any} value assumption is that the supplied value is of type
54
+ * 'symbol' however, unless allowOnlySymbols is set to true, false will
55
+ * be returned for any non-symbol values.
56
+ * @param {boolean} allowOnlySymbols true if an error should be thrown
57
+ * if the supplied value is not of type 'symbol'
58
+ * @returns true if the symbol is not registered, meaning, any of the
59
+ * spec static symbols (`toStringTag`, `iterator`, etc...), and any symbols
60
+ * created by passing a value directly to the `Symbol` function, such as
61
+ * `Symbol('name')`
62
+ * @returns
63
+ */
64
+ isNonRegistered(value, allowOnlySymbols = false) {
65
+ return !Symbol.isRegistered(value, allowOnlySymbols);
66
+ },
24
67
  });
@@ -0,0 +1,2 @@
1
+ export const WeakRefExtensions: Patch;
2
+ import { Patch } from '@nejs/extension';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WeakRefExtensions = void 0;
4
+ const extension_1 = require("@nejs/extension");
5
+ exports.WeakRefExtensions = new extension_1.Patch(WeakRef, {
6
+ /**
7
+ * A static method to check if a given value is a valid target for a WeakRef.
8
+ *
9
+ * @param {*} value - The value to check for validity as a WeakRef target.
10
+ * @returns {boolean} - True if the value is a valid WeakRef target,
11
+ * false otherwise.
12
+ */
13
+ isValidReference(value) {
14
+ return !((typeof value === 'symbol' && Symbol.keyFor(value) === undefined) ||
15
+ (typeof value !== 'object' && typeof value !== 'symbol') ||
16
+ (value === null || value === undefined));
17
+ },
18
+ });
@@ -0,0 +1,3 @@
1
+ export const AsyncIterableExtensions: Extension;
2
+ export const AsyncIteratorExtensions: Extension;
3
+ import { Extension } from '@nejs/extension';
@@ -0,0 +1,188 @@
1
+ import { Extension } from '@nejs/extension';
2
+ /**
3
+ * The AsyncIterable class extends the concept of Iterable to asynchronous
4
+ * operations. It allows creating iterable objects where each element can be
5
+ * an asynchronous entity, like a Promise. This class is particularly useful
6
+ * when dealing with asynchronous data sources, such as API responses, file
7
+ * reading in chunks, or any other data that is not immediately available but
8
+ * arrives over time.
9
+ */
10
+ class AsyncIterable {
11
+ /**
12
+ * Private field to store the elements of the async iterable.
13
+ * @private
14
+ */
15
+ #elements = [];
16
+ /**
17
+ * Constructs an instance of AsyncIterable. Similar to Iterable, it can be
18
+ * initialized with either an iterable object or individual elements. The
19
+ * elements can be promises, direct values, or a mix of both. If the first
20
+ * argument is an iterable, the instance is initialized with the elements
21
+ * from the iterable, followed by any additional arguments. If the first
22
+ * argument is not an iterable, all arguments are treated as individual
23
+ * elements.
24
+ *
25
+ * @param {Iterable|Promise|*} elementsOrFirstElement - An iterable object,
26
+ * a Promise, or the first element.
27
+ * @param {...Promise|*} moreElements - Additional elements if the first
28
+ * argument is not an iterable.
29
+ */
30
+ constructor(elementsOrFirstElement, ...moreElements) {
31
+ if (elementsOrFirstElement != null &&
32
+ typeof elementsOrFirstElement[Symbol.iterator] === 'function') {
33
+ this.#elements = [...elementsOrFirstElement, ...moreElements];
34
+ }
35
+ else {
36
+ this.#elements = [elementsOrFirstElement, ...moreElements];
37
+ }
38
+ }
39
+ /**
40
+ * Implements the async iterable protocol. When an instance of AsyncIterable
41
+ * is used in a `for await...of` loop, this async generator function is
42
+ * invoked. It yields each element as a Promise, allowing asynchronous
43
+ * iteration. Elements that are not Promises are automatically wrapped in
44
+ * a resolved Promise to ensure consistency.
45
+ *
46
+ * @returns {AsyncGenerator} An async generator that yields each element as
47
+ * a Promise.
48
+ */
49
+ async *[Symbol.asyncIterator]() {
50
+ for (const element of this.#elements) {
51
+ // Treat each element as a promise. If it's not, it's automatically
52
+ // wrapped as a resolved promise.
53
+ yield Promise.resolve(element);
54
+ }
55
+ }
56
+ /**
57
+ * Ensures that the constructor of this object instance's name
58
+ * is returned if the string tag for this instance is queried
59
+ *
60
+ * @returns {string} the name of the class
61
+ */
62
+ get [Symbol.toStringTag]() {
63
+ return this.constructor.name;
64
+ }
65
+ /**
66
+ * Being able to create a compliant `AsyncIterator` around any type of
67
+ * iterable object. This can be wrapped around any type of object that
68
+ * has a `[Symbol.asyncIterator]` property assigned to a generator
69
+ * function.
70
+ */
71
+ static AsyncIterator = class AsyncIterator {
72
+ /**
73
+ * Creates a new `AsyncIterator` object instance.
74
+ *
75
+ * @param {object} asyncIterable any object that has a
76
+ * `[Symbol.asyncIterable]` property assigned to a generator function.
77
+ */
78
+ constructor(asyncIterable) {
79
+ if (!asyncIterable || !Reflect.has(asyncIterable, Symbol.asyncIterator)) {
80
+ throw new TypeError('Value used to instantiate AsyncIterator is not an async iterable');
81
+ }
82
+ this.#asyncIterable = asyncIterable;
83
+ this.#asyncIterator = asyncIterable[Symbol.asyncIterator]();
84
+ }
85
+ /**
86
+ * Returns a new `Array` derived from the iterable this object
87
+ * wraps.
88
+ *
89
+ * @returns {array} a new `Array` generated from the wrapped
90
+ * iterable. The method is generated from using an async for of
91
+ * loop.
92
+ */
93
+ async asArray() {
94
+ const array = [];
95
+ for await (const value of this) {
96
+ array.push(value);
97
+ }
98
+ return array;
99
+ }
100
+ /**
101
+ * Returns the actual iterable object passed to the constructor that
102
+ * created this instance.
103
+ *
104
+ * @returns {object} the object containing the `[Symbol.iterator]`
105
+ */
106
+ get asyncIterable() {
107
+ return this.#asyncIterable;
108
+ }
109
+ /**
110
+ * The function retrieves the next value in the iterator. If the
111
+ * the iterator has run its course, `reset()` can be invoked to
112
+ * reset the pointer to the beginning of the iteration.
113
+ *
114
+ * @returns {any} the next value
115
+ */
116
+ async next() {
117
+ const result = await this.#asyncIterator.next();
118
+ if (result.done) {
119
+ return { value: undefined, done: true };
120
+ }
121
+ else {
122
+ return { value: result.value, done: false };
123
+ }
124
+ }
125
+ /**
126
+ * Resets the async iterator to the beginning allowing it to be
127
+ * iterated over again.
128
+ */
129
+ async reset() {
130
+ this.#asyncIterator = this.#asyncIterable[Symbol.asyncIterator]();
131
+ }
132
+ /**
133
+ * The existence of this symbol on the object instances, indicates that
134
+ * it can be used in `for(.. of ..)` loops and its values can be
135
+ * extracted from calls to `Array.from()`
136
+ *
137
+ * @returns {AsyncIterable} this is returned since this object is already
138
+ * conforming to the expected JavaScript AsyncIterator interface
139
+ */
140
+ [Symbol.asyncIterator]() {
141
+ return this;
142
+ }
143
+ /**
144
+ * Ensures that the constructor of this object instance's name
145
+ * is returned if the string tag for this instance is queried
146
+ *
147
+ * @returns {string} the name of the class
148
+ */
149
+ get [Symbol.toStringTag]() {
150
+ return this.constructor.name;
151
+ }
152
+ /**
153
+ * The object from which its iterator functionality is derived.
154
+ *
155
+ * @type {object}
156
+ * @private
157
+ */
158
+ #asyncIterable = null;
159
+ /**
160
+ * The results of a call to the iterable's `[Symbol.asyncIterator]`
161
+ * generator function.
162
+ *
163
+ * @type {object}
164
+ * @private
165
+ */
166
+ #asyncIterator = null;
167
+ };
168
+ /**
169
+ * Checks if a given value is an async iterable. This method determines if
170
+ * the provided value has a `Symbol.asyncIterator` property that is an async
171
+ * generator function. It's a precise way to identify if the value conforms
172
+ * to the async iterable protocol using an async generator function.
173
+ *
174
+ * Note: This method specifically checks for async generator functions. Some
175
+ * async iterables might use regular async functions that return an async
176
+ * iterator, which this method won't identify.
177
+ *
178
+ * @param {*} value - The value to be checked for async iterability.
179
+ * @returns {boolean} - Returns true if the value is an async iterable
180
+ * implemented using an async generator function, false otherwise.
181
+ */
182
+ static isAsyncIterable(value) {
183
+ const type = Object.prototype.toString.call(value?.[Symbol.asyncIterator]);
184
+ return type === '[object AsyncGeneratorFunction]';
185
+ }
186
+ }
187
+ export const AsyncIterableExtensions = new Extension(AsyncIterable);
188
+ export const AsyncIteratorExtensions = new Extension(AsyncIterable.AsyncIterator);
@@ -1,2 +1,2 @@
1
- export const DescriptorExtension: Extension;
1
+ export const DescriptorExtensions: Extension;
2
2
  import { Extension } from '@nejs/extension';
@@ -171,6 +171,10 @@ class Descriptor {
171
171
  set set(value) {
172
172
  (this.#desc || {}).set = value;
173
173
  }
174
+ [Symbol.for('nodejs.util.inspect.custom')](depth, options, inspect) {
175
+ const type = this.isAccessor ? ' (Accessor)' : this.isData ? ' (Data)' : '';
176
+ return `Descriptor${type} ${inspect(this.#desc, { ...options, depth })}`;
177
+ }
174
178
  /**
175
179
  * Shorthand for Object.getOwnPropertyDescriptor()
176
180
  *
@@ -228,6 +232,15 @@ class Descriptor {
228
232
  return this.#desc;
229
233
  }
230
234
  }
235
+ /**
236
+ * Ensures that the constructor of this object instance's name
237
+ * is returned if the string tag for this instance is queried
238
+ *
239
+ * @returns {string} the name of the class
240
+ */
241
+ get [Symbol.toStringTag]() {
242
+ return this.constructor.name;
243
+ }
231
244
  /**
232
245
  * The function `getData` retrieves the value of a property from an object if it
233
246
  * exists and is a data property.
@@ -494,4 +507,4 @@ class Descriptor {
494
507
  return ['value', 'writable'];
495
508
  }
496
509
  }
497
- export const DescriptorExtension = new Extension(Descriptor);
510
+ export const DescriptorExtensions = new Extension(Descriptor);