automerge / automerge-classic

A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically.
http://automerge.org/
MIT License
14.75k stars 467 forks source link

Proxy polyfill does not support trap 'deleteProperty' #257

Open leehambley opened 4 years ago

leehambley commented 4 years ago

Note: Opening this issue here just as a heads-up.

I don't know that I expect it to be fixed, but I did want to have raised it.

I have to support Mobile Safari 9 (internal version 601.2 in the UA string) and it completely lacks support for the Proxy object. No big deal, we can polyfill with proxy-polyfill.

However, proxy-polyfill doesn't support a specific feature that is used (and none of other pollyfils I could find do, either)

TypeError: Proxy polyfill does not support trap 'deleteProperty'
  at trap(../node_modules/proxy-polyfill/src/proxy.js:60:19)
  at mapProxy(../webpack:/Automerge/frontend/proxies.js:195:14)
  at rootObjectProxy(../webpack:/Automerge/frontend/proxies.js:220:10)
  at Frontend(../webpack:/Automerge/frontend/index.js:274:12)
  at change(../webpack:/Automerge/src/automerge.js:39:37)
  at quizReducer(quizReducer.ts:234:20)
  at dispatch(../node_modules/redux/es/redux.js:213:22)
  at apply(../node_modules/redux/es/redux.js:299:3)
  at enhancer(../node_modules/redux/es/redux.js:629:31)
  at createStore(../node_modules/redux/es/redux.js:79:12)
  at call(quizStore.ts:72:17)
  at tryCatch(../node_modules/regenerator-runtime/runtime.js:45:40)
  at _invoke(../node_modules/regenerator-runtime/runtime.js:271:22)
  at call(../node_modules/regenerator-runtime/runtime.js:97:21)
  at tryCatch(../node_modules/regenerator-runtime/runtime.js:45:40)
  at invoke(../node_modules/regenerator-runtime/runtime.js:135:20)
  at handler(../node_modules/regenerator-runtime/runtime.js:145:13)
  at fn(../node_modules/core-js/modules/es.promise.js:114:22)
  at r(../node_modules/core-js/internals/microtask.js:26:9)
  at promiseReactionJob([native code])

Safari 0.1 versions newer adds support for this, so I can definitely work around this, and I'm starting to discover a base-line for what I can, or cannot support.

Feel free to close if this is/was a known issue and there's no easy workaround. I don't know enough about Proxy or the polyfills to know if ther is an alternative to the delete trap.


ept commented 4 years ago

Hi @leehambley, thanks for this. It's a known issue, and also affects e.g. React Native, as far as I know. Unfortunately the current Automerge API relies heavily on proxies to detect changes to object properties, and there isn't an easy workaround.

In the medium term, I think the best solution would be to implement an alternative API that doesn't use proxies. This is not as bad as it may sound: Automerge is already cleanly structured into a frontend and backend with a clear protocol between the two, and proxies are only used in the frontend. Therefore, a proxy-free API could be implemented as an alternative frontend that talks to the existing (unmodified) backend. All the hairy CRDT logic is in the backend, so this alternative frontend wouldn't be very difficult to write.

If anyone wants to have a go at this, I'll happily provide pointers to help get you started.

leehambley commented 4 years ago

In the medium term, I think the best solution would be to implement an alternative API that doesn't use proxies.

I could see that, or find a way not to need the deleteProperty trap and implement things in other terms.

Either way, I don't think in 2020 this is an unreasonable baseline to require, on the web. But for React Native it may well be a different story.

Should I close this @ept, or do you prefer to keep it open? I didn't find any closed issues for the same problem, so this may be the only record?

ept commented 4 years ago

Let's keep it open, as reference for anyone running into the same issue.

zhang123cnn commented 3 years ago

Just want to echo the issue above. I am also trying to use automerge with JS runtime which does not support Proxy for security and performance reasons. Proxy itself is not easy to be poly-filled because it needs lower level engine support.

Currently I am working around it by replacing some features in proxies.js and restrict what developers can do it doc update. But it would be great to have an automerge implementation without the use of Proxy.

In the medium term, I think the best solution would be to implement an alternative API that doesn't use proxies. If anyone wants to have a go at this, I'll happily provide pointers to help get you started.

@ept since you mentioned it might not be that hard, I may have time to give it a try. Do you mind provide me some pointers to get me started?

Thanks

ept commented 3 years ago

@zhang123cnn Great that you want to try this! I recommend starting by familiarising yourself with the frontend-backend communication protocol. Use the version on the performance branch in this repository, as it has changed substantially compared to the main branch. Some rough documentation of the protocol can be found in BINARY_FORMAT.md (a somewhat misnamed file…) and you can also look at the TypeScript types which describe the protocol.

I recommend you make a new frontend that produces changes to send to the existing backend, and which takes patches from the existing backend and applies them to the frontend state. This mainly happens through the backend functions applyChanges and applyLocalChange. You can look at the tests (especially backend_test.js for examples of how these functions behave. On top of this, your new frontend can provide any API you think is appropriate.