Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

multimix

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

multimix - npm Package Compare versions

Comparing version 5.0.0 to 6.0.0

lib/errors.js

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"
}
}

@@ -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".&nbsp;<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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc