@nejs/basic-extensions 2.20.0 → 2.21.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 (68) hide show
  1. package/bin/repl.basics.js +36 -6
  2. package/bin/repl.signature.js +63 -0
  3. package/dist/@nejs/basic-extensions.bundle.2.21.0.js +25 -0
  4. package/dist/@nejs/basic-extensions.bundle.2.21.0.js.map +7 -0
  5. package/dist/cjs/classes/descriptor.d.ts +1 -1
  6. package/dist/cjs/classes/enum.d.ts +37 -19
  7. package/dist/cjs/classes/enum.js +181 -56
  8. package/dist/cjs/classes/enum.js.map +1 -1
  9. package/dist/cjs/classes/iterable.d.ts +3 -3
  10. package/dist/cjs/classes/param.parser.d.ts +1 -7
  11. package/dist/cjs/classes/pluggable.proxy.d.ts +2 -1
  12. package/dist/cjs/classes/property.d.ts +2 -9
  13. package/dist/cjs/classes/refset.d.ts +2 -2
  14. package/dist/cjs/classes/symkeys.d.ts +1 -1
  15. package/dist/cjs/global.this.js +17 -7
  16. package/dist/cjs/global.this.js.map +1 -1
  17. package/dist/cjs/index.d.ts +8 -5
  18. package/dist/cjs/index.js +13 -8
  19. package/dist/cjs/index.js.map +1 -1
  20. package/dist/cjs/string.extensions.js +38 -0
  21. package/dist/cjs/string.extensions.js.map +1 -1
  22. package/dist/cjs/utils/copy.object.d.ts +2 -2
  23. package/dist/cjs/utils/descriptor.utils.d.ts +152 -0
  24. package/dist/cjs/utils/descriptor.utils.js +228 -9
  25. package/dist/cjs/utils/descriptor.utils.js.map +1 -1
  26. package/dist/cjs/utils/index.d.ts +15 -0
  27. package/dist/cjs/utils/index.js +9 -0
  28. package/dist/cjs/utils/index.js.map +1 -1
  29. package/dist/cjs/utils/stdout.d.ts +742 -0
  30. package/dist/cjs/utils/stdout.js +1042 -0
  31. package/dist/cjs/utils/stdout.js.map +1 -0
  32. package/dist/mjs/classes/descriptor.d.ts +1 -1
  33. package/dist/mjs/classes/enum.d.ts +37 -19
  34. package/dist/mjs/classes/enum.js +181 -57
  35. package/dist/mjs/classes/enum.js.map +1 -1
  36. package/dist/mjs/classes/iterable.d.ts +3 -3
  37. package/dist/mjs/classes/param.parser.d.ts +1 -7
  38. package/dist/mjs/classes/pluggable.proxy.d.ts +2 -1
  39. package/dist/mjs/classes/property.d.ts +2 -9
  40. package/dist/mjs/classes/refset.d.ts +2 -2
  41. package/dist/mjs/classes/symkeys.d.ts +1 -1
  42. package/dist/mjs/index.d.ts +8 -5
  43. package/dist/mjs/index.js +13 -8
  44. package/dist/mjs/index.js.map +1 -1
  45. package/dist/mjs/string.extensions.js +38 -0
  46. package/dist/mjs/string.extensions.js.map +1 -1
  47. package/dist/mjs/utils/copy.object.d.ts +2 -2
  48. package/dist/mjs/utils/descriptor.utils.d.ts +152 -0
  49. package/dist/mjs/utils/descriptor.utils.js +222 -9
  50. package/dist/mjs/utils/descriptor.utils.js.map +1 -1
  51. package/dist/mjs/utils/index.d.ts +15 -0
  52. package/dist/mjs/utils/index.js +10 -1
  53. package/dist/mjs/utils/index.js.map +1 -1
  54. package/dist/mjs/utils/stdout.d.ts +742 -0
  55. package/dist/mjs/utils/stdout.js +1037 -0
  56. package/dist/mjs/utils/stdout.js.map +1 -0
  57. package/package.json +6 -20
  58. package/repl.bootstrap.js +24 -16
  59. package/repl.history +30 -30
  60. package/src/classes/enum.js +249 -109
  61. package/src/index.js +22 -8
  62. package/src/string.extensions.js +41 -0
  63. package/src/utils/descriptor.utils.js +277 -9
  64. package/src/utils/index.js +20 -0
  65. package/src/utils/stdout.js +1151 -0
  66. package/tests/utils/descriptor.utils.test.js +130 -0
  67. package/dist/@nejs/basic-extensions.bundle.2.20.0.js +0 -19
  68. package/dist/@nejs/basic-extensions.bundle.2.20.0.js.map +0 -7
@@ -1,5 +1,5 @@
1
1
  import { Extension } from '@nejs/extension'
2
- import { accessor, as, data, isDescriptor } from '../utils/index.js'
2
+ import { accessor, as, data, isDescriptor, redescribe } from '../utils/index.js'
3
3
 
4
4
  /**
5
5
  * Creates an enumeration object with specified values and properties.
@@ -12,54 +12,32 @@ import { accessor, as, data, isDescriptor } from '../utils/index.js'
12
12
  * @returns {Object} The constructed enumeration object.
13
13
  *
14
14
  * @example
15
- * const colors = Enum('Colors', ['red', 'green', 'blue'])
16
- * console.log(colors.red) // EnumValue object for 'red'
17
- *
18
- * @description
19
- * The `Enum` function constructs an enumeration object with a given name,
20
- * values, and optional properties. It supports primitive types, arrays,
21
- * and objects as values. The function uses a combination of `Object.create`
22
- * and `Proxy` to define and manage the properties of the enumeration.
23
- *
24
- * The enumeration object includes:
25
- * - A `toString` method that returns the enumeration name.
26
- * - A `Symbol.toStringTag` for identifying the object as an 'Enum'.
27
- * - A `Symbol.for('Enum.name')` for storing the enumeration name.
28
- *
29
- * For array values, it creates a maker function that returns an
30
- * `EnumValue` object with properties like `real`, `value`, `type`,
31
- * `name`, and `compare`.
32
- */
15
+ * const colors = Enum('Colors', ['red', 'green', 'blue'])
16
+ * console.log(colors.red) // EnumValue object for 'red'
17
+ *
18
+ * @description
19
+ * The `Enum` function constructs an enumeration object with a given name,
20
+ * values, and optional properties. It supports primitive types, arrays,
21
+ * and objects as values. The function uses a combination of `Object.create`
22
+ * and `Proxy` to define and manage the properties of the enumeration.
23
+ *
24
+ * The enumeration object includes:
25
+ * - A `toString` method that returns the enumeration name.
26
+ * - A `Symbol.toStringTag` for identifying the object as an 'Enum'.
27
+ * - A `Symbol.for('Enum.name')` for storing the enumeration name.
28
+ *
29
+ * For array values, it creates a maker function that returns an
30
+ * `EnumValue` object with properties like `real`, `value`, `type`,
31
+ * `name`, and `compare`.
32
+ */
33
33
  export function Enum(name, values, properties) {
34
- const enumeration = Object.create({}, {
35
- [Symbol.toStringTag]: accessor('Enum', false, true, false),
36
- [Symbol.for('Enum.name')]: accessor(name, false, true, false),
37
- [Symbol.for('Enum.valueKeys')]: data([], false, true, false),
38
- [Symbol.for('nodejs.util.inspect.custom')]: data(
39
- function(depth, options, inspect) {
40
- const valueKeys = this[Symbol.for('Enum.valueKeys')] ?? []
41
- let valueText = valueKeys
42
- .map(key => `\x1b[1;2m${key}\x1b[22m`)
43
- .join(', ')
44
-
45
- if (valueText.length)
46
- valueText = ` { ${valueText} }`
47
-
48
- return `\x1b[1menum \x1b[22m${name}${valueText}`
49
- }, false, true, false
50
- ),
51
- toString: data(function() {
52
- return `Enum(${name})`
53
- }, false, true, false)
54
- })
55
-
56
- if (!Array.isArray(values)) {
57
- values = [values]
58
- }
59
-
60
- const asString = o => as.string(o, { description: true, stringTag: true })
61
-
62
- /**
34
+ const enumeration = makeBaseEnum(name)
35
+
36
+ if (!Array.isArray(values)) {
37
+ values = [values]
38
+ }
39
+
40
+ /**
63
41
  * A new base `EnumValue` type object. It contains enough custom symbols and
64
42
  * identifiers to allow things like a `compare(to)` function to also work on
65
43
  * each of the elements. Thing of this as the shared base functionality for
@@ -71,25 +49,25 @@ export function Enum(name, values, properties) {
71
49
  * custom {@link Symbol} keys. The `node.js` custom inspect symbol is also
72
50
  * defined for better REPL representation.
73
51
  */
74
- const makeEnumValue = (property, enumValue) => ({
75
- toString: data(() => enumValue, false, true, false),
76
-
77
- [Symbol.for('Enum.name')]: data(name, false, true, false),
78
- [Symbol.for('Enum.is')]: data(true, false, false, false),
79
- [Symbol.for('nodejs.util.inspect.custom')]: data(
80
- function(depth, options, inspect) {
81
- const _options = { ...(options || {}), colors: true }
82
- const _skip = this.value === Symbol.for('Enum.NonAssociatedValue')
83
- const _value = _skip
84
- ? ''
85
- : ` { value: ${inspect(this.value, _options) } }`;
86
-
87
- return `${property}${_value}`
88
- },
89
- false, true, false
90
- ),
91
- [Symbol.toStringTag]: accessor('EnumValue', false, true, false),
92
- [Symbol.for('compare')]: data(
52
+ const makeEnumValue = (property, enumValue) => ({
53
+ toString: data(() => enumValue, false, true, false),
54
+
55
+ [Symbol.for('Enum.name')]: data(name, false, true, false),
56
+ [Symbol.for('Enum.is')]: data(true, false, false, false),
57
+ [Symbol.for('nodejs.util.inspect.custom')]: data(
58
+ function(depth, options, inspect) {
59
+ const _options = { ...(options || {}), colors: true }
60
+ const _skip = this.value === Symbol.for('Enum.NonAssociatedValue')
61
+ const _value = _skip
62
+ ? ''
63
+ : ` { value: ${inspect(this.value, _options) } }`;
64
+
65
+ return `${property}${_value}`
66
+ },
67
+ false, true, false
68
+ ),
69
+ [Symbol.toStringTag]: accessor('EnumValue', false, true, false),
70
+ [Symbol.for('compare')]: data(
93
71
  function compareValue(to) {
94
72
  const toObj = (to && typeof to === 'object') ? to : { real: to }
95
73
  const kName = Symbol.for('Enum.name')
@@ -110,8 +88,8 @@ export function Enum(name, values, properties) {
110
88
  lName === rName && lType === rType &&
111
89
  lReal === rReal && lValue === rValue
112
90
  )
113
- }, false, true, false),
114
- [Symbol.toPrimitive]: data(
91
+ }, false, true, false),
92
+ [Symbol.toPrimitive]: data(
115
93
  function EnumValueToPrimitive(hint) {
116
94
  const original = this.real
117
95
  const type = typeof original
@@ -140,9 +118,9 @@ export function Enum(name, values, properties) {
140
118
  }
141
119
  },
142
120
  false, true, false),
143
- })
121
+ })
144
122
 
145
- /**
123
+ /**
146
124
  * Given a value, determine how to represent it as both a key and a response
147
125
  * or underlying original value. The method for this is dependent on the type
148
126
  * of the value itself.
@@ -151,7 +129,7 @@ export function Enum(name, values, properties) {
151
129
  * @returns {[string, any]} an array where the first value is the transformed
152
130
  * value as a key and the second element is the originally supplied value.
153
131
  */
154
- const fromPrimitive = (value) => {
132
+ const fromPrimitive = (value) => {
155
133
  let valueType = typeof value
156
134
 
157
135
  switch (valueType) {
@@ -173,17 +151,17 @@ export function Enum(name, values, properties) {
173
151
  return [str, str]
174
152
  }
175
153
  }
176
- }
154
+ }
177
155
 
178
- // Determine the keys that the final proxy should be aware of when computing
179
- // the enumeration value itself.
180
- const kValueProps = ['real', 'value', 'type', 'name', 'compare', 'isEnum']
181
- const kCustomPropKeys = []
156
+ // Determine the keys that the final proxy should be aware of when computing
157
+ // the enumeration value itself.
158
+ const kValueProps = ['real', 'value', 'type', 'name', 'compare', 'isEnum']
159
+ const kCustomPropKeys = []
182
160
 
183
- // Capture and calculate any custom properties defined for each element
184
- // of the enumeration. Each custom property is appended to `kCustomPropKeys`
185
- const props = {}
186
- if (properties) {
161
+ // Capture and calculate any custom properties defined for each element
162
+ // of the enumeration. Each custom property is appended to `kCustomPropKeys`
163
+ const props = {}
164
+ if (properties) {
187
165
  if (Array.isArray(properties)) {
188
166
  const entries = properties.filter(e => Array.isArray(e) && e.length === 2)
189
167
 
@@ -234,14 +212,13 @@ export function Enum(name, values, properties) {
234
212
  props[property] = value
235
213
  }
236
214
  }
237
- }
215
+ }
238
216
 
239
- for (const value of values) {
217
+ for (const value of values) {
240
218
  const valueType = Array.isArray(value) ? 'array' : typeof value
241
219
 
242
220
  let property = undefined
243
221
  let response = undefined
244
- let makeNew = undefined
245
222
  let wasArray = false
246
223
  let elements = value
247
224
 
@@ -258,31 +235,31 @@ export function Enum(name, values, properties) {
258
235
 
259
236
  const maker = {
260
237
  [property](initialValue) {
261
- const storage = new Map()
262
- const key = property
263
- const realValue = accessor(response, false, { storage, key })
264
-
265
- let _opts, associatedValue;
266
-
267
- if (wasArray) {
268
- _opts = { storage, key: key + '.associated' }
269
- associatedValue = elements.length === 1
270
- ? accessor(initialValue, true, _opts)
271
- : accessor(elements?.[1], elements?.[2], _opts);
272
- }
273
- else
238
+ const storage = new Map()
239
+ const key = property
240
+ const realValue = accessor(response, false, { storage, key })
241
+
242
+ let _opts, associatedValue;
243
+
244
+ if (wasArray) {
245
+ _opts = { storage, key: key + '.associated' }
246
+ associatedValue = elements.length === 1
247
+ ? accessor(initialValue, true, _opts)
248
+ : accessor(elements?.[1], elements?.[2], _opts);
249
+ }
250
+ else
274
251
  associatedValue = accessor(
275
252
  Symbol.for('Enum.NonAssociatedValue'),
276
253
  false, false, false)
277
254
 
278
- const _prop = Object(asString(response))
279
- const valueProps = [...kValueProps, ...kCustomPropKeys]
280
- const enumResponse = Object.create(_prop, {
255
+ const _prop = Object(asString(response))
256
+ const valueProps = [...kValueProps, ...kCustomPropKeys]
257
+ const enumResponse = Object.create(_prop, {
281
258
  ...makeEnumValue(property, response),
282
259
  ...props,
283
- })
260
+ })
284
261
 
285
- const proxy = new Proxy(_prop, {
262
+ const proxy = new Proxy(_prop, {
286
263
  get(target, _property, receiver) {
287
264
  if (_property === 'real')
288
265
  return realValue.get()
@@ -317,12 +294,12 @@ export function Enum(name, values, properties) {
317
294
 
318
295
  return false
319
296
  }
320
- })
297
+ })
321
298
 
322
- Object.setPrototypeOf(proxy, Object.getPrototypeOf(_prop))
323
- Object.setPrototypeOf(enumResponse, proxy)
299
+ Object.setPrototypeOf(proxy, Object.getPrototypeOf(_prop))
300
+ Object.setPrototypeOf(enumResponse, proxy)
324
301
 
325
- return enumResponse
302
+ return enumResponse
326
303
  }
327
304
  }
328
305
 
@@ -336,9 +313,172 @@ export function Enum(name, values, properties) {
336
313
  }
337
314
 
338
315
  Object.defineProperty(enumeration, property, data(dataValue, baseProps))
339
- }
316
+ }
317
+
318
+ return enumeration
319
+ }
320
+
321
+ export const EnumExtension = new Extension(Enum)
322
+
323
+
324
+
325
+ /**
326
+ * Converts a given value to a string representation with additional
327
+ * options for description and string tag.
328
+ *
329
+ * @param {any} value - The value to be converted to a string. This can
330
+ * be of any type, including objects, arrays, numbers, etc.
331
+ * @returns {string} A string representation of the input value, enhanced
332
+ * with a description and string tag if applicable.
333
+ *
334
+ * @example
335
+ * // Convert a number to a string with additional options
336
+ * const result = asString(123)
337
+ * console.log(result) // Outputs: "123" with description and string tag
338
+ *
339
+ * @example
340
+ * // Convert an object to a string with additional options
341
+ * const obj = { key: 'value' }
342
+ * const result = asString(obj)
343
+ * console.log(result) // Outputs: "[object Object]" with description and
344
+ * // string tag
345
+ */
346
+ function asString(value) {
347
+ return as.string(value, { description: true, stringTag: true })
348
+ }
340
349
 
341
- return enumeration
350
+ /**
351
+ * Creates a base enumeration object with predefined properties and
352
+ * symbols. This function is used to initialize an enumeration with
353
+ * specific characteristics, such as a name and various symbolic
354
+ * properties that enhance its functionality and identification.
355
+ *
356
+ * @param {string} name - The name of the enumeration. This name is
357
+ * used to identify the enumeration and is stored as a symbol on
358
+ * the object.
359
+ * @returns {Object} A new enumeration object with specific properties
360
+ * and symbols set for enhanced functionality and identification.
361
+ *
362
+ * @example
363
+ * // Create a base enum with the name 'Color'
364
+ * const colorEnum = makeBaseEnum('Color')
365
+ * console.log(colorEnum[Symbol.for('Enum.name')]) // Outputs: 'Color'
366
+ */
367
+ export function makeBaseEnum(name) {
368
+ return Object.create({}, {
369
+ /**
370
+ * Defines the `toStringTag` symbol on each enumeration to allow for
371
+ * type identification and to be consistent in naming conventions.
372
+ *
373
+ * @type {string}
374
+ */
375
+ [Symbol.toStringTag]: accessor('Enum', false, true, false),
376
+
377
+ /**
378
+ * In addition to the `toStringTag` symbol which defines this enumeration
379
+ * as an Enum type, the name of the enum is also codified in as a symbol
380
+ * on the object. It can be found using `Symbol.for('Enum.name')`.
381
+ *
382
+ * @type {string|symbol|number}
383
+ */
384
+ [Symbol.for('Enum.name')]: accessor(name, false, true, false),
385
+
386
+ /**
387
+ * Knowing which keys of the enum are part of the keys themselves is also
388
+ * crucial for enumerations. These can always be found on each Enum type
389
+ * as `Symbol.for('Enum.valueKeys')` as an array, but can also be found
390
+ * as the `.keys` property if there is no enum value of that name.
391
+ */
392
+ [Symbol.for('Enum.valueKeys')]: data([], false, true, false),
393
+
394
+ /**
395
+ * For users of the node.js REPL, this symbol represents enum types in a
396
+ * more readily readable format. See the docs for node's `util.inspect()`
397
+ * function for more details.
398
+ *
399
+ * @type {(number, object, Function) => string}
400
+ */
401
+ [Symbol.for('nodejs.util.inspect.custom')]: data(
402
+ function(depth, options, inspect) {
403
+ const valueKeys = this[Symbol.for('Enum.valueKeys')] ?? []
404
+ let valueText = valueKeys
405
+ .map(key => `\x1b[1;2m${key}\x1b[22m`)
406
+ .join(', ')
407
+
408
+ if (valueText.length)
409
+ valueText = ` { ${valueText} }`
410
+
411
+ return `\x1b[1menum \x1b[22m${name}${valueText}`
412
+ }, false, true, false
413
+ ),
414
+
415
+ /**
416
+ * This function checks the supplied `possibleEnumValue` to see if it
417
+ * is an enum value type from this enum.
418
+ *
419
+ * @param {any} possibleEnumValue the value to evaluate
420
+ * @returns {boolean} returns `true` if the value is an enum value type
421
+ * from this `Enum`, irrespective of any associated value. Returns `false`
422
+ * otherwise.
423
+ */
424
+ isValueOf: data(function isValueOf(possibleEnumValue) {
425
+ if (!possibleEnumValue || typeof possibleEnumValue !== 'object')
426
+ return false
427
+
428
+ const enumValueType = possibleEnumValue?.type
429
+ const enumValueName = possibleEnumValue?.name
430
+
431
+ const thisEnumName = this[Symbol.for('Enum.name')]
432
+ const thisEnumKeys = this[Symbol.for('Enum.valueKeys')]
433
+
434
+ return (
435
+ enumValueType === thisEnumName &&
436
+ thisEnumKeys.includes(enumValueName)
437
+ )
438
+ }, false, true, false),
439
+
440
+ /**
441
+ * A simple overload of the `toString()` method to represent the enum
442
+ * more identifiably when called. The result will be the word enum with
443
+ * the name of the enum in parenthesis. So a Gender enum might be seen
444
+ * as `Enum(Gender)` as a result to calling this function.
445
+ *
446
+ * @returns {string} a {@link String} representation of this object.
447
+ */
448
+ toString: data(function toString() {
449
+ return `Enum(${name})`
450
+ }, false, true, false)
451
+ })
452
+
453
+ const applySyntacticSugar = () => {
454
+ createSymlinks(Symbol.for('Enum.valueKeys'), 'keys')
455
+ createSymlinks(Symbol.for('Enum.name'), 'name')
456
+ }
457
+
458
+ return [base, applySyntacticSugar]
342
459
  }
343
460
 
344
- export const EnumExtension = new Extension(Enum)
461
+ /**
462
+ * Creates a symlink for a property from an existing key to a new key on
463
+ * the specified object. This function checks if the new key already
464
+ * exists on the object and, if not, it creates a new property with the
465
+ * same descriptor as the old key.
466
+ *
467
+ * Since this method creates the new link through the use of property
468
+ * descriptors, computed getters and setters also are mapped properly.
469
+ *
470
+ * @param {Object} on - The target object on which the symlink is to be
471
+ * created.
472
+ * @param {string|symbol} oldKey - The existing key whose property
473
+ * descriptor will be used for the new key.
474
+ * @param {string|symbol} newKey - The new key under which the property
475
+ * will be accessible.
476
+ *
477
+ * @example
478
+ * const obj = { a: 1 }
479
+ * createSymlink(obj, 'a', 'b')
480
+ * console.log(obj.b) // Outputs: 1
481
+ */
482
+ function createSymlinks(on, oldKey, ...newKeys) {
483
+ redescribe(on, oldKey, {}, { alsoAs: newKeys })
484
+ }
package/src/index.js CHANGED
@@ -29,6 +29,15 @@ import { RefSetExtensions } from './classes/refset.js'
29
29
  import { SymkeysExtension } from './classes/symkeys.js'
30
30
  import { TypeExtensions } from './classes/type.js'
31
31
 
32
+ export * from './utils/stdout.js'
33
+ import {
34
+ StringConsole,
35
+ StdoutGlobalPatches,
36
+ StringConsoleExtension,
37
+
38
+ captureStdout,
39
+ } from './utils/stdout.js'
40
+
32
41
  export * from './utils/copy.object.js'
33
42
  export * from './utils/toolkit.js'
34
43
  export * from './utils/descriptor.utils.js'
@@ -68,6 +77,9 @@ const InstancePatches = [
68
77
  const Patches = new Map([
69
78
  ...StaticPatches,
70
79
  ...InstancePatches,
80
+
81
+ [globalThis, GlobalFunctionsAndProps, 'globalThis'], // Missing .name property
82
+ [globalThis, StdoutGlobalPatches, 'globalThis'], // Missing .name property
71
83
  ])
72
84
 
73
85
  const Extensions = {
@@ -85,6 +97,7 @@ const Extensions = {
85
97
  [PropertyExtensions.key]: PropertyExtensions,
86
98
  [RefMapExtensions.key]: RefMapExtensions,
87
99
  [RefSetExtensions.key]: RefSetExtensions,
100
+ [StringConsoleExtension.key]: StringConsoleExtension,
88
101
  [SymkeysExtension.key]: SymkeysExtension,
89
102
  [TypeExtensions.key]: TypeExtensions,
90
103
  }
@@ -121,7 +134,6 @@ Object.assign(Controls, {
121
134
 
122
135
  enableExtensions() {
123
136
  Object.values(Extensions).forEach((extension) => { extension.apply() })
124
- GlobalFunctionsAndProps.apply()
125
137
  },
126
138
 
127
139
  disableAll() {
@@ -147,7 +159,6 @@ Object.assign(Controls, {
147
159
 
148
160
  disableExtensions() {
149
161
  Object.values(Extensions).forEach((extension) => { extension.revert() })
150
- GlobalFunctionsAndProps.revert()
151
162
  },
152
163
  })
153
164
 
@@ -193,7 +204,9 @@ export const all = (() => {
193
204
  .reduce(entriesReducer, dest.classes)
194
205
  )
195
206
 
196
- for (const [key, entry] of GlobalFunctionsAndProps) {
207
+ const globals = [...GlobalFunctionsAndProps].concat([...StdoutGlobalPatches])
208
+
209
+ for (const [key, entry] of globals) {
197
210
  const descriptor = new Descriptor(entry.descriptor, entry.owner)
198
211
  Object.defineProperty(dest.global, key, descriptor.toObject(true))
199
212
  }
@@ -203,15 +216,16 @@ export const all = (() => {
203
216
 
204
217
  const results = {
205
218
  ...Controls,
219
+ all,
220
+ Controls,
206
221
  Extensions,
207
- Patches,
222
+ extensions: Extensions,
208
223
  GlobalFunctionsAndProps,
209
- StaticPatches,
210
224
  InstancePatches,
211
- Controls,
212
- extensions: Extensions,
225
+ Patches,
213
226
  patches: Patches,
214
- all,
227
+ StaticPatches,
228
+ StdoutGlobalPatches,
215
229
  }
216
230
 
217
231
  export default results
@@ -723,6 +723,47 @@ export const StringPrototypeExtensions = new Patch(String.prototype, {
723
723
  };
724
724
  }
725
725
  },
726
+
727
+ /**
728
+ * Prints the string representation of the current object using a specified
729
+ * function and context.
730
+ *
731
+ * This method converts the current object to a string and then prints it
732
+ * using the provided function. By default, it uses `console.log` to print
733
+ * to the console. The function is called with a specified `thisArg` to
734
+ * set the context in which the function is executed.
735
+ *
736
+ * @param {Function} [fn=console.log] - The function used to print the
737
+ * string. Defaults to `console.log`.
738
+ * @param {Object} [thisArg=console] - The value of `this` provided for
739
+ * the call to `fn`. Defaults to the global `console` object.
740
+ *
741
+ * @returns {string} The string representation of the current object.
742
+ *
743
+ * @example
744
+ * // assuming extensions are applied
745
+ * const str = 'Hello, World!'
746
+ * str.print() // Logs: 'Hello, World!' to the console
747
+ *
748
+ * @example
749
+ * // assuming extensions are applied
750
+ * const str = 'Hello, World!'
751
+ * const customLogger = {
752
+ * time: new Date(),
753
+ * log: (msg) => {
754
+ * console.info('[%s]: %s', this.time..toLocaleDateString(), msg)
755
+ * }
756
+ * }
757
+ * str.print(customLogger.log, customLogger)
758
+ * // prints "[10/16/2024] Hello, World!"
759
+ */
760
+ print(fn = console.log, thisArg = console) {
761
+ const string = this.toString()
762
+
763
+ fn.call(thisArg, string)
764
+
765
+ return string
766
+ },
726
767
  },
727
768
  })
728
769