{"id":18365374,"url":"https://github.com/le0pard/cable-shared-worker","last_synced_at":"2025-04-10T12:22:09.824Z","repository":{"id":50695876,"uuid":"437284309","full_name":"le0pard/cable-shared-worker","owner":"le0pard","description":"ActionCable and AnyCable Shared Worker support","archived":false,"fork":false,"pushed_at":"2024-09-24T14:11:41.000Z","size":7200,"stargazers_count":60,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-10-29T22:31:45.557Z","etag":null,"topics":["actioncable","anycable","shared","visibility","worker"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/le0pard.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","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,"publiccode":null,"codemeta":null},"funding":{"custom":["https://www.buymeacoffee.com/leopard"]}},"created_at":"2021-12-11T13:04:55.000Z","updated_at":"2024-09-24T14:11:43.000Z","dependencies_parsed_at":"2023-02-12T02:46:17.991Z","dependency_job_id":"905f455c-7b7f-4554-a01e-befc759676ce","html_url":"https://github.com/le0pard/cable-shared-worker","commit_stats":{"total_commits":153,"total_committers":5,"mean_commits":30.6,"dds":"0.15032679738562094","last_synced_commit":"9936787aae5d3e88b8c82a702262b279498b7aea"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le0pard%2Fcable-shared-worker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le0pard%2Fcable-shared-worker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le0pard%2Fcable-shared-worker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/le0pard%2Fcable-shared-worker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/le0pard","download_url":"https://codeload.github.com/le0pard/cable-shared-worker/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247934825,"owners_count":21020728,"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","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":["actioncable","anycable","shared","visibility","worker"],"created_at":"2024-11-05T23:13:17.843Z","updated_at":"2025-04-10T12:22:09.804Z","avatar_url":"https://github.com/le0pard.png","language":"JavaScript","readme":"# Cable-shared-worker (CableSW) - ActionCable and AnyCable Shared Worker support [![Test/Build/Deploy](https://github.com/le0pard/cable-shared-worker/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/le0pard/cable-shared-worker/actions/workflows/release.yml)\n\n![schema](https://user-images.githubusercontent.com/98444/146681981-2e87a26e-9a5b-4109-9b05-73c1329b3ccc.jpg)\n\nCable-shared-worker is running ActionCable or AnyCable client in a Shared Worker allows you to share a single websocket connection for multiple browser windows and tabs.\n\n## Motivation\n\n - It's more efficient to have a single websocket connection\n - Page refreshes and new tabs already have a websocket connection, so connection setup time is zero\n - The websocket connection runs in a separate thread/process so your UI is 'faster'\n - Cordination of event notifications is simpler as updates have a single source\n - Close connection for non active (on background) tabs (by [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API))\n - It's the cool stuff...\n\n## Install\n\n```bash\nnpm install @cable-shared-worker/web @cable-shared-worker/worker\n# or\nyarn add @cable-shared-worker/web @cable-shared-worker/worker\n```\n\nBoth packages should be the same version.\n\n## Web\n\nYou need to initialize worker inside your JS file:\n\n```js\nimport {initWorker} from '@cable-shared-worker/web'\n\nawait initWorker('/worker.js')\n```\n\nSecond argument accept different options:\n\n```js\nawait initWorker(\n  '/worker.js',\n  {\n    workerOptions: { // worker options - more info https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker/SharedWorker\n      name: 'CableSW'\n    },\n    onError: (error) =\u003e console.error(error), // subscribe to worker errors\n    fallbackToWebWorker: true, // switch to web worker on safari\n    visibilityTimeout: 0, // timeout for visibility API, before close channels; 0 is disabled\n    onVisibilityChange: () =\u003e ({}) // subscribe for visibility changes\n  }\n)\n```\n\nAfter this you can start subscription channel:\n\n```js\nimport {createChannel} from '@cable-shared-worker/web'\n\n// Subscribe to the server channel via the client\nconst channel = await createChannel('ChatChannel', {roomId: 42}, (data) =\u003e {\n  console.log(data)\n})\n\n// call `ChatChannel#speak(data)` on the server\nchannel.perform('speak', {msg: 'Hello'})\n\n// Unsubscribe from the channel\nchannel.unsubscribe()\n```\n\nYou can manually close worker (for shared worker this will only close current tab connection, but not worker itself):\n\n```js\nimport {closeWorker} from '@cable-shared-worker/web'\n\n// close tab connection to worker\ncloseWorker()\n```\n\nThis helpers may help to get info what kind of workers available in browser:\n\n```js\nimport {\n  isWorkersAvailable,\n  isSharedWorkerAvailable,\n  isWebWorkerAvailable\n} from '@cable-shared-worker/web'\n\nisWorkersAvailable // return true, if Shared or Web worker available\nisSharedWorkerAvailable // return true, if Shared worker available\nisWebWorkerAvailable // return true, if Web worker available\n```\n\n### Visibility API\n\nYou can use [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) to detect, that user move tab on background and close websocket channels. Shared Worker websocket connection can be closed, if no active channels (behaviour controlled by option `closeWebsocketWithoutChannels` in worker component).\n\n```js\nimport {initWorker} from '@cable-shared-worker/web'\n\ninitWorker(\n  '/worker.js',\n  {\n    visibilityTimeout: 60, // 60 seconds wait before start close channels, default 0 is disable this functionality\n    onVisibilityChange: (isVisible, isChannelsWasPaused) =\u003e { // callback for visibility changes\n      if (isVisible \u0026\u0026 isChannelsWasPaused) {\n        // this condition can be used to fetch data changes, because channels was closed due to tab on background\n      }\n    }\n  }\n)\n```\n\n## Worker\n\nIn worker script (in example `/worker.js`) you need initialize websocket connection.\n\nFor actioncable you need installed [@rails/actioncable](https://www.npmjs.com/package/@rails/actioncable) package:\n\n```js\nimport * as actioncableLibrary from '@rails/actioncable'\nimport {initCableLibrary} from '@cable-shared-worker/worker'\n\n// init actioncable library\nconst api = initCableLibrary({\n  cableType: 'actioncable',\n  cableLibrary: actioncableLibrary\n})\n\n// connect by websocket url\napi.createCable(WebSocketURL)\n```\n\nFor anycable you need install [@anycable/web](https://www.npmjs.com/package/@anycable/web) package:\n\n```js\nimport * as anycableLibrary from '@anycable/web'\nimport {initCableLibrary} from '@cable-shared-worker/worker'\n\n// init anycable library\nconst api = initCableLibrary({\n  cableType: 'anycable',\n  cableLibrary: anycableLibrary\n})\n\n// connect by websocket url\napi.createCable(WebSocketURL)\n```\n\nYou can also use Msgpack and Protobuf protocols supported by [AnyCable Pro](https://anycable.io/#pro) (you must install the corresponding encoder package yourself):\n\n```js\nimport * as anycableLibrary from '@anycable/web'\nimport {MsgpackEncoder} from '@anycable/msgpack-encoder'\nimport {initCableLibrary} from '@cable-shared-worker/worker'\n\nconst api = initCableLibrary({\n  cableType: 'anycable',\n  cableLibrary: anycableLibrary\n})\n\napi.createCable(\n  webSocketURL,\n  {\n    protocol: 'actioncable-v1-msgpack',\n    encoder: new MsgpackEncoder()\n  }\n)\n\n// or for protobuf\nimport * as anycableLibrary from '@anycable/web'\nimport {ProtobufEncoder} from '@anycable/protobuf-encoder'\nimport {initCableLibrary} from '@cable-shared-worker/worker'\n\nconst api = initCableLibrary({\n  cableType: 'anycable',\n  cableLibrary: anycableLibrary\n})\n\napi.createCable(\n  webSocketURL,\n  {\n    protocol: 'actioncable-v1-protobuf',\n    encoder: new ProtobufEncoder()\n  }\n)\n```\n\nIf you need manually close websocket connection, you can use `destroyCable` method:\n\n```js\nimport * as actioncableLibrary from '@rails/actioncable'\nimport {initCableLibrary} from '@cable-shared-worker/worker'\n\nconst api = initCableLibrary({\n  cableType: 'actioncable',\n  cableLibrary: actioncableLibrary\n})\n\napi.createCable(WebSocketURL)\n\n// later in code\n\napi.destroyCable()\n```\n\nMethod `initCableLibrary` accept additional option `closeWebsocketWithoutChannels`:\n\n```js\nconst api = initCableLibrary({\n  cableType: 'actioncable',\n  cableLibrary: actioncableLibrary,\n  // if true (default), worker will close websocket connection, if have zero active channels\n  // example: all tabs on the background send a signal to close all channels by visibility API timeout\n  closeWebsocketWithoutChannels: false\n})\n```\n\n## Custom communication between window and worker\n\nYou can use cable-shared-worker for custom communication between window and worker. In window you can use method `sendCommand` to send custom command to worker:\n\n```js\nimport {initWorker} from '@cable-shared-worker/web'\n\nconst worker = await initWorker('/worker.js')\n\nworker.sendCommand('WINDOW_CUSTOM_COMMAND', {data: 'example'})\n```\n\nOn worker side you need define `handleCustomWebCommand` function. First argument will be custom command (in example `WINDOW_CUSTOM_COMMAND`), second one - command data (in example `{data: 'example'}`), third one - response function, which can send response command to window:\n\n```js\nimport * as actioncableLibrary from '@rails/actioncable'\nimport {initCableLibrary} from '@cable-shared-worker/worker'\n\nconst api = initCableLibrary({\n  cableType: 'actioncable',\n  cableLibrary: actioncableLibrary,\n  handleCustomWebCommand: (command, data, responseFunction) =\u003e {\n    responseFunction('WORKER_CUSTOM_COMMAND', {another: 'data'})\n  }\n})\n```\n\nTo handle custom commands from worker in window, you need provide `handleCustomWorkerCommand` method in `initWorker`:\n\n```js\nimport {initWorker} from '@cable-shared-worker/web'\n\nconst worker = await initWorker(\n  '/worker.js',\n  {\n    handleCustomWorkerCommand: (command, data) =\u003e {\n      console.log('worker response', command, data)\n    }\n  }\n)\n\nworker.sendCommand('WINDOW_CUSTOM_COMMAND', {data: 'example'})\n```\n\nNote: You cannot [send commands](https://github.com/le0pard/cable-shared-worker/blob/main/shared/constants.js), that the package uses itself for communication.\n\n## Browser Support\n\nSupported modern browsers, that support Shared Worker (IE, Opera Mini not supported).\n\nSafari supports [Shared Worker](https://caniuse.com/sharedworkers) only from version 16.0 (Sep, 2022). For older version, package will switch to Web Worker, which cannot share connection between tabs. You can disable fallback to Web Worker by `fallbackToWebWorker: false` (or use `isSharedWorkerAvailable` for own logic).\n\n## Development\n\n```bash\n$ yarn # install all dependencies\n$ yarn dev # run development build with watch functionality\n$ yarn build # run production build\n$ yarn lint # run eslint checks\n$ yarn test # run tests\n```\n\n\n","funding_links":["https://www.buymeacoffee.com/leopard"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fle0pard%2Fcable-shared-worker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fle0pard%2Fcable-shared-worker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fle0pard%2Fcable-shared-worker/lists"}