@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
package/src/refset.js ADDED
@@ -0,0 +1,414 @@
1
+ import { Extension } from '@nejs/extension'
2
+
3
+ /**
4
+ * RefSet class extends the standard Set object to manage a collection of
5
+ * WeakRef objects. It provides additional functionality such as objectification
6
+ * of values and various utility methods.
7
+ *
8
+ * Unlike standard Sets or Arrays, RefSet stores weak references to objects,
9
+ * allowing them to be garbage-collected if there are no other references to
10
+ * them. This behavior is different from Arrays and standard Sets, which
11
+ * maintain strong references to their elements.
12
+ *
13
+ * @extends Set
14
+ */
15
+ class RefSet extends Set {
16
+ /**
17
+ * Private field to track whether the RefSet should objectify primitive
18
+ * values.
19
+ *
20
+ * @private
21
+ */
22
+ #objectifyValues = false
23
+
24
+ /**
25
+ * Method to control whether the RefSet should objectify its values. When
26
+ * objectifying, primitive values (number, string, boolean, bigint) are
27
+ * converted to their respective object types, which allows them to be used as
28
+ * WeakRef targets.
29
+ *
30
+ * @param {boolean} setObjectification - Flag to enable or disable
31
+ * objectification.
32
+ * @returns {RefSet} - The current RefSet instance to allow method chaining.
33
+ */
34
+ objectifying(setObjectification = true) {
35
+ this.objectifyValues = setObjectification
36
+ return this
37
+ }
38
+
39
+ /**
40
+ * Returns the state indicating whether or not `RefSet` will attempt to
41
+ * convert non-valid primitives into targets that are valid input for
42
+ * new `WeakRef` object instances. If this value is `false` then no
43
+ * *objectification* will occur.
44
+ *
45
+ * @returns {boolean} The current state of objectifyValues.
46
+ */
47
+ get objectifyValues() {
48
+ return this.#objectifyValues
49
+ }
50
+
51
+ /**
52
+ * Setting this value to true, will cause all added values to the Set to
53
+ * be analyzed for validity as a candidate to be wrapped in a `WeakRef`
54
+ * object. If true, and if possible, the object will be turned into an
55
+ * `Object` variant first. This will also enable less rigid variable
56
+ * comparison in the `.has()` method (i.e. `==` instead of `===`).
57
+ *
58
+ * @param {boolean} value - The new state to set for objectifyValues.
59
+ */
60
+ set objectifyValues(value) {
61
+ this.#objectifyValues = !!value
62
+ }
63
+
64
+ /**
65
+ * Overrides the add method of Set. Adds a value to the RefSet, converting it
66
+ * to a WeakRef. Throws an error if the value is not a valid WeakRef target
67
+ * (e.g., null, undefined, or a registered symbol). If `objectifyValues` is
68
+ * enabled, an attempt to convert primitives to their object variants will be
69
+ * made. These are numbers, strings, boolean values and big integers.
70
+ *
71
+ * @param {*} value - The value to be added to the RefSet.
72
+ * @throws {TypeError} If the value is not a valid WeakRef target.
73
+ */
74
+ add(value) {
75
+ // Objectify the value if needed
76
+ if (this.#objectifyValues && (
77
+ typeof value === 'number' ||
78
+ typeof value === 'string' ||
79
+ typeof value === 'boolean' ||
80
+ typeof value === 'bigint'
81
+ )) {
82
+ value = Object(value);
83
+ }
84
+
85
+ // Check if the value is an object, and if it's a symbol, ensure it's not registered
86
+ if (typeof value === 'symbol' && Symbol.keyFor(value) !== undefined) {
87
+ throw new TypeError('RefSet cannot accept registered symbols as values');
88
+ }
89
+
90
+ if (typeof value !== 'object' && typeof value !== 'symbol') {
91
+ throw new TypeError(
92
+ 'RefSet values must be objects, non-registered symbols, or objectified primitives'
93
+ );
94
+ }
95
+
96
+ // If the value is null or undefined, throw an error
97
+ if (value === null || value === undefined) {
98
+ throw new TypeError('RefSet values cannot be null or undefined');
99
+ }
100
+
101
+ super.add(new WeakRef(value));
102
+ }
103
+
104
+ /**
105
+ * Adds multiple values to the RefSet. The supplied `values` should be
106
+ * iterable and truthy. This function defers to `.add()` for its logic so
107
+ * each value from the supplied collection of values will also be subject
108
+ * to the criteria of that function.
109
+ *
110
+ * @param {Iterable} values - An iterable of values to add to the RefSet.
111
+ * @throws {TypeError} If the supplied values are falsey or non-iterable.
112
+ */
113
+ addAll(values) {
114
+ if (
115
+ !values ||
116
+ (typeof values !== 'object') ||
117
+ !Reflect.has(values, Symbol.iterator)
118
+ ) {
119
+ throw new TypeError('The supplied values are either falsey or non-iterable')
120
+ }
121
+
122
+ for (const value of values) {
123
+ this.add(value)
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Removes all elements from the RefSet that have been garbage collected
129
+ * (i.e., their WeakRef no longer points to an object).
130
+ *
131
+ * @returns {RefSet} - The current RefSet instance to allow method chaining.
132
+ */
133
+ clean() {
134
+ for (const ref of this) {
135
+ if (!ref.deref()) {
136
+ this.delete(ref)
137
+ }
138
+ }
139
+
140
+ return this
141
+ }
142
+
143
+ /**
144
+ * Executes a provided function once for each value in the RefSet. The callback
145
+ * function receives the dereferenced value, the value again (as RefSet doesn't
146
+ * use keys), and the RefSet itself. This method provides a way to iterate over
147
+ * and apply operations to the values stored in the RefSet, taking into account
148
+ * that they are weak references and may have been garbage-collected.
149
+ *
150
+ * @param {Function} forEachFn - Function to execute for each element. It
151
+ * takes three arguments: element, element (again, as RefSet has no key), and
152
+ * the RefSet itself.
153
+ * @param {*} thisArg - Value to use as `this` when executing `forEachFn`.
154
+ */
155
+ entries() {
156
+ const refEntries = super.entries()
157
+
158
+ return refEntries
159
+ .map(([_, ref]) => [ref.deref(), ref.deref()])
160
+ .filter(([_, value]) => !!value)
161
+ }
162
+
163
+ forEach(forEachFn, thisArg) {
164
+ const set = this
165
+
166
+ super.forEach(function(ref) {
167
+ const value = ref.deref()
168
+
169
+ if (!value) {
170
+ return
171
+ }
172
+
173
+ forEachFn.call(thisArg, value, value, set)
174
+ })
175
+ }
176
+
177
+ /**
178
+ * Returns an iterator for the values in the RefSet. Each value is
179
+ * dereferenced from its WeakRef before being returned. This method allows
180
+ * iterating over he set's values, similar to how one would iterate over
181
+ * values in a standard Set or Array, but with the understanding that the
182
+ * values are weakly referenced and may no longer exist (in which case
183
+ * they are skipped).
184
+ *
185
+ * @returns {Iterator} An iterator for the values.
186
+ */
187
+ values() {
188
+ const values = []
189
+
190
+ for (const value of this) {
191
+ const dereferenced = value.deref()
192
+
193
+ if (dereferenced) {
194
+ values.push(dereferenced)
195
+ }
196
+ }
197
+
198
+ return values
199
+ }
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
+ /**
215
+ * Determines whether an element with the specified value exists in the
216
+ * `RefSet`. For non-objectified sets, this method checks if the dereferenced
217
+ * values of the set include the specified value.
218
+ *
219
+ * For objectified sets, it uses the `contains` method which accounts for
220
+ * the objectification. This method differs from standard Set `has` in that
221
+ * it works with weak references and considers objectification settings.
222
+ *
223
+ * @param {*} value - The value to check for presence in the RefSet.
224
+ * @returns {boolean} - True if an element with the specified value exists
225
+ * in the RefSet, false otherwise.
226
+ */
227
+ has(value) {
228
+ if (this.#objectifyValues) {
229
+ return this.contains(value)
230
+ }
231
+
232
+ for (const item of this.values()) {
233
+ if (item === value) {
234
+ return true
235
+ }
236
+ }
237
+
238
+ return false
239
+ }
240
+
241
+ /**
242
+ * Checks if the RefSet contains a value that is equal to the specified
243
+ * value. This method is used primarily in objectified RefSets to determine
244
+ * the presence of a value, taking into account objectification. It differs
245
+ * from the `has` method in that it's tailored for sets that have
246
+ * transformed their primitive values into objects, whereas `has` is more
247
+ * general-purpose.
248
+ *
249
+ * @param {*} value - The value to search for in the RefSet.
250
+ * @returns {boolean} - True if the RefSet contains the value, false otherwise.
251
+ */
252
+ contains(value) {
253
+ return !!(Array.from(this.values())
254
+ .filter(dereferencedValue => {
255
+ return value == dereferencedValue
256
+ })
257
+ .length
258
+ )
259
+ }
260
+
261
+ /**
262
+ * Creates a new array with all elements that pass the test implemented by
263
+ * the provided function. This method iterates over each element,
264
+ * dereferences it, and applies the filter function. Unlike Array `filter`,
265
+ * the callback receives the dereferenced value and not an index or array,
266
+ * reflecting the non-indexed nature of RefSet. Useful for selectively
267
+ * creating arrays from the set based on certain conditions, especially when
268
+ * dealing with weak references.
269
+ *
270
+ * @param {Function} filterFn - Function to test each element of the RefSet.
271
+ * The function receives the dereferenced value.
272
+ * @param {*} thisArg - Value to use as `this` when executing `filterFn`.
273
+ * @returns {Array} - A new array with the elements that pass the test.
274
+ */
275
+ filter(filterFn, thisArg) {
276
+ const results = []
277
+
278
+ for (const value of this) {
279
+ const dereferenced = value?.deref()
280
+
281
+ if (dereferenced) {
282
+ const include = filterFn.call(thisArg, dereferenced, NaN, this)
283
+
284
+ if (include) {
285
+ results.push(dereferenced)
286
+ }
287
+ }
288
+ }
289
+
290
+ return results
291
+ }
292
+
293
+ /**
294
+ * Returns the value of the first element in the RefSet that satisfies the
295
+ * provided testing function. Similar to Array `find`, this method iterates
296
+ * over the RefSet, dereferencing each value and applying the testing
297
+ * function. The non-indexed nature of RefSet is considered, as the
298
+ * callback does not receive an index. This method is useful for finding a
299
+ * specific element based on a condition.
300
+ *
301
+ * @param {*} thisArg - Value to use as this when executing findFn.
302
+ * @returns {*} - The value of the first element in the RefSet that satisfies
303
+ * the testing function, or undefined if none found.
304
+ * @returns
305
+ */
306
+ find(findFn, thisArg) {
307
+ for (const value of this) {
308
+ const dereferenced = value?.deref()
309
+
310
+ if (dereferenced) {
311
+ const found = findFn.call(thisArg, dereferenced, NaN, this)
312
+
313
+ if (found) {
314
+ return dereferenced
315
+ }
316
+ }
317
+ }
318
+
319
+ return undefined
320
+ }
321
+
322
+ /**
323
+ * Creates a new array or `RefSet` with the results of calling a provided
324
+ * function on every element in the calling `RefSet`. This method dereferences
325
+ * each value, applies the `mapFn`, and collects the results. If `toRefSet` is
326
+ * `true`, a new `RefSet` is returned; otherwise, an array. This method
327
+ * differs from `Array.map` in handling weak references and the potential to
328
+ * return a new `RefSet` instead of an array.
329
+ *
330
+ * @param {Function} mapFn - Function that produces an element of the new
331
+ * array or `RefSet`, taking three arguments.
332
+ * @param {*} thisArg - Value to use as this when executing mapFn.
333
+ * @param {boolean} toRefSet - Determines if the output should be a new
334
+ * `RefSet` (`true`) or an array (`false`).
335
+ * @param {boolean} mirrorObjectification - If `true` and `toRefSet` is
336
+ * `true`, the new `RefSet` mirrors the objectification setting of the
337
+ * original.
338
+ * @returns {Array|RefSet} - A new array or `RefSet` with each element being
339
+ * the result of the `mapFn`.
340
+ */
341
+ map(mapFn, thisArg, toRefSet, mirrorObjectification) {
342
+ const mapped = []
343
+
344
+ let validRefSetOutput = true
345
+ let validRefSetOutputIfObjectified = true
346
+
347
+ for (const value of this) {
348
+ const dereferenced = value?.deref()
349
+
350
+ if (dereferenced) {
351
+ const mappedItem = mapFn.call(thisArg, dereferenced, NaN, this)
352
+
353
+ if (validRefSetOutput || validRefSetOutputIfObjectified) {
354
+ const weakReferenceable = this.#validWeakRefTarget(mappedItem)
355
+
356
+ if (!weakReferenceable) {
357
+ validRefSetOutput = false
358
+
359
+ if (validRefSetOutputIfObjectified) {
360
+ validRefSetOutputIfObjectified =
361
+ this.#validWeakRefTarget(Object(mappedItem))
362
+ }
363
+ }
364
+ }
365
+
366
+ mapped.push(mappedItem)
367
+ }
368
+ }
369
+
370
+ if (toRefSet) {
371
+ if (validRefSetOutput) {
372
+ return new RefSet(mapped).objectifying(
373
+ mirrorObjectification ? this.objectifyValues : false
374
+ )
375
+ }
376
+
377
+ if (validRefSetOutputIfObjectified) {
378
+ return new RefSet(mapped.map(value => {
379
+ return this.#validWeakRefTarget(value) ? value : Object(value)
380
+ })).objectifying()
381
+ }
382
+ }
383
+
384
+ return mapped
385
+ }
386
+
387
+ /**
388
+ * Ensures that the constructor of this object instance's name
389
+ * is returned if the string tag for this instance is queried
390
+ *
391
+ * @returns {string} the name of the class
392
+ */
393
+ get [Symbol.toStringTag]() {
394
+ return this.constructor.name
395
+ }
396
+
397
+ /**
398
+ * Private method to check if a given value is a valid target for a WeakRef.
399
+ *
400
+ * @param {*} value - The value to check for validity as a WeakRef target.
401
+ * @returns {boolean} - True if the value is a valid WeakRef target,
402
+ * false otherwise.
403
+ * @private
404
+ */
405
+ #validWeakRefTarget(value) {
406
+ return !(
407
+ (typeof value === 'symbol' && Symbol.keyFor(value) === undefined) ||
408
+ (typeof value !== 'object' && typeof value !== 'symbol') ||
409
+ (value === null || value === undefined)
410
+ )
411
+ }
412
+ }
413
+
414
+ export const RefSetExtensions = new Extension(RefSet)
@@ -19,4 +19,50 @@ export const SymbolExtensions = new Patch(Symbol, {
19
19
  isSymbol(value) {
20
20
  return value && (typeof value === 'symbol');
21
21
  },
22
+
23
+ /**
24
+ * Returns true if the supplied value is a Symbol created using
25
+ * `Symbol.for()`.
26
+ *
27
+ * @param {any} value assumption is that the supplied value is of type
28
+ * 'symbol' however, unless `allowOnlySymbols` is set to `true`, `false`
29
+ * will be returned for any non-symbol values.
30
+ * @param {boolean} allowOnlySymbols true if an error should be thrown
31
+ * if the supplied value is not of type 'symbol'
32
+ * @returns true if the symbol is registered, meaning, none of the spec
33
+ * static symbols (`toStringTag`, `iterator`, etc...), and no symbols
34
+ * created by passing a value directly to the Symbol function, such as
35
+ * `Symbol('name')`
36
+ */
37
+ isRegistered(value, allowOnlySymbols = false) {
38
+ if (!Symbol.isSymbol(value)) {
39
+ if (allowOnlySymbols) {
40
+ throw new TypeError('allowOnlySymbols specified; value is not a symbol')
41
+ }
42
+ return false
43
+ }
44
+
45
+ return Symbol.keyFor(value) !== undefined
46
+ },
47
+
48
+ /**
49
+ * A function that returns true if the symbol is not registered, meaning,
50
+ * any of the spec static symbols (`toStringTag`, `iterator`, etc...), and
51
+ * any symbols created by passing a value directly to the `Symbol` function,
52
+ * such as `Symbol('name')`.
53
+ *
54
+ * @param {any} value assumption is that the supplied value is of type
55
+ * 'symbol' however, unless allowOnlySymbols is set to true, false will
56
+ * be returned for any non-symbol values.
57
+ * @param {boolean} allowOnlySymbols true if an error should be thrown
58
+ * if the supplied value is not of type 'symbol'
59
+ * @returns true if the symbol is not registered, meaning, any of the
60
+ * spec static symbols (`toStringTag`, `iterator`, etc...), and any symbols
61
+ * created by passing a value directly to the `Symbol` function, such as
62
+ * `Symbol('name')`
63
+ * @returns
64
+ */
65
+ isNonRegistered(value, allowOnlySymbols = false) {
66
+ return !Symbol.isRegistered(value, allowOnlySymbols)
67
+ },
22
68
  });
@@ -0,0 +1,18 @@
1
+ import { Patch } from '@nejs/extension'
2
+
3
+ export const WeakRefExtensions = new Patch(WeakRef, {
4
+ /**
5
+ * A static method to check if a given value is a valid target for a WeakRef.
6
+ *
7
+ * @param {*} value - The value to check for validity as a WeakRef target.
8
+ * @returns {boolean} - True if the value is a valid WeakRef target,
9
+ * false otherwise.
10
+ */
11
+ isValidReference(value) {
12
+ return !(
13
+ (typeof value === 'symbol' && Symbol.keyFor(value) === undefined) ||
14
+ (typeof value !== 'object' && typeof value !== 'symbol') ||
15
+ (value === null || value === undefined)
16
+ )
17
+ },
18
+ })
@@ -0,0 +1,44 @@
1
+ const { enableAll, disableAll } = require('../dist/cjs/index.js')
2
+
3
+ describe('AsyncIterable', () => {
4
+ beforeAll(() => { enableAll() })
5
+ afterAll(() => { disableAll() })
6
+
7
+ test('should create an async iterable from an array of promises', async () => {
8
+ const promises = [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)];
9
+ const asyncIterable = new AsyncIterable(promises);
10
+ const result = [];
11
+
12
+ for await (const item of asyncIterable) {
13
+ result.push(item);
14
+ }
15
+
16
+ expect(result).toEqual([1, 2, 3]);
17
+ });
18
+
19
+ test('should create an async iterable from individual promises', async () => {
20
+ const asyncIterable = new AsyncIterable(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3));
21
+ const result = [];
22
+
23
+ for await (const item of asyncIterable) {
24
+ result.push(item);
25
+ }
26
+
27
+ expect(result).toEqual([1, 2, 3]);
28
+ });
29
+
30
+ test('isAsyncIterable should return true for async generator function', async () => {
31
+ async function* gen() {
32
+ yield Promise.resolve(1);
33
+ }
34
+ const asyncIterable = new AsyncIterable(gen);
35
+
36
+ expect(AsyncIterable.isAsyncIterable(asyncIterable)).toBeTruthy();
37
+ });
38
+
39
+ test('isAsyncIterable should return false for non-async iterables', () => {
40
+ const nonAsyncIterable = new Iterable([1, 2, 3]);
41
+
42
+ expect(AsyncIterable.isAsyncIterable(nonAsyncIterable)).toBeFalsy();
43
+ });
44
+ });
@@ -0,0 +1,45 @@
1
+ const { enableAll, disableAll } = require('../dist/cjs/index.js')
2
+
3
+ describe('Iterable', () => {
4
+ beforeAll(() => { enableAll() })
5
+ afterAll(() => { disableAll() })
6
+
7
+ test('should create an iterable from an array', () => {
8
+ const array = [1, 2, 3];
9
+ const iterable = new Iterable(array);
10
+
11
+ expect([...iterable]).toEqual(array);
12
+ });
13
+
14
+ test('should create an iterable from individual elements', () => {
15
+ const iterable = new Iterable(1, 2, 3);
16
+
17
+ expect([...iterable]).toEqual([1, 2, 3]);
18
+ });
19
+
20
+ test('should work with for...of loop', () => {
21
+ const iterable = new Iterable('a', 'b', 'c');
22
+ const result = [];
23
+
24
+ for (const item of iterable) {
25
+ result.push(item);
26
+ }
27
+
28
+ expect(result).toEqual(['a', 'b', 'c']);
29
+ });
30
+
31
+ test('isIterable should return true for generator function', () => {
32
+ function* gen() {
33
+ yield 1;
34
+ }
35
+ const iterable = new Iterable(gen);
36
+
37
+ expect(Iterable.isIterable(iterable)).toBeTruthy();
38
+ });
39
+
40
+ test('isIterable should return false for non-iterables', () => {
41
+ const nonIterable = {};
42
+
43
+ expect(Iterable.isIterable(nonIterable)).toBeFalsy();
44
+ });
45
+ });
@@ -0,0 +1,58 @@
1
+ const { enableAll, disableAll } = require('../dist/cjs/index.js')
2
+
3
+ describe('RefSet', () => {
4
+ beforeAll(() => { enableAll() })
5
+ afterAll(() => { disableAll() })
6
+
7
+ let refSet;
8
+
9
+ beforeEach(() => {
10
+ refSet = new RefSet();
11
+ });
12
+
13
+ describe('objectification behavior', () => {
14
+ test('should objectify primitive values when objectification is enabled', () => {
15
+ refSet.objectifying(true).add(123)
16
+
17
+ expect([...refSet].length).toBe(1);
18
+ expect(typeof [...refSet][0]).toBe('object');
19
+ });
20
+
21
+ test('should not objectify primitive values when objectification is disabled', () => {
22
+ expect(() => refSet.objectifying(false).add(123)).toThrow()
23
+ });
24
+ });
25
+
26
+ describe('add method', () => {
27
+ test('should add non-null objects and non-registered symbols', () => {
28
+ const obj = {};
29
+ const symbol = Symbol('test');
30
+ refSet.add(obj);
31
+ refSet.add(symbol);
32
+
33
+ expect(refSet.has(obj)).toBeTruthy();
34
+ expect(refSet.has(symbol)).toBeTruthy();
35
+ });
36
+
37
+ test('should throw for null, undefined, and registered symbols', () => {
38
+ expect(() => refSet.add(null)).toThrow(TypeError);
39
+ expect(() => refSet.add(undefined)).toThrow(TypeError);
40
+ expect(() => refSet.add(Symbol.for('test'))).toThrow(TypeError);
41
+ });
42
+ });
43
+
44
+ describe('addAll method', () => {
45
+ test('should add all elements from an iterable', () => {
46
+ const values = [1, 2, 3];
47
+ refSet.objectifying().addAll(values);
48
+
49
+ expect(refSet.size).toBe(values.length);
50
+ });
51
+
52
+ test('should throw for non-iterable values', () => {
53
+ expect(() => refSet.addAll(null)).toThrow(TypeError);
54
+ });
55
+ });
56
+
57
+ });
58
+
@@ -1,2 +0,0 @@
1
- var nejsBasicExtensions=(()=>{var R=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var B=(e,t)=>{for(var s in t)R(e,s,{get:t[s],enumerable:!0})},K=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of C(t))!I.call(e,i)&&i!==s&&R(e,i,{get:()=>t[i],enumerable:!(r=F(t,i))||r.enumerable});return e};var Y=e=>K(R({},"__esModule",{value:!0}),e);var J={};B(J,{ArrayPrototypeExtensions:()=>D,DescriptorExtension:()=>E,FunctionExtensions:()=>d,GlobalFunctionsAndProps:()=>x,ObjectExtensions:()=>p,ReflectExtensions:()=>b,StringExtensions:()=>y,SymbolExtensions:()=>v,all:()=>W,disableAll:()=>H,enableAll:()=>U});var k=e=>/(\w+)]/.exec(Object.prototype.toString.call(e))[1],f=class extends Error{constructor(t,s){super(`${k(t)} disallows tampering with ${s}.`),Object.assign(this,{owner:t,key:s})}get[Symbol.toStringTag](){return this.constructor.name}};var M=e=>/(\w+)]/.exec(Object.prototype.toString.call(e))[1],h=class extends Error{constructor(t,s){super(`${M(t)} does not have a property named '${s}'.`),Object.assign(this,{owner:t,key:s})}get[Symbol.toStringTag](){return this.constructor.name}};var g=class{constructor(t,s=!1){this.started=!1,this.preventRevert=s,this.patch=t,this.patchName=t.owner?.name??t.owner?.constructor?.name??/(\w+)]/.exec(Object.prototype.toString.call(t.owner))[1],this.state={needsApplication:!1,needsReversion:!1}}start(){return this.started||(this.state.needsApplication=!this.patch.applied,this.state.needsReversion=this.patch.applied,this.started=!0,this.state.needsApplication&&this.patch.apply()),this}stop(){return this.started&&((this.preventRevert||this.patch.applied)&&this.patch.revert(),this.state.needsApplication=!1,this.state.needsReversion=!1,this.started=!1),this}get[Symbol.toStringTag](){return`${this.constructor.name}:${this.patchName}`}[Symbol.for("nodejs.util.inspect.custom")](t,s,r){let i=this[Symbol.toStringTag],n=`(started: ${this.started} needed: ${this.state.needsApplication})`;return r(`${i} ${n}`,{...s,depth:t})}};var c=class e{constructor(t,s,r={}){Object.assign(this,{owner:t,options:r,applied:!1}),this.patchConflicts={},this.patchEntries={},this.patchesOwner=s,Reflect.ownKeys(s).forEach(i=>{this.patchEntries[i]=new e.#t(i,this.patchesOwner),Reflect.has(this.owner,i)&&(this.patchConflicts[i]=new e.#t(i,this.owner))}),e.patches.has(t)||e.patches.set(t,[]),e.patches.get(t).push(this)}get patches(){return Reflect.ownKeys(this.patchEntries).map(t=>[t,this.patchEntries[t]])}get conflicts(){return Reflect.ownKeys(this.patchConflicts).map(t=>[t,this.patchConflicts[t]])}apply(){this.applied||(this.patches.forEach(([,t])=>{Object.defineProperty(this.owner,t.key,t.descriptor)}),this.applied=!0)}createToggle(t=!1){return new g(this,t)}revert(){this.applied&&(this.patches.forEach(([,t])=>{delete this.owner[t.key]}),this.conflicts.forEach(([,t])=>{Object.defineProperty(this.owner,t.key,t.descriptor)}),this.applied=!1)}release(){let t=e.patches.get(this.owner);t.splice(t.find(s=>s===this),1)}owner=null;options=null;static patches=new Map;static enableFor(t){if(e.patches.has(t))for(let s of e.patches.get(t))s.apply()}static disableFor(t){if(e.patches.has(t))for(let s of e.patches.get(t))s.revert()}static#t=class{constructor(t,s=globalThis){Object.assign(this,{key:t,descriptor:Object.getOwnPropertyDescriptor(s,t),owner:s})}get computed(){return this.isAccessor?this.descriptor.get.bind(this.owner).call():this.descriptor.value}get isData(){return Reflect.has(this.descriptor,"value")}get isAccessor(){return Reflect.has(this.descriptor,"get")}get isReadOnly(){return Reflect.has(this.descriptor,"configurable")&&!this.descriptor.configurable||Reflect.has(this.descriptor,"writable")&&!this.descriptor.writable}get[Symbol.toStringTag](){return this.constructor.name}[Symbol.for("nodejs.util.inspect.custom")](t,s,r){return`PatchEntry<${this.key}, ${this.isData?"Data":"Accessor"}${this.isReadOnly?" [ReadOnly]":""}>`}}};var m=class e extends c{constructor(t,s,r=globalThis,i={}){let{key:n,extension:o,valid:a}=e.determineInput(t);if(o=s||o,!a)throw new h(r,n);let l=Object.getOwnPropertyDescriptor(r,n);if(l&&(Reflect.has(l,"writable")&&!l.writable||Reflect.has(l,"configurable")&&!l.configurable))throw new f(r,n);super(r,{[n]:o},i),this.key=n}static determineInput(t){let s={key:null,extension:null,valid:!1};return t instanceof Function?s={key:t.name,extension:t,valid:!0}:(typeof t=="string"||t instanceof String)&&(s={key:t,extension:null,valid:!0}),s}[Symbol.for("nodejs.util.inspect.custom")](t,s,r){return`Extension<${this.key}>`}get[Symbol.toStringTag](){return this.constructor.name}};var d=new c(Function,{isClass(e){return e instanceof Function&&!!/^class\s/.exec(String(e))},isFunction(e){return e instanceof Function},isAsync(e){let t=/(\w+)]/g.exec(Object.prototype.toString.call(e))[1];return e instanceof Function&&t.includes("Async")},isBigArrow(e){return e instanceof Function&&String(e).includes("=>")&&!String(e).startsWith("bound")&&!Reflect.has(e,"prototype")},isBound(e){return e instanceof Function&&String(e).startsWith("bound")&&!Reflect.has(e,"prototype")}});var p=new c(Object,{getStringTag(e){return/\s(.+)]/.exec(Object.prototype.toString.call(e))[1]},getType(e,t=globalThis){let s=Object.getStringTag(e);switch(s){case"Null":return null;case"Undefined":return;default:return t[s]}},isObject(e){return e&&(e instanceof Object||typeof e=="object")},isPrimitive(e){if(e===null)return!0;switch(typeof e){case"string":case"number":case"bigint":case"boolean":case"undefined":case"symbol":return!0;default:return!1}},isValidKey(e){return typeof e=="string"||typeof e=="symbol"},stripTo(e,t,s=!0){let r={};if(!Array.isArray(t))return r;for(let i of t)if(Reflect.has(e,i)){let n=Object.getOwnPropertyDescriptor(e,i);(Reflect.has(n,"get")||Reflect.has(n,"set"))&&s&&(n.get=n?.get?.bind(e),n.set=n?.set?.bind(e)),Object.defineProperty(r,n)}return r}});var b=new c(Reflect,{hasAll(e,...t){return Object.isObject(e)&&t.flat(1/0).map(s=>Reflect.has(e,s)).every(s=>s)},ownDescriptors(e){let t={},s=()=>s.doIt?p.revert():"";if(s.doIt=!1,Object.isObject||(s.doIt=!0,p.apply()),!Object.isObject(e))return s(),{};let r=Reflect.ownKeys(e);for(let i of r)t[i]=Object.getOwnPropertyDescriptor(i);return s(),t},hasSome(e,...t){return Object.isObject(e)&&t.flat(1/0).map(s=>Reflect.has(e,s)).some(s=>s)},entries(e){return!e||typeof e!="object"?[]:Reflect.ownKeys(e).map(t=>[t,Object.getOwnPropertyDescriptor(e,t)])},values(e){return Reflect.entries.map(([,t])=>t)}});var y=new c(String,{isString(e){return e&&(typeof e=="string"||e instanceof String)?e.length>0:!1}});var v=new c(Symbol,{isSymbol(e){return e&&typeof e=="symbol"}});var D=new c(Array.prototype,{contains(e){return!!this.find(t=>t===e)},findEntry(e){let t=this.entries(),s=1;for(let r of t)if(e(r[s]))return r},get first(){return this[0]},get last(){return this[this.length-1]}});var S=p.patchEntries?.isObject?.computed,P=p.patchEntries?.isValidKey?.computed,G=y.patchEntries?.isString?.computed,w=b.patchEntries?.hasSome?.computed,T=class e{#t=e.enigmatic;constructor(t,s){if(this.#t=t,S(t)&&P(s)&&(this.#t=Object.getOwnPropertyDescriptor(t,s)),!this.isDescriptor)throw new Error("Not a valid descriptor:",this.#t)}get isAccessor(){return e.isAccessor(this.#t)}get isData(){return e.isData(this.#t)}get isDescriptor(){return e.isDescriptor(this.#t)}get configurable(){return!!this.#t?.configurable}set configurable(t){(this.#t||{}).configurable=!!t}get enumerable(){return this.#t?.enumerable}set enumerable(t){(this.#t||{}).enumerable=t}get writable(){return this.#t?.writable}set writable(t){(this.#t||{}).writable=t}get value(){return this.#t?.value}set value(t){(this.#t||{}).value=t}get get(){return this.#t?.get}set get(t){(this.#t||{}).get=t}get set(){return this.#t?.writable}set set(t){(this.#t||{}).set=t}static for(t,s){return!S(t)&&!P(s)?null:Object.getOwnPropertyDescriptor(t,s)}applyTo(t,s){if(!S(t)||!P(s))throw new Error("Cannot apply descriptor to non-object or invalid key");return Object.defineProperty(t,s,this.#t)}[Symbol.toPrimitive](t){switch(t){case"string":if(this.isAccessor){let s=Reflect.has(this.#t,"get")?"getter":"",r=Reflect.has(this.#t,"set")?"setter":"";return`Accessor (${s}${s&&r?", ":""}${r})`}else if(this.isData){let s=Reflect.has(this.#t,"value")?"value":"",r=Reflect.has(this.#t,"writable")?"writable":"";return`Data (${s}${s&&r?", ":""}${r})`}break;case"number":return NaN;default:return this.#t}}static getData(t,s){if(!S(t)||!G(s))return null;let r=e.all(t);if(r.has(s)){let i=r.get(s);if(e.isData(i))return i.value}}static getAccessor(t,s){if(!S(t))return null;let[r,i,n]=[0,1,2],o=[void 0,void 0,void 0],a=this.all(t),l=e.isDescriptor(t);if(a.has(s)||l){let u=l?t:a.get(s);if(e.isAccessor(u))return o[n]=a.object(s),o[r]=u?.get,o[i]=u?.set,Object.assign(o,{get(){this[r].bind(this[n])()},set(A){this[i].bind(this[n])(A)},get accessor(){return!0},get descriptor(){return u},get boundDescriptor(){return{...u,get:u.get?.bind(t),set:u.set?.bind(t)}}}),o}}static base(t=!1,s=!1){return{enumerable:t,configurable:s}}static accessor(t,s,{enumerable:r,configurable:i}=e.base()){return{get:t,set:s,enumerable:r,configurable:i}}static data(t,s=!0,{enumerable:r,configurable:i}=e.base()){return{value:t,enumerable:r,writable:s,configurable:i}}static isDescriptor(t){let s=[...e.SHARED_KEYS,...e.ACCESSOR_KEYS,...e.DATA_KEYS];return w(t,s)}static isData(t,s){let i=(typeof t=="object"||t instanceof Object)&&s instanceof String?e.for(t,s):t,{ACCESSOR_KEYS:n,DATA_KEYS:o}=this,a=!1;return w(i,n)?a=!1:w(i,o)&&(a=!0),a}static isAccessor(t,s){let i=t&&s&&(typeof t=="object"||t instanceof Object)&&(s instanceof String||typeof s=="symbol")?e.for(t,s):t,{ACCESSOR_KEYS:n,DATA_KEYS:o}=this,a=!1;return w(i,o)?a=!1:w(i,n)&&(a=!0),a}static get flexible(){return this.base(!0,!0)}static get enigmatic(){return this.base(!1,!0)}static get intrinsic(){return this.base(!1,!1)}static get transparent(){return this.base(!0,!1)}static get SHARED_KEYS(){return["configurable","enumerable"]}static get ACCESSOR_KEYS(){return["get","set"]}static get DATA_KEYS(){return["value","writable"]}},E=new m(T);var{isClass:V,isFunction:O}=d.patchEntries.isClass.computed,N=Symbol.for("nodejs.util.inspect.custom"),x=new c(globalThis,{asBigIntObject(e){let t={configurable:!0,enumerable:!1},s={value:e};return Object.defineProperties(s,{[Symbol.toPrimitive]:{value:function(){return e},...t},[Symbol.toStringTag]:{value:BigInt.name,...t},[Symbol.species]:{get(){return BigInt},...t},[N]:{...t,value(r,i,n){return n(this[Symbol.toPrimitive](),{...i,depth:r})}}}),Object.setPrototypeOf(s,BigInt.prototype),Reflect.ownKeys(BigInt.prototype).forEach(r=>{typeof s[r]=="function"&&(s[r]=function(...i){return BigInt.prototype[r].apply(this,i)}.bind(s.value))}),s},maskAs(e,t,s){let{prototype:r,toPrimitive:i}=GenericMask({...s,prototype:t}),n={configurable:!0,enumerable:!1},o=O(r)?r.prototype:r,a=V(r)?r:o?.constructor;return!a&&!o?null:(Object.setPrototypeOf(e,o),Object.defineProperties(e,{valueOf:{value(){return String(i("default",e))},...n},[Symbol.toPrimitive]:{value(l){return i(l,e)},...n},[Symbol.toStringTag]:{value:a.name,...n},[Symbol.species]:{get(){return a},...n},[N]:{...n,value(l,u,A){return A(this[Symbol.toPrimitive](),{...u,depth:l})}}}),e)},maskAsString(e,t,s){return e&&Reflect.has(e,t)?maskAs(e,StringMask(t??"value",s)):null},maskAsNumber(e,t,s){return e&&Reflect.has(e,t)?maskAs(e,NumberMask(t??"value",s)):null},GenericMask({prototype:e,targetKey:t="value",toPrimitive:s}){let r={targetKey:t,toPrimitive:s,prototype:e};return O(s)||(r.toPrimitive=(i,n)=>{let o=n[t],a=typeof o=="number"&&Number.isFinite(o)||typeof o=="string"&&!isNaN(parseFloat(o))&&isFinite(o);switch(i){case"string":return a?String(o):o??String(n);case"number":return a?Number(o):NaN;case"default":default:return a?Number(o):o}}),r},StringMask(e,t){let s={targetKey:e,toPrimitive:t,prototype:String.prototype};return O(t)||(s.toPrimitive=function(i,n){switch(i){case"default":return n[e];case"number":return parseInt(n[e],36);case"string":return String(n[e]);default:return n}}),s},NumberMask(e,t){let s={targetKey:e,toPrimitive:t,prototype:Number.prototype};return O(t)||(s.toPrimitive=function(i,n){switch(i){case"default":return n[e];case"number":return Number(n[e]);case"string":return String(n[e]);default:return n}}),s}});var $=[Object,Function,Reflect,String,Symbol,Array.prototype],j=[x,E];function U(e){let t=e||$;if(!t)throw new Error("Unable to enable features without owners list");t.forEach(s=>{c.enableFor(s)}),j.forEach(s=>{s.apply()})}function H(e){let t=e||$;if(!t)throw new Error("Unable to disable features without owners list");t.forEach(s=>{c.disableFor(s)}),j.forEach(s=>{s.revert()})}var W=[p,d,b,y,v,D,x,E].reduce((s,r)=>(Reflect.ownKeys(r.patchEntries).reduce((i,n)=>(s[n]=r.patchEntries[n].computed,s),s),s),{});return Y(J);})();
2
- //# sourceMappingURL=basic-extensions.bundle.1.4.0.js.map