{"id":21509460,"url":"https://github.com/mmis1000/dom-proxy","last_synced_at":"2025-08-02T03:09:31.704Z","repository":{"id":43930373,"uuid":"193365073","full_name":"mmis1000/DOM-Proxy","owner":"mmis1000","description":"A trasparent rpc for allowing web worker to access dom in mainland(and vise versa)","archived":false,"fork":false,"pushed_at":"2019-08-21T14:41:08.000Z","size":632,"stargazers_count":11,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-09T17:08:20.727Z","etag":null,"topics":["atomics","finalizationgroup","rpc","sharedarraybuffer","weakref","web-worker"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mmis1000.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-06-23T15:32:21.000Z","updated_at":"2023-04-20T01:26:21.000Z","dependencies_parsed_at":"2022-08-31T05:02:10.151Z","dependency_job_id":null,"html_url":"https://github.com/mmis1000/DOM-Proxy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mmis1000/DOM-Proxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmis1000%2FDOM-Proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmis1000%2FDOM-Proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmis1000%2FDOM-Proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmis1000%2FDOM-Proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mmis1000","download_url":"https://codeload.github.com/mmis1000/DOM-Proxy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mmis1000%2FDOM-Proxy/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268330911,"owners_count":24233151,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["atomics","finalizationgroup","rpc","sharedarraybuffer","weakref","web-worker"],"created_at":"2024-11-23T21:27:17.528Z","updated_at":"2025-08-02T03:09:31.647Z","avatar_url":"https://github.com/mmis1000.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![Dom-proxy](./public/img/dom-proxy-64.png) WebWorker DOM Proxy (POC)  \nTransparent rpc between web worker and mainland(the main page context)  \nLooks like [via.js](https://github.com/AshleyScirra/via.js), But more transparent at the cost of halt the web worker and continue on operation finished with `Atomic.wait`.  \n\n## Contents\n1. [Requirements](#Requirements-As-of-2019-06-25)\n2. [API](#API)\n3. [Example](#Example)\n4. [Explanation and caveats](#Explanation-and-caveats)\n   1. [Explanation](#Explanation)\n      1. [Operate on mainland sync](#Operate-on-mainland-sync)\n      2. [Strict equal of proxied same object](#Strict-equal-of-proxied-same-object)\n      3. [The garbage collection](#The-garbage-collection)\n   2. [Caveats](#Caveats)\n5. [Todos](#TODOS)\n\n\n## Requirements As of 2019-06-25\n\n1. [Atomics.waitAsync (stage2)](https://github.com/tc39/proposal-atomics-wait-async)\n2. [WeakRef (stage3)](https://github.com/tc39/proposal-weakrefs)\n3. [FinalizationGroup (stage2)](https://github.com/tc39/proposal-weakrefs)\n\nYou can try this with chromium(and its friends) 76+ with `--js-flags='--harmony-weak-refs'` flag from cli\n\n## API\n- DOMProxy.create(`ia32`, `getRoot`) =\u003e `[[Payload Object]]`\n  - Call this on both side that want to sue the proxy with same Int32Array baked by SharedArrayBuffer\n  - arguments\n    - ia32: Int32Array baked by SharedArrayBuffer\n    - getRoot : function that return object that represent this thread\n  - **must use only once per thread or it will hang**\n\n- [[Payload Object]] \n  - properties\n    - current: Id of current payload\n    - getRemote(`remoteId`) =\u003e `[[proxied remote object]]`\n      - arguments\n        - remoteId: other thread's `[[Payload Object]].current`\n\n- No other api required, no async await things, no value wrapper, nothing else\n\n## Example\nHost\n```html\n\u003c!-- polyfill --\u003e\n\u003cscript src=\"await-async.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"rpc.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"dom-proxy.js\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n    var sab = new SharedArrayBuffer(1024 * 1024 * 8)\n    var ia32 = new Int32Array(sab)\n    var proxy = DomProxy.create(ia32, () =\u003e window)\n    myWorker.postMessage({\n        hostId: proxy.current,\n        ia32: ia32\n    })\n\u003c/script\u003e\n```\n\nWorker\n```js\nimportScripts('await-async.js')\nimportScripts('rpc.js')\nimportScripts('dom-proxy.js')\n\nself.addEventListener('message', function (event) {\n    var payload = event.data\n    var proxy = DomProxy.create(payload.ia32, () =\u003e self)\n    var remoteWindow = proxy.getRemote(payload.hostId)\n\n    // access the window object as if it is actully in webworker\n    console.log(remoteWindow.location.href)\n    console.log(remoteWindow.document.title)\n\n    // strict equal works as is thanks to WeakRef\n    console.log(remoteWindow.document === remoteWindow.document)\n\n    // object keys got proxied\n    console.log(Object.keys(remoteWindow))\n    console.log(Object.getOwnPropertyNames(remoteWindow.location))\n\n    // modify dom as is\n    var el = remoteWindow.document.createElement('div')\n    el.innerHTML = 'You have been hacked from web worker lol'\n    remoteWindow.document.body.appendChild(el)\n\n    // while applied directly (no async await required!!!)\n    console.log(remoteWindow.document.body.innerHTML)\n\n    // reverse proxied object as is\n    remoteWindow.a = {}\n\n    // reverse proxied function also works as is\n    el.addEventListener('click', () =\u003e remoteWindow.alert('LOLLLL'))\n})\n```\n\n## Explanation and caveats\n### Explanation\n#### Operate on mainland sync\nThis library merely proxy traps of `Proxy` to the mainland,  \nso everything should just works as if you are operate on these object in the mainland.\n\nThe mainland then receive the request async with `Atomic.asyncAwait`.  \nAnd do things it need to do, response to worker with `Atomic.notify`.\n\n#### Strict equal of proxied same object\n1. This library marked the object it send to worker with an id and save it to a collection with that id.  \n\n```js\n// mainland\nvar id = createId()\nvar map = new Map()\nmap.set(objectToSend, id)\nsendTheIdToWorker(id)\n\n// worker\n```\n\n2. The worker then receive the id, build a fake object with it and save the fake object to a collection\n\n```js\n// mainland\n\n// worker\nvar id = getIdFromMainLand()\nvar map = new Map()\nvar fakeObject = createProxy(id)\nmap.set(id, fakeObject)\n```\n\n3. When the next time, the worker is requesting the same object,  \n   the mainland will find the item in map and send the same id to worker.  \n   The worker then find the same object in map with that id\n\n```js\n// mainland\nvar id = map.get(objectToSend)\nsendTheIdToWorker(id)\n\n// worker\nvar id = getIdFromMainLand()\nmap.get(id)\n```\n\n#### The garbage collection\nContinue from section above.  \n\nHow could you prevent the map in mainland and worker from leaking?  \n\nThe `WeakRef` proposal introduced two new APIs that allow you to observe the time garbage collection happened.  \nSo we make use of them.\n\n1. The worker was modified to hold the fakeObject with a `WeakRef` instead of directly to allow the proxy being collected.\n\n```js\n// mainland\n\n// worker\n-- map.set(id, fakeObject)\n++ map.set(id, new WeakRef(fakeObject))\n```\n\n2. The worker use the `FinalizationGroup` to track when will the garbage collection could happen (no one is holding the proxy anymore).  \n   And send the event to mainland.\n\n```js\n// mainland\n\n// worker\nconst cleaner = new FinalizationGroup(id =\u003e {\n    // remove the cache from entry\n    map.delete(id)\n    tellTheMainlandToDropTheRef(id)\n})\ncleaner.register(fakeObject, id)\n```\n\n3. The mainland then drop the cache it has with the id received.  \n   At this point, the knowledge of that object is totally gone from the proxy system.  \n   No memory leak happened, cheers.\n\n```js\n// mainland\nconst id = getIdToDropFromWorker()\nfindIdInMapAndDropIt(map, id)\n\n// worker\n```\n\n### Caveats\n1. Due to the lack of native `Atomic.asyncAwait`, polyfill is used, the operation is actually far more expensive then it should (each call cost about 0.5ms).\n\n   This means calls like `Object.keys(fakeObject)` will be very slow because it requires to call `getOwnPropertyDescriptor` on every single property (call it on `window` will result in about 200 requests).\n2. Due to the `FinalizationGroup` and `WeakRef` isn't ship on all stable browser version.\n\n   It isn't possible to use this library without edit the browser setting from cli directly currently. (that's why this is a POC, WeakRef can't be polyfilled at all)\n\n## TODOS\n1. Mapping symbol between workers (current throws)\n2. Fix worker count limit (current 256)\n3. Fix object id limit (current 8388608, aka `2**23`)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmis1000%2Fdom-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmmis1000%2Fdom-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmmis1000%2Fdom-proxy/lists"}