Comparing version 5.0.0 to 6.0.0
308
lib/main.js
(function() { | ||
'use strict'; | ||
var Multimix, module_keywords, | ||
indexOf = [].indexOf; | ||
var E, GUY, H, alert, debug, echo, freeze, get, get_types, help, hide, info, inspect, iterator_symbol, log, multimix_symbol, nameit, node_inspect, nosuchvalue, plain, praise, rpr, rvr, stringtag_symbol, truth, urge, warn, whisper; | ||
//########################################################################################################### | ||
GUY = require('guy'); | ||
({alert, debug, help, info, plain, praise, urge, warn, whisper} = GUY.trm.get_loggers('MULTIMIX')); | ||
({rpr, inspect, echo, log} = GUY.trm); | ||
({get, hide} = GUY.props); | ||
({freeze} = GUY.lft); | ||
rvr = GUY.trm.reverse; | ||
truth = GUY.trm.truth.bind(GUY.trm); | ||
node_inspect = Symbol.for('nodejs.util.inspect.custom'); | ||
nameit = function(name, f) { | ||
return Object.defineProperty(f, 'name', { | ||
value: name | ||
}); | ||
}; | ||
H = {}; | ||
E = require('./errors'); | ||
multimix_symbol = Symbol('multimix'); | ||
stringtag_symbol = Symbol.toStringTag; | ||
iterator_symbol = Symbol.iterator; | ||
nosuchvalue = Symbol('nosuchvalue'); | ||
//=========================================================================================================== | ||
// MODULE METACLASS provides static methods `@extend()`, `@include()` | ||
//----------------------------------------------------------------------------------------------------------- | ||
/* The little dance around the module_keywords variable is to ensure we have callback support when mixins | ||
extend a class. See https://arcturo.github.io/library/coffeescript/03_classes.html */ | ||
//----------------------------------------------------------------------------------------------------------- | ||
module_keywords = ['extended', 'included']; | ||
get_types = function() { | ||
var Intertype, R, types; | ||
if ((R = H.types) != null) { | ||
return R; | ||
} | ||
//--------------------------------------------------------------------------------------------------------- | ||
({Intertype} = require('intertype')); | ||
types = new Intertype(); | ||
//--------------------------------------------------------------------------------------------------------- | ||
types.declare.hdg_new_hedge_cfg({ | ||
$handler: 'function', | ||
$hub: 'optional.function.or.object', | ||
// $state: 'optional.object' | ||
$create: 'boolean.or.function', | ||
$strict: 'boolean', | ||
$oneshot: 'boolean', | ||
$deletion: 'boolean', | ||
$hide: 'boolean', | ||
extras: false, | ||
default: { | ||
hub: null, | ||
handler: null, | ||
// state: null | ||
create: null, | ||
strict: false, | ||
oneshot: false, | ||
deletion: true, | ||
hide: true | ||
} | ||
}); | ||
//--------------------------------------------------------------------------------------------------------- | ||
return types; | ||
}; | ||
Multimix = (function() { | ||
//=========================================================================================================== | ||
//=========================================================================================================== | ||
this.Multimix = (function() { | ||
class Multimix { | ||
//--------------------------------------------------------------------------------------------------------- | ||
static extend(object, settings = null) { | ||
var key, ref, value; | ||
settings = {...{ | ||
overwrite: true | ||
}, ...(settings != null ? settings : null)}; | ||
for (key in object) { | ||
value = object[key]; | ||
if (!(indexOf.call(module_keywords, key) < 0)) { | ||
continue; | ||
} | ||
if ((!settings.overwrite) && ((this.prototype[key] != null) || (this[key] != null))) { | ||
throw new Error(`^multimix/include@5684 overwrite set to false but name already set: ${JSON.stringify(key)}`); | ||
} | ||
this[key] = value; | ||
constructor(cfg) { | ||
var R, clasz, descriptor, key, mmx, ref; | ||
/* TAINT bug in Intertype::create() / Intertype::validate(), returns `true` instead of input value */ | ||
// cfg = create.hdg_new_hedge_cfg cfg | ||
//....................................................................................................... | ||
/* TAINT temporary code to avoid faulty `Intertype::validate` */ | ||
/* NOTE use `create` when `validate` is fixed */ | ||
/* TAINT circular dependency Intertype <--> GUY.props.Hedge ??? */ | ||
mmx = this; | ||
hide(mmx, 'types', get_types()); | ||
cfg = {...cfg}; | ||
if (cfg.hub == null) { | ||
cfg.hub = mmx; | ||
} | ||
if ((ref = object.extended) != null) { | ||
ref.apply(this); | ||
if (cfg.create == null) { | ||
cfg.create = !cfg.strict; | ||
} | ||
return this; | ||
} | ||
//--------------------------------------------------------------------------------------------------------- | ||
static include(object, settings = null) { | ||
var key, ref, value; | ||
settings = {...{ | ||
overwrite: true | ||
}, ...(settings != null ? settings : null)}; | ||
for (key in object) { | ||
value = object[key]; | ||
if (!(indexOf.call(module_keywords, key) < 0)) { | ||
cfg = {...mmx.types.isa.hdg_new_hedge_cfg.default, ...cfg}; | ||
clasz = mmx.constructor; | ||
if (!mmx.types.isa.function(cfg.handler)) { | ||
throw new E.Multimix_cfg_error('^mmx.ctor@1^', `need handler, got ${rpr(cfg.handler)}`); | ||
} | ||
if (!mmx.types.isa.boolean.or.function(cfg.create)) { | ||
throw new E.Multimix_cfg_error('^mmx.ctor@2^', `expected boolean or function, got ${rpr(cfg.create)}`); | ||
} | ||
if (!mmx.types.isa.boolean(cfg.strict)) { | ||
throw new E.Multimix_cfg_error('^mmx.ctor@3^', `expected boolean, got ${rpr(cfg.strict)}`); | ||
} | ||
if (cfg.strict && (cfg.create !== false)) { | ||
throw new E.Multimix_cfg_error('^mmx.ctor@4^', "cannot set both `create` and `strict`"); | ||
} | ||
if (!mmx.types.isa.boolean(cfg.oneshot)) { | ||
throw new E.Multimix_cfg_error('^mmx.ctor@5^', `expected boolean, got ${rpr(cfg.oneshot)}`); | ||
} | ||
for (key in mmx.types.isa.hdg_new_hedge_cfg.default) { | ||
//....................................................................................................... | ||
mmx[key] = cfg[key]; | ||
} | ||
//....................................................................................................... | ||
/* set `mmx.state` to a value shared by all Multimix instances with the same `hub`: */ | ||
(mmx.state = clasz.states.get(mmx.hub)); | ||
if (mmx.state === void 0) { | ||
clasz.states.set(mmx.hub, mmx.state = structuredClone(clasz.state)); | ||
} | ||
//....................................................................................................... | ||
R = mmx._get_proxy(true, mmx, (...P) => { | ||
return mmx.handler.call(mmx.hub, [], ...P); | ||
}); | ||
ref = Object.getOwnPropertyDescriptors(this.handler); | ||
for (key in ref) { | ||
descriptor = ref[key]; | ||
if (key === 'length') { | ||
continue; | ||
} | ||
if ((!settings.overwrite) && ((this.prototype[key] != null) || (this[key] != null))) { | ||
throw new Error(`^multimix/include@5683 overwrite set to false but name already set: ${JSON.stringify(key)}`); | ||
if (key === 'prototype') { | ||
continue; | ||
} | ||
// Assign properties to the prototype | ||
this.prototype[key] = value; | ||
Object.defineProperty(R, key, descriptor); | ||
} | ||
if ((ref = object.included) != null) { | ||
ref.apply(this); | ||
} | ||
return this; | ||
} | ||
//--------------------------------------------------------------------------------------------------------- | ||
export(target = null) { | ||
/* Return an object with methods, bound to the current instance. */ | ||
var R, k, ref, ref1, v; | ||
R = target != null ? target : {}; | ||
ref = (require('./cataloguing')).walk_all_keys_of(this); | ||
for (k of ref) { | ||
v = this[k]; | ||
if ((v != null ? v.bind : void 0) == null) { | ||
R[k] = v; | ||
} else if ((ref1 = v[Multimix.isa_keymethod_proxy]) != null ? ref1 : false) { | ||
R[k] = Multimix.get_keymethod_proxy(this, v); | ||
} else { | ||
R[k] = v.bind(this); | ||
} | ||
} | ||
return R; | ||
@@ -82,31 +142,88 @@ } | ||
//--------------------------------------------------------------------------------------------------------- | ||
get_my_prototype() { | ||
return Object.getPrototypeOf(Object.getPrototypeOf(this)); | ||
} | ||
//--------------------------------------------------------------------------------------------------------- | ||
new(...P) { | ||
return new this.constructor(...P); | ||
} | ||
//========================================================================================================= | ||
// KEYMETHOD FACTORY | ||
//--------------------------------------------------------------------------------------------------------- | ||
static get_keymethod_proxy(bind_target, f) { | ||
var R; | ||
R = new Proxy(f.bind(bind_target), { | ||
get: function(target, key) { | ||
if (key === 'bind') { // ... other properties ... | ||
return target[key]; | ||
_get_proxy(is_top, mmx, handler) { | ||
var R, clasz, dsc; | ||
clasz = this.constructor; | ||
dsc = { | ||
//----------------------------------------------------------------------------------------------------- | ||
get: (target, key) => { | ||
var R, hedges, proxy; | ||
// debug '^43453^', { target, key, mmx_hub: mmx.hub, } | ||
switch (key) { | ||
case multimix_symbol: | ||
return mmx; | ||
case stringtag_symbol: | ||
return `${target.constructor.name}`; | ||
case 'constructor': | ||
return target.constructor; | ||
case 'toString': | ||
return target.toString; | ||
case 'call': | ||
return target.call; | ||
case 'apply': | ||
return target.apply; | ||
case iterator_symbol: | ||
return target[Symbol.iterator]; | ||
case node_inspect: | ||
return target[node_inspect]; | ||
/* NOTE necessitated by behavior of `node:util.inspect()`: */ | ||
case '0': | ||
return target[0]; | ||
} | ||
if ((typeof key) === 'symbol') { | ||
//................................................................................................... | ||
if (is_top) { | ||
this.state.hedges = [key]; | ||
} else { | ||
this.state.hedges.push(key); | ||
} | ||
dsc.apply = (target, self, P) => { | ||
return this.handler.call(self, [...this.state.hedges], ...P); | ||
}; | ||
if ((R = get(target, key, nosuchvalue)) !== nosuchvalue) { | ||
//................................................................................................... | ||
// @handler @state.hedges ### put call for prop access here ### | ||
return R; | ||
} | ||
if (this.strict) { | ||
throw new E.Multimix_no_such_property('^mmx.proxy.get@1^', key); | ||
} | ||
if (this.create === false) { | ||
return void 0; | ||
} | ||
hedges = [...this.state.hedges]; | ||
if (this.create === true) { | ||
handler = this.handler; | ||
} else { | ||
this.create(key, target); | ||
return target[key]; | ||
} | ||
return function(...xP) { | ||
return target(key, ...xP); | ||
}; | ||
//................................................................................................... | ||
proxy = this._get_proxy(false, mmx, nameit(key, (...P) => { | ||
R = handler.call(mmx.hub, hedges, ...P); | ||
this.state.hedges = []; | ||
return R; | ||
})); | ||
if (this.hide) { | ||
hide(target, key, proxy); | ||
} else { | ||
target[key] = proxy; | ||
} | ||
return proxy; | ||
}, | ||
//----------------------------------------------------------------------------------------------------- | ||
set: (target, key, value) => { | ||
if (this.oneshot && (get(target, key, nosuchvalue)) !== nosuchvalue) { | ||
throw new E.Multimix_reassignment_error('^mmx.proxy.set@1^', key); | ||
} | ||
return target[key] = value; | ||
}, | ||
//----------------------------------------------------------------------------------------------------- | ||
deleteProperty: (target, key) => { | ||
if (!this.deletion) { | ||
throw new E.Multimix_deletion_error('^mmx.proxy.set@1^', key); | ||
} | ||
return delete target[key]; | ||
} | ||
}); | ||
R[Multimix.isa_keymethod_proxy] = true; | ||
return R; | ||
}; | ||
//....................................................................................................... | ||
return R = new Proxy(handler, dsc); | ||
} | ||
@@ -116,6 +233,10 @@ | ||
//========================================================================================================= | ||
// @js_type_of = ( x ) -> return ( ( Object::toString.call x ).slice 8, -1 ).toLowerCase() | ||
Multimix.isa_keymethod_proxy = Symbol('proxy'); | ||
Multimix.symbol = multimix_symbol; | ||
Multimix.states = new WeakMap(); | ||
Multimix.state = GUY.lft.freeze({ | ||
hedges: [] | ||
}); | ||
return Multimix; | ||
@@ -125,7 +246,4 @@ | ||
//########################################################################################################### | ||
module.exports = Multimix; | ||
}).call(this); | ||
//# sourceMappingURL=main.js.map |
{ | ||
"name": "multimix", | ||
"version": "5.0.0", | ||
"description": "flexible object copying", | ||
"version": "6.0.0", | ||
"description": "Objects with auto-generated property chains", | ||
"main": "lib/main.js", | ||
"scripts": { | ||
"test": "use https://github.com/loveencounterflow/hengist" | ||
}, | ||
"repository": { | ||
@@ -19,9 +16,14 @@ "type": "git", | ||
"multimix", | ||
"oop", | ||
"inheritance", | ||
"traits", | ||
"mixin" | ||
"hedges", | ||
"properties" | ||
], | ||
"author": "loveencounterflow", | ||
"license": "MIT" | ||
} | ||
"license": "MIT", | ||
"dependencies": { | ||
"guy": "11.8.0", | ||
"intertype": "^10.1.0" | ||
}, | ||
"scripts": { | ||
"test": "use https://github.com/loveencounterflow/hengist/dev/multimix" | ||
} | ||
} |
181
README.md
@@ -0,1 +1,4 @@ | ||
# MultiMix | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
@@ -6,7 +9,4 @@ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
- [MultiMix](#multimix) | ||
- [Usage](#usage) | ||
- [Methods](#methods) | ||
- [Links](#links) | ||
- [Motivation](#motivation) | ||
- [To Do](#to-do) | ||
- [Is Done](#is-done) | ||
@@ -21,145 +21,70 @@ <!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
An ES6 `class` with some metaprogramming capabilities: | ||
* Objects with auto-generated property chains | ||
* easy to mixin instance methods from an arbitrary number of objects; | ||
* easy to mixin static (class) methods from an arbitrary number of objects; | ||
* sample implementation for a kind of 'keymethod proxies' (essentially instance method with custum special | ||
behavior); | ||
* ability to 'export' an object with methods bound to a particular instance (great in conjunction with ES6 | ||
object destructuring). | ||
* class `Hedge` | ||
* purpose: enable turning property access to function calls | ||
* propoerties may be predefined | ||
* or auto-generated, either | ||
* as plain objects | ||
* or by calling a custom factory function | ||
* example: | ||
Implementation was inspired by / copy-pasted from [Chapter 3 of *The Little Book on | ||
CoffeeScript*](https://arcturo.github.io/library/coffeescript/03_classes.html). | ||
```coffee | ||
handler = ( hedges, a, b, c ) -> | ||
log hedges, [ a, b, c, ] | ||
return null | ||
h = new Hedge { handler, } | ||
h.foo | ||
# [ 'foo', ] | ||
``` | ||
## Usage | ||
* since hubs and properties are proxies, can do things on property access, no call needed, so both `d.foo` | ||
and `d.foo 42` can potentially do things | ||
Have a look at [the | ||
demo](https://github.com/loveencounterflow/multimix/blob/master/src/experiments/demo.coffee): | ||
* `class X extends Multimix` gives you a class with the following static methods inherited through | ||
`Multimix`: | ||
* `<class>.extend: ( object, settings = null ) ->`—extends class with the properties of `object` | ||
(class-level mixin). | ||
* `<class>.include: ( object, settings = null ) ->`—extends instances with the properties of `object` | ||
(instance-level mixin). | ||
* `<class>.get_keymethod_proxy = ( bind_target, f ) ->`—produces an instance method `f` which will | ||
translate calls from immediate attributes (as in, `f.some_text some_value`) to calls to `f` proper, | ||
using the attribute name as first argument: `f some_text, some_value`. I needed this for a specific | ||
purpose and included the code as a demo how to implement such a thing. | ||
* `export: ( target = null ) ->`—when called on an instance, returns an object with bound instance | ||
methods; this allows to 'export' instance methods into a namespace without fearing 'JavaScript method | ||
tear-off symptome': | ||
* `cfg`: | ||
```coffee | ||
my_instance = new My_class | ||
{ method_a | ||
method_b } = my_instance.export_methods() | ||
# now you can use `method_a`, `method_b` without prefixing them with `my_instance`: | ||
method_a 42 | ||
``` | ||
* `cfg.handler`: mandatory property; function to be called on prop access, call, or both | ||
* `d = new Multimix { handler, }` returns the handler wrapped into a proxy | ||
* the `Multimix` instance is accessible as `d[Multimix.symbol]`. `Multimix.symbol` is a private symbol | ||
and thus guaranteed not to overwrite or shadow an existing property | ||
* existing properties of `handler` will be returned | ||
* non-existant properties of `handler` will be auto-generated on first access; these will be functions | ||
that, when called with any number of arguments `f P...`, will in turn call `handler props, P...` | ||
* `handler` will be called in the context of `hub` where given; otherwise, its context will be the | ||
`Multimix` instance. | ||
When argument `target` is given, methods will be attached on that object (overwriting existing ones). | ||
* `hub`: optional reference / base object (re 'hub': as if props were spokes) | ||
* When `extend()` or `include()` are used with `settings` as ` { overwrite: false, }`, an error will | ||
be raised during class definition time when any name clashes are detected. | ||
* `cfg.create`: | ||
* `true` (default): missing props will be auto-generated as functions that call `handler` in the context | ||
of `cfg.hub` where given (or else the `Multimix` instance) | ||
* `false`: no missing props will be generated | ||
* a function: to be called as `create key, target` when a new property is first accessed; this function | ||
may or may not create a new property as seen fit. The MultiMix proxy will, at any rate, return | ||
`target[ key ]` which may or may not be `undefined`. | ||
Code: | ||
* `cfg.strict`: (default `false`) if set to `true`, trying to access an unset property will cause an | ||
error. This setting is only valid when used in conjunction with `create: false`. | ||
```coffee | ||
Multimix = require 'multimix' | ||
* `cfg.oneshot`: (default `false`) if set to `true`, trying to re-assign any value to an existing property | ||
will cause an error | ||
#========================================================================================================= | ||
# SAMPLE OBJECTS WITH INSTANCE METHODS, STATIC METHODS | ||
#--------------------------------------------------------------------------------------------------------- | ||
object_with_class_properties = | ||
find: ( id ) -> info "class method 'find()'", ( k for k of @ ) | ||
create: ( attrs ) -> info "class method 'create()'", ( k for k of @ ) | ||
* `cfg.deletion`: (default `true`) if set to `false`, trying to delete any property will cause an error | ||
#--------------------------------------------------------------------------------------------------------- | ||
object_with_instance_properties = | ||
save: -> info "instance method 'save()'", ( k for k of @ ) | ||
* `cfg.hide`: (default `true`) if set to `true`, will make auto-generated properties non-enumerable so | ||
they don't show up in console output | ||
#========================================================================================================= | ||
# CLASS DECLARATION | ||
#--------------------------------------------------------------------------------------------------------- | ||
isa = ( type, xP... ) -> | ||
### NOTE realistic method should throw error when `type` not in `specs` ### | ||
urge "µ1129 object #{rpr @instance_name} isa #{rpr type} called with #{rpr xP}" | ||
urge "µ1129 my @specs: #{rpr @specs}" | ||
urge "µ1129 spec for type #{rpr type}: #{rpr @specs[ type ]}" | ||
## To Do | ||
#--------------------------------------------------------------------------------------------------------- | ||
class Intertype extends Multimix | ||
@extend object_with_class_properties | ||
@include object_with_instance_properties | ||
* **[–]** documentation | ||
#------------------------------------------------------------------------------------------------------- | ||
constructor: ( @instance_name ) -> | ||
super() | ||
@specs = {} | ||
@declare type, value for type, value of @constructor.base_types | ||
@isa = Multimix.get_keymethod_proxy @, isa | ||
## Is Done | ||
#------------------------------------------------------------------------------------------------------- | ||
declare: ( type, value ) -> | ||
whisper 'µ7474', 'declare', type, rpr value | ||
@specs[ type ] = value | ||
* **[+]** `cfg.strict` | ||
* **[+]** `cfg.oneshot` | ||
* **[+]** `cfg.deletion` | ||
#------------------------------------------------------------------------------------------------------- | ||
@base_types = | ||
foo: 'spec for type foo' | ||
bar: 'spec for type bar' | ||
########################################################################################################## | ||
intertype_1 = new Intertype | ||
intertype_2 = new Intertype | ||
info 'µ002-1', Intertype.base_types | ||
info 'µ002-2', intertype_1.declare 'new_on_it1', 'a new hope' | ||
info 'µ002-3', 'intertype_1.specs', intertype_1.specs | ||
info 'µ002-4', 'intertype_2.specs', intertype_2.specs | ||
info 'µ002-5', intertype_1.isa 'new_on_it1', 1, 2, 3 | ||
info 'µ002-6', intertype_1.isa.new_on_it1 1, 2, 3 | ||
info 'µ002-7', intertype_2.isa 'new_on_it1', 1, 2, 3 | ||
info 'µ002-8', intertype_2.isa.new_on_it1 1, 2, 3 | ||
{ isa, declare, } = intertype_1.export_methods() | ||
info 'µ002-9', isa 'new_on_it1', 1, 2, 3 | ||
info 'µ002-10', isa.new_on_it1 1, 2, 3 | ||
``` | ||
### Methods | ||
* **`@new ( P... ) ->`**—instances of derivatives of MultiMix have a method `new()` that returns a new | ||
instances of the same class; `j = i.new settings` is just a shortcut for the not-so-obviously correct | ||
incantation `j = new i.constructor settings`. Sine the standard use case for classes derived from | ||
`Multimix` is one-per-module libraries (that may or may not need or allow to be configured), it is | ||
practical to have a standardized way to produce new library instances where called for. | ||
## Links | ||
* [jeremyckahn/inherit-by-proxy.js](https://gist.github.com/jeremyckahn/5552373) | ||
* [JS Objects: Distractions](https://davidwalsh.name/javascript-objects-distractions) | ||
* [JS Objects: De"construct"ion](https://davidwalsh.name/javascript-objects-deconstruction) | ||
## Motivation | ||
"JavaScript's prototypal inheritance is vastly simpler than class-based, 'classical' OOP". <sup>*[citation | ||
needed]*</sup> | ||
[Is it](https://davidwalsh.name/javascript-objects-deconstruction)? | ||
![](https://raw.githubusercontent.com/loveencounterflow/multimix/master/artwork/JavaScriptObjects--Full.png) | ||
## To Do | ||
* [X] implement `get_my_prototype()` | ||
* [ ] document `get_my_prototype()` (allows to access methods of protoype even from methods that were | ||
defined in a mixin object) | ||
* [ ] can we use fat-arrow defs as in `method: ( x ) => ...` to produce bound methods? Should this become | ||
standard, to be validated (at instantiation time)? | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
42980
259
2
89
1
+ Addedguy@11.8.0
+ Addedintertype@^10.1.0
+ Addedacorn@8.14.0(transitive)
+ Addedacorn-loose@8.4.0(transitive)
+ Addedacorn-walk@8.3.4(transitive)
+ Addedansi-regex@5.0.1(transitive)
+ Addedastring@1.9.0(transitive)
+ Addedemoji-regex@8.0.0(transitive)
+ Addedguy@11.6.011.8.0(transitive)
+ Addedintertype@10.1.4(transitive)
+ Addedintertype-legacy@7.7.1(transitive)
+ Addedis-fullwidth-code-point@3.0.0(transitive)
+ Addedletsfreezethat@3.1.0(transitive)
+ Addedmultimix@5.0.0(transitive)
+ Addedrfdc@1.4.1(transitive)
+ Addedstring-width@4.2.3(transitive)
+ Addedstrip-ansi@6.0.1(transitive)
+ Addedto-width@1.2.0(transitive)
+ Addedvarsize-string@2.2.2(transitive)
+ Addedwcsize@1.0.0(transitive)
+ Addedwcstring@2.1.1(transitive)