GithubHelp home page GithubHelp logo

proxy-polyfill's Introduction

Please note that this polyfill is now in maintenance mode, as of Jan, 2023. We are not planning to add more features or enhancements.


This is a polyfill for the Proxy object, part of ES6. See the MDN docs or Introducing ES2015 Proxies for more information on Proxy itself. Unlike other polyfills, this does not require Object.observe, which is no longer supported anywhere.

⚠️ Note that Firefox, Chrome, Safari 10+ and Edge support Proxy natively. You don't need this if you're only targeting these modern browsers.

The polyfill supports just a limited number of proxy 'traps'. It also works by calling seal on the object passed to Proxy. This means that the properties you want to proxy must be known at creation time.

Additionally, your objects' prototypes will be snapshotted at the time a proxy is created. The properties of your objects can still change - you're just unable to define new ones. For example, proxying unrestricted dictionaries is not a good use-case for this polyfill.

Currently, the following traps are supported-

  • get
  • set
  • apply
  • construct

The Proxy.revocable method is also supported, but only for calls to the above traps.

This has no external dependencies. Skip down to usage to get started.

Example

The most compelling use case for Proxy is to provide change notifications.

function observe(o, callback) {
  return new Proxy(o, {
    set(target, property, value) {
      callback(property, value);
      target[property] = value;
    },
  });
}

const x = {'name': 'BB-8'};
const p = observe(x, (property, value) => console.info(property, value));
p.name = 'BB-9';
// name BB-9

You can extend this to generate change notifications for anywhere in an object tree-

function observe(o, callback) {
  function buildProxy(prefix, o) {
    return new Proxy(o, {
      set(target, property, value) {
        // same as above, but add prefix
        callback(prefix + property, value);
        target[property] = value;
      },
      get(target, property) {
        // return a new proxy if possible, add to prefix
        const out = target[property];
        if (out instanceof Object) {
          return buildProxy(prefix + property + '.', out);
        }
        return out;  // primitive, ignore
      },
    });
  }

  return buildProxy('', o);
}

const x = {'model': {name: 'LEAF'}};
const p = observe(x, (property, value) => console.info(property, value));
p.model.name = 'Tesla';
// model.name Tesla

Adding new properties

The following line will fail (with a TypeError in strict mode) with the polyfill, as it's unable to intercept new properties-

p.model.year = 2016;  // error in polyfill

However, you can replace the entire object at once - once you access it again, your code will see the proxied version.

p.model = {name: 'Falcon', year: 2016};
// model Object {name: "Falcon", year: 2016}

For a similar reason, this polyfill can't proxy Array objects very well - but you can replace them all at once.

Usage

Install via your favourite package manager as proxy-polyfill.

To polyfill Proxy everywhere

You should either:

  • include proxy-polyfill into your build system (just require it directly, it doesn't export anything), or
  • import the proxy.min.js file directly.

This is the recommended approach and works on the web, in Node, or React Native.

Do not import ./src/proxy.js in old browsers directly, as it uses modern JavaScript features. It needs to be compiled, which is why we have the proxy.min.js file.

To consume the polyfill as a function

This is an advanced pattern. Requires ./src/proxy.js, which exports a proxy polyfill builder function in commonJS.

// commonJS require
const proxyPolyfill = require('proxy-polyfill/src/proxy')();

// Your environment may also support transparent rewriting of commonJS to ES6:
import ProxyPolyfillBuilder from 'proxy-polyfill/src/proxy';
const proxyPolyfill = ProxyPolyfillBuilder();

// Then use...
const myProxy = new proxyPolyfill(...);

Support

The polyfill supports browsers that implement the full ES5 spec, such as IE9+ and Safari 6+. It may work in other non-browser environments too.

proxy-polyfill's People

Contributors

alexanderwallin avatar bramus avatar ealush avatar elliotnb avatar exe-boss avatar gund avatar machour avatar matthewp avatar richardspence avatar samthor avatar schweinepriester avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

proxy-polyfill's Issues

Class properties not proxied

Been using the proxy-polyfill for a while now and I'm really happy with it. Thanks for making it 🎉

I've encountered only one issue so far and that is that classes don't seem to be proxied correctly.

Example:

const ProxyFactory = require("proxy-polyfill/src/proxy");
const PProxy = ProxyFactory();

function observe(o, callback) {
  return new Proxy(o, {
    set(target, property, value) {
      callback(property, value);
      target[property] = value;
      return true;
    },
  });
}

class Foo {
  foo = "bar";
}

const f = new Foo();
observe(f, (prop, value) => {
  console.log("prop: " + prop, "value: " + value);
});

f.foo = "foobar";
// observer callback is never executed

There don't seem to be any setters attached to Foo.foo.

Proxy.revocable, revoke function holds reference to target, can't be garbage collected

Since revoke is used to make proxy unusable, and loosing reference to proxy:

const revocable = Proxy.revocable(target, handler);
revocable.revoke();
revocable.proxy = null;

should mean that target is free to be garbage collected.

But revoke has a reference to target
and needs to be nulled too

revocable.revoke = null;

I you aware of this behavior? I could not find any reference to correct behavior?

ES5 version

Why is it not compiled to ES5?
Safari 9 is complaining "let" key word.
Most of js libs will do es5 as the ultimate result of transpile.
I know dealing with cross browser issue is quite annoying, but can Google be the most professional in the world at least? Feel free to regard this as a complaining letter.

IE11 recognizes Proxy of an Array as a plain Object, not an Array

If you run the following code in Chrome:

var foo = [1,2,3];
var bar = new Proxy(foo,{});
console.log(Array.isArray(bar));
console.log(JSON.stringify(bar));

then it will log this to the console:

true
[1,2,3]

However, if you run that same code in IE11 with proxy-polyfill, then it will log this:

false
{"0":1,"1":2,"2":3}

Is this an issue that can be resolved or is it just an ES5 limitation?

Can not set prototype properties in IE10

Since IE10 does not support setPrototypeOf, prototype properties are copied and arenot writable.
As a workaround I changed
Object.defineProperty(proxy, k, {get: getter.bind(target, k)});
to
Object.defineProperty(proxy, k, {get: getter.bind(target, k)}, set: setter.bind(target, k));

any reason why you don't allow setting prototype properties?

deleteProperty

Hi,

Will there be any support soon for the deleteProperty trap?

Basic "get" not working

I'm trying to get this to work on ReactNative, using version =0.1.6.
Here is the Proxy call:

const example = new Proxy({}, { get: (target, name) => name in target ? target[name] : new Set() });

Unfortunately, that doesn't give me a set when I call example.anyPropertyName

I'm trying to work through the source code, but It's a bit above me. Any suggestions on how to get through it?

"Can't find variable: self"

Just got this with 1.7.0 when used in react-native.

Don't have too much info as I've downgraded to 1.6.0.

Exception thrown and not caught

Environment: Ubuntu 18.04, React 16.6.1.
Problem: Project will not render in IE11.
Expected behavior:
Proxy-polyfill takes care of the error: 'Proxy' is undefined but then throws Exception thrown and not caught
I've tried to trace it and comment out the source but it still persists. I've never tried to debug in IE and I'd love some help if anyone has any tips. This has been a full day of hell so far.

image

Better support for arrays

It's possible that we could implement a limited polyfill for array types that doesn't require them to be sealed (i.e., made immutable).

TypeError: 'set' on proxy: trap returned falsish for property

Hello awesome polly! Got an error not sure if it is me but it only happens when I use strict;. I am not adding a new path just editing existing path. If it is a bug lets see if we can fix it. If it is me slap me in the face but tell me what I am doing wrong please.

error: TypeError: 'set' on proxy: trap returned falsish for property 'port'

Example

'use strict'; // this makes the error throw

function ObserveObject(object, callback) {
    function createProxy(prefix, object) {
        return new Proxy(object, {
            set: function(target, property, value) {
                target[property] = value;
                callback(prefix + property, value);
            },
            get: function(target, property) {
                var value = target[property];

                if (isObject(value)) return createProxy(prefix + property + '.', value); 
                else return value; 
            }
        });
    }

    return createProxy('', object);
}

function isObject(value) {
    if (value === null || value === undefined) return false;
    else return value.constructor === Object;
}

var o = {
    hello: 'blue',
    app: {
        env: {
            port: 7777,
            la: 1
        }
    },
    more: 'stuff'
};

var m = ObserveObject (o, function(path, value) {
    console.log(path);
    console.log(value);
});

m.app.env.port = 8888;
console.log(m);

TypeError: Proxy polyfill does not support trap 'has'

I ran my Cordova APP on a 'vivox6s' cell phone and got this error.

I import this lib in the index.html like:

<!DOCTYPE html>
<html lang="en">

<head>
  <!-- <meta http-equiv="Content-Security-Policy" content="default-src *; style-src 'self' http://* 'unsafe-inline'; script-src 'self' http://* 'unsafe-inline' 'unsafe-eval'"
  /> -->
  <!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;"> -->
  <!-- <meta http-equiv="Content-Security-Policy" content="default-src https://codepush.azurewebsites.net 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *"
  /> -->
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">
  <!--<meta name="viewport" content="viewport-fit=cover, user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">-->
  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
  <script src="./polyfill.min.js"></script>
  <script src="./proxy.min.js"></script>
  <script src="./fastclick.js"></script>

And the ./polyfill.min.js comes from babel-polyfill, version 6.26.0, by the way.

Looking for some help or guide, because I can't see any related solution about this issue on Google.

Using `Proxy` across realms

The polyfill uses instanceof extensibly to detect object and function instances, but this fails when creating proxies for objects from another document. At first glance, it seems that using typeof might be enough.

Empty proxy object returned when target is empty object

I'm using a third-party library that uses Proxy and I'm attempting to use your polyfill in IE11.

They're using it like this:

  return new Proxy(
    {},
    {
      get: (_, propKey): unknown => {
        if (isAppRoute(router)) {
          switch (propKey) {
            case 'query':
              return getRouteQuery(router, args);
            case 'mutation':
              return getRouteQuery(router, args);
            case 'useQuery':
              return getRouteUseQuery(router, args);
            case 'useInfiniteQuery':
              return getRouteUseInfiniteQuery(router, args);
            case 'useQueries':
              return getRouteUseQueries(router, args);
            case 'useMutation':
              return getRouteUseMutation(router, args);
            default:
              throw new Error(`Unknown method called on ${String(propKey)}`);
          }
        } else {
          const subRouter = router[propKey as string];

          return createNewProxy(subRouter, args);
        }
      },
    }
  );

Source: https://github.com/ts-rest/ts-rest/blob/main/libs/ts-rest/react-query/src/lib/react-query.ts#L344

So they pass {} as the target argument.

When I use your polyfill, I get an empty Proxy object back. Can someone please help with why this is the case? Does your polyfill support this type of usage?

Without the polyfill, this usage works fine in modern browsers.

Reflect polyfill possible?

This polyfill does not appear to include the Reflect API methods for the supported traps. Even if the implementation is generally trivial, for the sake of compatibility and/or completeness, should this polyfill do so? (That is, define Reflect.get/set/etc.?)

Make me able to import the polyfill as an object, not assign to window.

React and other libraries likely have issues with that this proxy polyfill is semi-functional (sealed).

I would really appreciate if I was able to just import this package and use it where I know it's necessary, instead of applying it to the window in every case.

React 16.3.0-alpha.0 fixes this issue, but as Proxy becomes more popular, it is likely to appear in other libraries sooner or later.

PR: facebook/react@80d6792
Issue: facebook/react#12011

throw TypeError in strict mode if set trap doesn't return true

See MDN.

This code will work inside the calling code, but that's not really what we want:

let strictMode = (function() { return !this; })();

For eager contributors: I want to point out that we need to work out whether the caller is in strict mode (not the proxy code itself). This is possible under some very limited circumstances, but not always. I suspect this issue is unsolvable.

callable traps

If the handler/traps is a function rather than a map of traps, then native implementations will return a callable proxy that calls that handler (with this set the proxied object).

Chrome will do this regardless of the proxied object, Firefox will only do this if the proxied object is callable (throwing a TypeError).

How to use it in angular 4 project

I am using a library called "builder-pattern" that uses es6 proxy in my angular 4 project. And I added this javascript file(import "../node_modules/proxy-polyfill/src/index.js") in the polyfill.ts file. But IE 11 still throws an error. Is there any one who used this library in angular 2+ successfully. If so please help?

IE11 doesn't support template literals

When using this on IE11 it throws on one of your error:

throw new TypeError(`Cannot perform '${trap}' on a proxy that has been revoked`);

Bundling your library with "production" environment produces the following error:

Error: vendor.js from UglifyJs
Unexpected token: name (lastRevokeFn) [vendor.js:57606,6]

Support web workers

This line only works in Node.js and a Browser Window:

})(typeof module !== 'undefined' && module['exports'] ? global : window);

Easy enough to fix, might as well run the tests in a Worker while we're at it. Going to try adding this myself.

Object is not extensible when using minified version

When using minified version, I get a an error 'Object is not extensible' when trying to push or splice to an array.... if I Array.isArray() from console, the result is false..... strange.....

If I switch back to non minified, the issue goes away.

Anyone else noticed this?

Object doesn't support property or method with IE11 (at least)

Hi,

This piece of code does not work on IE11 (using this polyfill) but works with the official Proxy javascript object.

var x = {};
var p = new Proxy(x, {
  get: function(target, method) {
    return function(){
        // I  apply method "method" to another object ...
        return anotherObject[method].apply(this, arguments);
    }
  },
});

p.hello();

The polyfill returns "SCRIPT438: Object doesn't support property or method « hello »" where the official Proxy object correctly forwards the call to the "get" trap (with Chrome, FF, ...).

If I create the hello method within the x object, it works :

// var x = {};
var x = {hello: function(){ return 'initial hello'} };
var p = ...

But I would like to avoid to declare all of he methods inside the proxy service.

Could you help me ?
Thx a lot

Add ability to ignore unsupported traps

It would be nice if there were a way to configure proxy-polyfill to ignore unknown traps rather than throwing a TypeError if one is encountered. This might allow some library using it to mostly or even fully function in the event the unhandled trap is never used.

Proxy polyfill calls get after typecheck error in ie11

I am creating safe navigation in angular project using Proxy.

I included proxy.min.js in my angular 7 project (in scripts of angular.json).

Now proxy object is no more unknown in IE11, but the get method for dot operator of object is called only if the property is defined.

let a = {
         b: {
            d: {
               f: 0
            }
         },
         c: {
            d:10
         },
         g: 'Hello'
      }
if(a.b.c.d){
  console.log('In if');
} else{
  console.log('In else')
}

so i called a.b.c.d where c is not the property of b, so the proxy get handler is called in chrome where i return new object in case of undefined, but this does not work in IE11.

It is expected to land in else part if any property is undefined.

error in IE:

image

expected(getting it in crome):
image

Get trap, undefined object parameter access

let service = {
    foo: function...
    bar: function...
}
let EndpointFunction(...){
    return service;
}
var proxyService = new Proxy(service, {
                    get: function (target, propKey, receiver) {
                        if (!(propKey in target)) {
                            return new EndpointFunction(target, propKey, receiver);
                        }
                        return service[propKey];
                    }
                });

ex usage:
proxyService.baz('input').bar('blah');

This is for a utility function I wrote for my team to enable chaining syntax without predefining every method in the service; it works perfectly with the native proxy object, but is throwing an undefined error when I try to access an object parameter that doesn't exist. Can the polyfill catch undefined object parameter access before throwing a typeerror like the native proxy object can?

thanks!

How to support IE?

Hello?

I have a problem using this module, so I have a question.

I am creating a Web application with React through TypeScript and using the latest version of the module called create-react-app.

I use a library called immer, and I know that this library uses Proxy internally.


Thus, I found proxy-polyfill while I am searching online, especially is a problem for IE users with proxy.

I installed and deployed proxy-polyfill because support supports IE9 or higher.

The polyfill supports browsers that implement the full ES5 spec, such as IE9+ and Safari 6+.

But just because package.json include proxy-polyfill, customers who use IE will not be able to access the website at all.

I re-read README.md because I don't understand the situation well.

In 'To assignment Proxy to the global object', the included source is said to be es5 and package.json also confirmed that main is 'proxy.min.js'.

However, in the Installation section guide, If you'd like to just get the polyfill' version, use the required status as above.

I think polyfill'd version doesn't need because It written ES5!

Am I wrong? Please let me know. please!


I have also added import "proxy-polyfill/proxy.min" to src/index.js,
but it is not accessible from IE as it was at the time of installation. Link

Even though I tried to include proxy.min.js in index.html (THE FIRST ENTRY OF APP), but it doesn't works too.

How to support IE?

calling seal on the object passed to Proxy prevents extension.

This is undesirable, because for example I might be making a Proxy on an object that will be used as a prototype and I still want users to be able to modify the prototype (mainly they can add their own props/methods).

Why does the object have to be sealed?

Usage Proxy as a function example does not clear

That's because one should call exported function to get Proxy constructor.

The correct example might look like:

// commonJS require
const ProxyPolyfill = require('proxy-polyfill/src/proxy')();

// Your environment may also support transparent rewriting of commonJS to ES6:
import ProxyPolyfillFunc from 'proxy-polyfill/src/proxy';
const ProxyPolyfill = ProxyPolyfillFunc();

// Then use...
const myProxy = new ProxyPolyfill(...);

Nonexistent object properties cannot work

var asd = { a: 1 }
asd = new Proxy(asd, {
        get: function (target , key , proxy){
        	console.log(key)
        	return target[key]
        }
})
asd.a
// a   (I need this)
// 1
asd.b
// undefined  (but)

Crash on Android with version 0.3.0

I have just added import 'proxy-polyfill'; on top of my root file in React Native project. And when running it on Android I will get this error

image

I guess this is related #26 but the problem still exists

Missing semicolon throws error when bundled with other JavaScript

Uncaught TypeError: (intermediate value)(intermediate value)(...) is not a function
    at es6-promise.js:9

When bundling this with other javascript, one after another in the same file, because the file proxy.min.js does not have a semicolon at the end, the JavaScript parser does not know how to react.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.