Skip to content

WebReflection/proxy-pants

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

88 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

proxy-pants

build status Coverage Status CSP strict

Social Media Photo by lan deng on Unsplash

Secured and reliable Proxy based utilities for more or less common tasks:

  • accessor to trap one or more accessors for any object
  • applier & caller to trap any borrowed callback/utility without needing to use .call or .apply to pass the context
  • bound to bind one or more methods all at once
  • bread & crumbs to track operations through paths (i.e. a.b.c.d) and namespaces
  • cache to compute once any accessed property through a proxied, and secured, map
  • chain to trap once all inherited descriptors down the prototypal chain and automatically ensure the right accessor or method
  • dsm to virtually trap dataset / *set accessors as DOMStringMap like references per each element. Please note this utility is not secured
  • extender to extend any object through weakly referenced behaviors, providing a new way to deal with state machines too, through the following features:
    • methods are always the same bound reference
    • properties are defined per extender and never directly attached to the source
    • accessors are also defined per each extender
    • multiple extenders calls to the same source preserve previous state, and any source can pass through multiple extenders without ever conflicting
  • fetch to shortcut fetch(url).json and other methods as direct accessors, defaulting to void when the response is not ok
  • own to destructure only own properties
  • secure to ensure local classes cannot be patched at runtime down their prototypal chain
  • watcher the good old Object.prototype.watch and unwatch methods to simplify selective reactive state handling
  • weak-cache same as cache but the returned reference is weakly retained
  • weak-proxy same as Proxy but it's register to the finalization registry so that if the handler contains a collected(target) callback, that will be invoked once the proxy reference is gone

accessor

Trap one or more accessors for any object.

// import {accessor} from 'proxy-pants/accessor';
import {accessor} from 'proxy-pants';

const {textContent} = accessor(document.body);

// get the current body text
textContent();

// set the new one
textContent('proxy pants!');

applier & caller

Trap any borrowed callback/utility without needing to use .call or .apply to pass the context.

// import {applier, caller} from 'proxy-pants/function';
import {applier, caller} from 'proxy-pants';

const {hasOwnProperty, toString} = caller(Object.prototype);

// true
hasOwnProperty({any: 'object'}, 'any');

// [object Null]
toString(null);

const {fromCharCode} = applier(String);

const charCodes = (...args) => fromCharCode(null, args);
// <=>
charCodes(60, 61, 62);

bound

Bind one or more methods all at once.

// import {bound} from 'proxy-pants/bound';
import {bound} from 'proxy-pants';

const map = new Map;
const {get, set, has} = bound(map);

// false
has('some');

// the map
set('some', 'value');

// true
has('some');

// 'value'
get('some');

bread & crumbs

Track operations through paths (i.e. a.b.c.d) and namespaces.

// import {bread, crumbs} from 'proxy-pants/breadcrumbs';
import {bread, crumbs} from 'proxy-pants';

const namespace = {
  some: 'value',
  method(...args) {
    return this.some + args.length;
  },
  Class: class {}
};

const facade = crumbs({
  apply(path, args) {
    return bread(namespace, path)(...args);
  },
  construct(path, args) {
    const Class = bread(namespace, path);
    return new Class;
  },
  get(path, key) {
    return bread(namespace, path)[key];
  },
  has(path, key) {
    return key in bread(namespace, path);
  },
  set(path, key, value) {
    bread(namespace, path)[key] = value;
    return true;
  },
  // alias for deleteProperty(path, key) {}
  delete(path, key) {
    return delete bread(namespace, path)[key];
  }
});

facade.some;            // value
facade.method(1, 2, 3); // some3
new facade.Class;       // [object Namespace]
'some' in facade;       // true
facade.test = 'ok';
facade.test;            // ok
delete facade.test;     // true

cache

A secured Map wrapper to retrieve any property once, through the given callback.

// import {cache} from 'proxy-pants/cache';
import {cache} from 'proxy-pants';

const uids = cache((name) => (name + Math.random()));

uids.a;             // "a0.23456787654"
uids.b;             // "b0.87654334567"
uids.a === uids.a;  // true
'a' in uids;        // true
delete uids.a;      // true
'a' in uids;        // false

chain

Trap once all inherited descriptors down the prototypal chain and automatically ensure the right accessor or method.

// import {chain} from 'proxy-pants/chain';
import {chain} from 'proxy-pants';

const asNode = chain(Node);
const asElement = chain(Element);

asNode(document.createTextNode('accessor')).data;
asElement(document.body).querySelector('method');

dsm

Virtually trap dataset / *set accessors as DOMStringMap like references per each element.

// import {dsm} from 'proxy-pants/dsm';
import {dsm} from 'proxy-pants';

const {ngset: ng, vset: v} = dsm(element);

// set ng-value attribute
ng.value = 123;

// remove ng-some-thing attribute
delete ng.someThing;

// logs v-if attribute, if any
console.log(v.if);

extender

Extend any object through weakly referenced behaviors, providing a new way to deal with state machines too, through the following features:

  • methods are always the same bound reference
  • properties are defined per extender and never directly attached to the source
  • accessors are also defined per each extender
  • multiple extenders calls to the same source preserve previous state, and any source can pass through multiple extenders without ever conflicting
// import {extender} from 'proxy-pants/extender';
import {extender} from 'proxy-pants';

const Magic = extender({
  // properties are per extender and weakly related
  isMagic: true,

  // accessors context is the original source/target
  get magic() {
    // this === source
    return Magic(this).isMagic;
  },

  // methods are always same bound method reference (except init)
  hasMagic() {
    // this === source
    return $(this).magic;
  },

  // a special method that helps setting up any reference once
  // bear in mind Magic(ref).init() won't be defined as own method
  init() {
    // this is implicitly invoked only the first time Magic(this) is used
  }
});

// it can be simplified per module as ...
const $ = Magic;

const source = {};
const target = Magic(source);

target.isMagic;         // true
target.magic;           // true
target.hasMagic();      // true

// introspection
Magic.extends(source);  // true
Magic.extends(target);  // true

fetch

Shortcut for fetch(url).json or any other Response method as direct accessor.

import {fetch} from 'proxy-pants';

const data = await fetch(restAIP).json!.data;
const text = await fetch(page).text || '';

own

Destructure only own properties.

// import {own} from 'proxy-pants/own';
import {own} from 'proxy-pants';

const created = Object.create(
  {inherited: true},
  {prop: {value: true}}
);

const {inherited, prop} = own(created);

console.assert(!inherited);
console.assert(prop);

secure

Ensure local classes cannot be patched at runtime down their prototypal chain.

// import {secure} from 'proxy-pants/secure';
import {secure} from 'proxy-pants';

const {
  Map,
  WekMap
} = secure(globalThis);

// both instances now can be used without
// possible issues down the prototypal chain
const map = new Map;
const wm = new WeakMap;

watcher

The good old Object.prototype.watch and unwatch methods to simplify selective reactive state handling.

// import {watcher} from 'proxy-pants/watcher';
import {watcher} from 'proxy-pants';

const watched = watcher({a: {b: 1}});
watched.watch('c', function (prop, oldVal, newVal) {
  prop;     // 'c'
  oldVal;   // undefined
  newVal;   // 2
  this.unwatch(prop); // this is the proxied target
});
watched.c = 2;

watched.a.watch('b', console.log);
watched.a.b = 3;  // will log

weak-cache

A secured WeakValue wrapper to retrieve any property once, through the given callback, and reteain the result weakly.

// import {wcache} from 'proxy-pants/wcache';
import {wcache} from 'proxy-pants';

// the value has to be an object/reference
const uids = wcache((name) => new String((name + Math.random())));

uids.a;             // new String("a0.23456787654")
uids.b;             // new String("b0.87654334567")
uids.a === uids.a;  // true
'a' in uids;        // true
delete uids.a;      // true
'a' in uids;        // false

weak-proxy

Same proxy constructor but with automatic registration that will invoke a collected(target) callback once that happens.

// import {WeakProxy} from 'proxy-pants/weak-proxy';
import {WeakProxy} from 'proxy-pants';

let ref = new WeakProxy(globalThis, {
  collected(ref) {
    console.assert(ref === globalThis);
  }
});

// whenever `ref` is collected
ref = null;
// the assertion in the `collected` handler will be true