All Projects → salesforce → Observable Membrane

salesforce / Observable Membrane

Licence: mit
A Javascript Membrane implementation using Proxies to observe mutation on an object graph

Programming Languages

typescript
32286 projects

Projects that are alternatives of or similar to Observable Membrane

Utility
Assign/Partial/ReadOnly/Proxy
Stars: ✭ 31 (-90.16%)
Mutual labels:  immutable, proxy
Mockserver
MockServer enables easy mocking of any system you integrate with via HTTP or HTTPS with clients written in Java, JavaScript and Ruby. MockServer also includes a proxy that introspects all proxied traffic including encrypted SSL traffic and supports Port Forwarding, Web Proxying (i.e. HTTP proxy), HTTPS Tunneling Proxying (using HTTP CONNECT) and…
Stars: ✭ 3,479 (+1004.44%)
Mutual labels:  proxy
Proxy Manager Bridge
Provides integration for ProxyManager with various Symfony components.
Stars: ✭ 274 (-13.02%)
Mutual labels:  proxy
Company Crawler
天眼查爬虫&企查查爬虫,指定关键字爬取公司信息
Stars: ✭ 285 (-9.52%)
Mutual labels:  proxy
Scrapy Crawlera
Crawlera middleware for Scrapy
Stars: ✭ 281 (-10.79%)
Mutual labels:  proxy
Aws Es Kibana
AWS ElasticSearch Kibana Proxy
Stars: ✭ 297 (-5.71%)
Mutual labels:  proxy
Cloudbunny
CloudBunny is a tool to capture the real IP of the server that uses a WAF as a proxy or protection. In this tool we used three search engines to search domain information: Shodan, Censys and Zoomeye.
Stars: ✭ 273 (-13.33%)
Mutual labels:  proxy
Macos Fortress
Firewall and Privatizing Proxy for Trackers, Attackers, Malware, Adware, and Spammers with Anti-Virus On-Demand and On-Access Scanning (PF, squid, privoxy, hphosts, dshield, emergingthreats, hostsfile, PAC file, clamav)
Stars: ✭ 307 (-2.54%)
Mutual labels:  proxy
Trojan Go
Go实现的Trojan代理,支持多路复用/路由功能/CDN中转/Shadowsocks混淆插件,多平台,无依赖。A Trojan proxy written in Go. An unidentifiable mechanism that helps you bypass GFW. https://p4gefau1t.github.io/trojan-go/
Stars: ✭ 4,049 (+1185.4%)
Mutual labels:  proxy
Socks5
A full-fledged high-performance socks5 proxy server written in C#. Plugin support included.
Stars: ✭ 286 (-9.21%)
Mutual labels:  proxy
Polychrome
🎨 Easy color manipulation in ~2kb (gzipped)
Stars: ✭ 286 (-9.21%)
Mutual labels:  immutable
Fpp
Functional PHP Preprocessor - Generate Immutable Data Types
Stars: ✭ 282 (-10.48%)
Mutual labels:  immutable
Kubetap
Kubectl plugin to interactively proxy Kubernetes Services with ease
Stars: ✭ 297 (-5.71%)
Mutual labels:  proxy
Httptunnel
Bidirectional data stream tunnelled in HTTP requests.
Stars: ✭ 279 (-11.43%)
Mutual labels:  proxy
Memoize State
The magic memoization for the State management. ✨🧠
Stars: ✭ 305 (-3.17%)
Mutual labels:  proxy
Spring Cloud Gateway
A Gateway built on Spring Framework 5.x and Spring Boot 2.x providing routing and more.
Stars: ✭ 3,305 (+949.21%)
Mutual labels:  proxy
Mosn
The Cloud-Native Network Proxy Platform.
Stars: ✭ 3,451 (+995.56%)
Mutual labels:  proxy
Lunnel
fast reverse-proxy
Stars: ✭ 293 (-6.98%)
Mutual labels:  proxy
Hetty
Hetty is an HTTP toolkit for security research.
Stars: ✭ 3,596 (+1041.59%)
Mutual labels:  proxy
Annon.api
Configurable API gateway that acts as a reverse proxy with a plugin system.
Stars: ✭ 306 (-2.86%)
Mutual labels:  proxy

Observable Membrane

Creating robust JavaScript code becomes increasingly important as web applications become more sophisticated. The dynamic nature of JavaScript code at runtime has always presented challenges for developers.

This package implements an observable membrane in JavaScript using Proxies.

A membrane can be created to observe access to a module graph, observe what the other part is attempting to do with certain objects, and even distort the way they see the module graph.

What is a Membrane

Use Cases

One of the prime use-cases for observable membranes is the popular @observed or @tracked decorator used in components to detect mutations on the state of the component to re-render the component when needed. In this case, any object value set into a decorated field can be wrapped into an observable membrane to monitor if the object is accessed during the rendering phase, and if so, the component must be re-rendered when mutations on the object value are detected. And this process is applied not only at the object value level, but at any level in the object graph accessible via the observed object value.

Additionally, it supports distorting objects within an object graph, which could be used for:

  • Avoid leaking certain references.
  • Values observed through the membrane can be different representations of the original values.

Usage

The following example illustrates how to create an observable membrane, and proxies:

import ObservableMembrane from 'observable-membrane';

const membrane = new ObservableMembrane();

const o = {
    x: 2,
    y: {
        z: 1
    },
};

const p = membrane.getProxy(o);

p.x;
// yields 2

p.y.z;
// yields 1

Note: If the value that you're accessing via the membrane is an object that can be observed, then the membrane will return a proxy around it. In the example above, o.y !== p.y because it is a proxy that applies the exact same mechanism. In other words, the membrane is applicable to an entire object graph.

Observing Access and Mutations

The most basic operation in an observable membrane is to observe property member access and mutations. For that, the constructor accepts an optional arguments options that accepts two callbacks, valueObserved and valueMutated:

import ObservableMembrane from 'observable-membrane';

const membrane = new ObservableMembrane({
    valueObserved(target, key) {
        // where target is the object that was accessed
        // and key is the key that was read
        console.log('accessed ', key);
    },
    valueMutated(target, key) {
        // where target is the object that was mutated
        // and key is the key that was mutated
        console.log('mutated ', key);
    },
});

const o = {
    x: 2,
    y: {
        z: 1
    },
};

const p = membrane.getProxy(o);

p.x;
// console output -> 'accessed x'
// yields 2

p.y.z;
// console output -> 'accessed z'
// yields 1

p.y.z = 3;
// console output -> 'mutated z'
// yields 3

Read Only Proxies

Another use-case for observable membranes is to prevent mutations in the object graph. For that, ObservableMembrane provides an additional method that gets a read-only version of any object value. One of the prime use-cases for read-only membranes is to hand over an object to another actor, observe how the actor uses that object reference, but prevent the actor from mutating the object. E.g.: passing an object property down to a child component that can consume the object value but not mutate it.

This is also a very cheap way of doing deep-freeze, although it is not exactly the same, but can cover a lot of ground without having to actually freeze the original object, or a copy of it:

import ObservableMembrane from 'observable-membrane';

const membrane = new ObservableMembrane({
    valueObserved(target, key) {
        // where target is the object that was accessed
        // and key is the key that was read
        console.log('accessed ', key);
    },
});

const o = {
    x: 2,
    y: {
        z: 1
    },
};

const r = membrane.getReadOnlyProxy(o);

r.x;
// yields 2

r.y.z;
// yields 1

r.y.z = 2;
// throws Error in dev-mode, and does nothing in production mode

Distortion

As described above, you can use distortions to avoid leaking non-observable objects and distorting values observed through the membrane:

import ObservableMembrane from 'observable-membrane';

const membrane = new ObservableMembrane({
    valueDistortion(value) {
        if (value instanceof Node) {
            throw new ReferenceError(`Invalid access to a non-observable Node`);
        }
        console.log('distorting ', value);
        if (value === 1) {
            return 10;
        }
        return value;
    },
});

const o = {
    x: 2,
    y: {
        z: 1,
        node: document.createElement('p'),
    },
};

const p = membrane.getProxy(o);

p.x;
// console output -> 'distorting 2'
// yields 2

p.y.z;
// console output -> 'distorting 1'
// yields 10

p.y.node;
// throws ReferenceError

Note: You could use a WeakMap to remap symbols to avoid leaking the original symbols and other non-observable objects through the distortion mechanism.

Unwrapping Proxies

For advanced usages, the observable membrane instance offers the ability to unwrap any proxy generated by the membrane. This can be used to detect membrane presence and other detections that may be useful to framework authors. E.g.:

import ObservableMembrane from 'observable-membrane';

const membrane = new ObservableMembrane();

const o = {
    x: 2,
    y: {
        z: 1,
    },
};

const p = membrane.getProxy(o);

o.y !== p.x;
// yields true because `p` is a proxy of `o`

o.y === membrane.unwrapProxy(p.y);
// yields true because `membrane.unwrapProxy(p.y)` returns the original target `o.y`

Observable Objects

This membrane implementation is tailored for what we call "observable objects", which are objects with basic functionalities that do not require identity to be preserved. Usually any object with the prototype chain set to Object.prototype or null. You can control what gets wrapped by implementing a custom callback for the valueIsObservable option:

import ObservableMembrane from 'observable-membrane';

const membrane = new ObservableMembrane({
    valueIsObservable(value) {
        // intentionally checking for null
        if (value === null) {
            return false;
        }

        // treat all non-object types, including undefined, as non-observable values
        if (typeof value !== 'object') {
            return false;
        }

        if (isArray(value)) {
            return true;
        }

        const proto = Reflect.getPrototypeOf(value);
        return (proto === Object.prototype || proto === null);
    },
});

const o = {
    x: 1
};
membrane.getProxy(o) !== o; // yields true because it was wrapped

membrane.getProxy(document) !== document; // yields false because a DOM object is not observable

Note: be very careful about customizing valueIsObservable. Any object with methods that requires identity (e.g.: objects with private fields, or methods accessing weakmaps with the this value as a key), will simply not work because those methods will be called with the this value being the actual proxy.

Example

There are runnable examples in this Git repository. You must build this package as described in the Contributing Guide before attempting to run the examples. Additionally, some of the examples might be relying on features that are not supported in all browsers (e.g.: reactivo-element example relies on Web Components APIs).

API

new ObservableMembrane([config])

Create a new membrane.

Parameters

  • config [Object] [Optional] The membrane configuration
    • valueObserved [Function] [Optional] Callback invoked when an observed property is accessed. This function receives as argument the original target and the property key.
    • valueMutated [Function] [Optional] Callback invoked when an observed property is mutated. This function receives as argument the original target and the property key.
    • valueDistortion [Function] [Optional] Callback to apply distortion to the objects present in the object graph. This function receives as argument a newly added object in the object graph.
    • valueIsObservable [Function] [Optional] Callback to determine whether or not a value qualifies as a proxy target for the membrane. The default implementation will only observe plain objects (objects with their prototype set to null or Object.prototype).
    • tagPropertyKey [PropertyKey] [Optional] A valid string or symbol that can be used to identify proxies created by the membrane. This is useful for any kind of debugging tools or object identity mechanism.

ObservableMembrane.prototype.getProxy(object)

Wrap an object in the membrane. If the object is observable it will return a proxified version of the object, otherwise it returns the original value.

Parameters

  • object [Object] The object to wrap in the membrane.

ObservableMembrane.prototype.getReadOnlyProxy(object)

Wrap an object in the read-only membrane. If the object is observable it will return a proxified version of the object, otherwise it returns the original value.

Parameters

  • object [Object] The object to wrap in the membrane.

ObservableMembrane.prototype.unwrapProxy(proxy)

Unwrap the proxified version of the object from the membrane and return it's original value.

Parameters

  • proxy [Object] Proxified object to unwrap from the membrane.

Limitations and Other Features

  • Not all objects can be wrapped by this membrane. More information about this in the examples above.
  • Distortion of Symbols and other built-in objects is possible via valueDistortion mechanism to avoid leaking internal.
  • The ability for the membrane creator to revoke all proxies within it to prevent further mutations to the underlying objects (aka, membrane shutdown switch) is not supported at the moment.
  • A value mutation that is set to a read-only proxy value is allowed, but the subtree will still be read-only, e.g.: const p = membrane.getProxy({}); p.abc = membrane.getReadOnlyProxy({}); p.abc.qwe = 1; will throw because the value assigned to abc is still read only.

Browser Compatibility

Observable membranes requires Proxy (ECMAScript 6) to be available.

Development State

This library is production ready, it has been used at Salesforce in production for over a year. It is very lightweight (~1k - minified and gzipped), that can be used with any framework or library. It is designed to be very performant.

Contribution

Please make sure to read the Contributing Guide before making a pull request.

License

MIT

Copyright (C) 2017 salesforce.com, Inc.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].