{"id":33258144,"url":"https://github.com/RubaXa/wormhole","last_synced_at":"2025-11-21T20:03:09.520Z","repository":{"id":20360651,"uuid":"23635811","full_name":"RubaXa/wormhole","owner":"RubaXa","description":"Wormhole — it's better EventEmitter for communication between tabs with supporting Master/Slave.","archived":false,"fork":false,"pushed_at":"2023-06-27T05:47:15.000Z","size":7834,"stargazers_count":389,"open_issues_count":3,"forks_count":31,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-09-21T17:50:48.578Z","etag":null,"topics":["communication","cors","eventemitter","master-slave","persistent","sharedworker","websocket","wormhole"],"latest_commit_sha":null,"homepage":"https://rubaxa.github.io/wormhole/","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/RubaXa.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,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2014-09-03T20:12:48.000Z","updated_at":"2025-08-07T06:26:55.000Z","dependencies_parsed_at":"2024-04-24T11:01:42.137Z","dependency_job_id":"4370ce67-2d12-427b-b946-92f4429fe2d6","html_url":"https://github.com/RubaXa/wormhole","commit_stats":{"total_commits":89,"total_committers":4,"mean_commits":22.25,"dds":0.0674157303370787,"last_synced_commit":"1d53ab5a59adbda23c7737d02f03967bf8293d4b"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/RubaXa/wormhole","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubaXa%2Fwormhole","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubaXa%2Fwormhole/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubaXa%2Fwormhole/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubaXa%2Fwormhole/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RubaXa","download_url":"https://codeload.github.com/RubaXa/wormhole/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RubaXa%2Fwormhole/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285681625,"owners_count":27213755,"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-11-21T02:00:06.175Z","response_time":61,"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":["communication","cors","eventemitter","master-slave","persistent","sharedworker","websocket","wormhole"],"created_at":"2025-11-17T03:00:30.887Z","updated_at":"2025-11-21T20:03:09.507Z","avatar_url":"https://github.com/RubaXa.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":["Parsers"],"readme":"# Wormhole\nIt's better EventEmitter for communication between tabs with supporting Master/Slave.\n\n```\nnpm i --save-dev wormhole.js\n```\n\n\n### Features\n * [One connection on WebSocket](#ws) for all tabs.\n * Support [Master/Slave](#ms)\n * [Cross-domain communication](#cors)\n * SharedWorker or fallback to localStorage\n * IE 8+, Chrome 10+, FireFox 10+, Opera 10+, Safari 6+\n * Test coverage ([run](http://rubaxa.github.io/wormhole/tests/))\n\n\n---\n\n\n### Basic example\n\n```js\nimport wormhole from 'wormhole.js'; // yes, with \".js\", it's not mistake\n\n// All tabs\nwormhole().on('coords', (x, y) =\u003e {\n\tconsole.log(x, y);\n});\n\n// Some tab\nwormhole().emit('coords', [5, 10]);\n\n// Master tab\nif (wormhole().master) {\n\t// ..\n}\n\nwormhole().on('master', () =\u003e {\n\tconsole.log('Wow!');\n});\n```\n\n\n---\n\n\n\u003ca name=\"ws\"\u003e\u003c/a\u003e\n\n### One connection on WebSocket for all tabs\nModule `wormhole.js/ws` implements WebSocket-like interface:\nhttps://rubaxa.github.io/wormhole/?ws=y\n\n```js\nimport WS from 'wormhole.js/ws';\n\n// Create WebScoket (wormhole-socket)\nconst socket = new WS('ws://echo.websocket.org'); // OR new WS('...', null, hole);\n\nsocket.onopen = () =\u003e console.log('Connected');\nsocket.onmessage = ({data}) =\u003e console.log('Received:', data);\n\n// Unique event\nsocket.onmaster = () =\u003e {\n\tconsole.log('Yes, I\\'m master!');\n};\n\n// Some tab\nsocket.send({foo: 'bar'})\n\n// All tabs:\n//  \"Received:\" {foo: 'bar'}\n```\n\n---\n\n\n\u003ca name=\"cors\"\u003e\u003c/a\u003e\n\n### CORS example\n\n 1. Create a subdomain, ex.: `http://wormhole.youdomain.com/`;\n 2. Copy-paste [universal.html](./universal.html) and [wormhole.js](./wormhole.js) into root;\n 3. Check access `http://wormhole.youdomain.com/universal.html`;\n 4. Profit:\n\n```js\n// http://foo.youdomain.com/\nimport {Universal} from 'wormhole.js';\nconst hole = new Universal('http://wormhole.youdomain.com/universal.html');\n\nhole.on('data', (data) =\u003e {\n\tconsole.log(data);\n});\n\n\n// http://bar.youdomain.com/\nimport {Universal} from 'wormhole.js';\nconst hole = new Universal('http://wormhole.youdomain.com/universal.html');\n\nhole.emit('data', 'any data');\n```\n\n\n---\n\n\n\u003ca name=\"ms\"\u003e\u003c/a\u003e\n\n### Master/slave example\n\n```js\nimport wormhole from 'wormhole.js';\n\n// All tabs (main.js)\n// Define remote command (master)\nwormhole()['get-data'] = (function (_cache) {\n\treturn function getData(req, callback) {\n\t\tif (!_cache.hasOwnProperty(req.url)) {\n\t\t\t_cache[req.url] = fetch(req.url).then(res =\u003e res.json());\n\t\t}\n\n\t\treturn _cache[key];\n\t};\n})({});\n\n// Get remote data method\nfunction getData(url) {\n\treturn new Promise((resolve, reject) =\u003e {\n\t\t// Calling command on master (from slave... or the master, is not important)\n\t\twormhole().call(\n\t\t\t'get-data', // command\n\t\t\t{url}, // arguments\n\t\t\t(err, json) =\u003e err ? reject(err) : resolve(json) // callback(err, result)\n\t\t);\n\t});\n};\n\n// I'm master!\nwormhole().on('master', () =\u003e {\n\t// some code\n});\n\n// Tab #X\ngetData('/path/to/api').then((json) =\u003e {\n\t// Send ajax request\n\tconsole.log(result);\n});\n\n// Tab #Y\ngetData('/path/to/api').then((result) =\u003e {\n\t// From master cache\n\tconsole.log(result);\n});\n```\n\n\n---\n\n\n### Peers\n\n```js\nwormhole()\n\t.on('peers', (peers) =\u003e {\n\t\tconsole.log('ids:', peers); // ['tab-id-1', 'tab-id-2', ..]\n\t})\n\t.on('peers:add', (id) =\u003e {\n\t\t// ..\n\t})\n\t.on('peers:remove', (id) =\u003e {\n\t\t// ..\n\t})\n;\n```\n\n---\n\n\n### Executing the command on master\n\n\n```js\n// Register command (all tabs)\nwormhole()['foo'] = (data, next) =\u003e {\n\t// bla-bla-bla\n\tnext(null, data.reverse()); // or `next('error')`\n};\n\n\n// Calling the command (some tab)\nwormhole().call('foo', [1, 2, 3], (err, results) =\u003e {\n\tconsole.log(results); // [3, 2, 1]\n})\n```\n\n\n---\n\n\n### Modules\n\n - [Emitter](#m-emitter) — Micro event emitter\n - [cors](#m-cors) — Handy wrapper over `postMessage`.\n - [store](#m-store) — Safe and a handy wrapper over `localStorage`.\n\n\n---\n\n\u003ca name=\"m-emitter\"\u003e\u003c/a\u003e\n\n#### wormhole.Emitter\nMicro event emitter.\n\n - **on**(type:`String`, fn:`Function`):`this`\n - **off**(type:`String`, fn:`Function`):`this`\n - **emit**(type:`String`[, args:`*|Array`]):`this`\n\n```js\nimport {Emitter} from 'wormhole.js';\n\nconst obj = Emitter.apply({}); // or new wormhole.Emitter();\n\nobj.on('foo', () =\u003e {\n  console.log(arguments);\n});\n\nobj.emit('foo'); // []\nobj.emit('foo', 1); // [1]\nobj.emit('foo', [1, 2, 3]); // [1, 2, 3]\n```\n\n\n---\n\n\u003ca name=\"m-cors\"\u003e\u003c/a\u003e\n\n#### wormhole.cors\nHandy wrapper over `postMessage`.\n\n```js\nimport {cors} from 'wormhole.js';\n\n// Main-document\ncors.on('data', (data) =\u003e {\n\tconsole.log('Received:', data);\n});\n\ncors['some:command'] = (value) =\u003e value * 2;\n\n// IFrame\ncors(parent).send({foo: 'bar'});\n// [main-document] \"Received:\" {foo: 'bar'}\n\ncors(parent).call('some:command', 3, (err, result) =\u003e {\n\tconsole.log('Error:', err, 'Result:', result);\n\t// [iframe] \"Error:\" null \"Result:\" 6\n});\n```\n\n\n---\n\n\u003ca name=\"m-store\"\u003e\u003c/a\u003e\n\n#### wormhole.store\nSafe and a handy wrapper over `localStorage`.\n\n - **get**(key:`String`):`*`\n - **set**(key:`String`, value:`*`)\n - **remove**(key:`String`)\n - **on**(type:`String`, fn:`Function`)\n - **off**(type:`String`, fn:`Function`)\n\n```js\nimport {store} from 'wormhole.js';\n\nstore.on('change', (key, data) =\u003e {\n\tconsole.log('change -\u003e ', key, data);\n});\n\nstore.on('change:prop', (key, value) =\u003e {\n\tconsole.log('change:prop -\u003e ', key, value);\n});\n\nstore.set('foo', {bar: 'baz'});\n// change -\u003e foo {bar: 'baz'}\n\nstore.set('prop', {qux: 'ok'});\n// change -\u003e prop {qux: 'ok'}\n// change:prop -\u003e prop {qux: 'ok'}\n```\n\n---\n\n\n### Utils\n\n - [uuid](#uuid)\n - [debounce](#debounce)\n\n\n---\n\n\u003ca name=\"uuid\"\u003e\u003c/a\u003e\n\n##### wormhole.uuid():`String`\nA universally unique identifier (UUID) is an identifier standard used in software construction,\nstandardized by the Open Software Foundation (OSF) as part of the Distributed Computing Environment (DCE)\n(c) [wiki](https://en.wikipedia.org/wiki/Universally_unique_identifier).\n\n\n---\n\n\u003ca name=\"debounce\"\u003e\u003c/a\u003e\n\n##### wormhole.debounce(fn:`Function`, delay:`Number`[, immediate:`Boolean`]):`Function`\n\nCreates and returns a new debounced version of the passed function that will postpone its execution until after wait milliseconds have elapsed since the last time it was invoked.\n\n----\n\n\n### Development\n\n- `npm test`\n- `npm run dev` — run dev watcher\n- `npm run build`","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRubaXa%2Fwormhole","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FRubaXa%2Fwormhole","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FRubaXa%2Fwormhole/lists"}