{"id":32708681,"url":"https://github.com/jakeg/websocket-functions","last_synced_at":"2026-01-20T17:32:21.328Z","repository":{"id":321591000,"uuid":"1086420667","full_name":"jakeg/websocket-functions","owner":"jakeg","description":"Run functions over WebSockets - for Bun and web browsers","archived":false,"fork":false,"pushed_at":"2025-10-31T05:00:06.000Z","size":16,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-31T06:31:46.510Z","etag":null,"topics":["bun","chat","javascript","json","json-api","messaging","multiplayer","npm","remote-procedure-calls","rpc","rpc-api","rpc-client","rpc-framework","rpc-server","websocket","websocket-client","websocket-server"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/websocket-functions","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/jakeg.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,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-30T11:52:23.000Z","updated_at":"2025-10-31T05:12:26.000Z","dependencies_parsed_at":"2025-10-31T06:31:48.724Z","dependency_job_id":null,"html_url":"https://github.com/jakeg/websocket-functions","commit_stats":null,"previous_names":["jakeg/websockets-rpc","jakeg/websocket-functions"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/jakeg/websocket-functions","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeg%2Fwebsocket-functions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeg%2Fwebsocket-functions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeg%2Fwebsocket-functions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeg%2Fwebsocket-functions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jakeg","download_url":"https://codeload.github.com/jakeg/websocket-functions/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jakeg%2Fwebsocket-functions/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282585709,"owners_count":26693766,"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-04T02:00:05.887Z","response_time":62,"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":["bun","chat","javascript","json","json-api","messaging","multiplayer","npm","remote-procedure-calls","rpc","rpc-api","rpc-client","rpc-framework","rpc-server","websocket","websocket-client","websocket-server"],"created_at":"2025-11-02T04:00:28.622Z","updated_at":"2025-11-05T07:01:21.516Z","avatar_url":"https://github.com/jakeg.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WebSocket Functions\n\n```js\nlet sum = await ws.func('addNums', [3, 5])\n```\n...and get the return value back over a WebSocket connection.\n\nAbstract away those pesky messages and just invoke remote functions.\n\nWorks with Bun (server and clients) and web clients.\n\nUnder 4KB. Single file. No dependencies.\n\n## Install\n\n```bash\nbun install websocket-functions\n```\n\n... or just copy the tiny `index.js` file into your project.\n\n## Quick start\n\nFor servers (only Bun currently supported):\n\n```js\nimport { wsServer } from 'websocket-functions'\n\n// these functions can be called by clients\nlet handlers = {\n  addNums: (nums) =\u003e nums.reduce((acc, v) =\u003e acc + v, 0)\n}\n\nlet server = wsServer(Bun.serve, handlers, {\n  routes: {\n    '/ws': (req) =\u003e server.upgrade(req),\n    // ... other routes\n  }\n})\n\nconsole.log(`Server listening at ${server.url}`)\n```\n\n---\n\nFor clients (web or Bun):\n\n```js\nimport { wsClient } from 'websocket-functions'\n\nlet ws = wsClient(new WebSocket('ws://localhost:3000/ws'))\n\nws.onopen = async () =\u003e {\n  let sum = await ws.func('addNums', [3, 5])\n  console.log(sum)\n}\n```\n\nYou can also use `ws.proc('funcName', params)` if no return value is needed.\n\n---\n\nClients can also have handlers which can be called from the server:\n\n```js\nimport { wsClient } from 'websocket-functions'\n\n// these functions can be called from the server\nlet handlers = {\n  addNums: (nums) =\u003e nums.reduce((acc, v) =\u003e acc + v, 0)\n}\n\n// pass the handlers as the optional 2nd argument to wsClient()\nlet ws = wsClient(new WebSocket('ws://localhost:3000/ws'), handlers)\n```\n\n---\n\nUse Bun's `pub/sub` on the server to run a function on all clients subscribed to a room/channel:\n\n```js\n// run on all clients in 'some-room' (excluding ws itself)\nws.publishProc('some-room', 'addNums', [5, 7])\n\n// run on all clients in 'some-room' (including ws itself)\nserver.publishProc('some-room', 'addNums', [5, 7])\n```\n\n## Bun server with web client example\n\nPut these 3 files in the same folder.\n\n`server.js`:\n```js\nimport { wsServer } from 'websocket-functions'\nimport clientPage from './client.html'\n\n// these functions can be called by clients\nlet handlers = {\n  logMsg: (msg) =\u003e console.log('Your message:', msg),\n  addNums: (nums) =\u003e nums.reduce((acc, v) =\u003e acc + v, 0)\n}\n\nlet server = wsServer(Bun.serve, handlers, {\n  routes: {\n    '/': clientPage,\n    '/ws': (req) =\u003e server.upgrade(req)\n  }\n})\n\nconsole.log(`Server listening at ${server.url}`)\n```\n\n`client.html`:\n```html\n\u003c!doctype html\u003e\n\u003ctitle\u003eExample RPC client\u003c/title\u003e\n\u003cstyle\u003epre { display: inline; background: #eee; padding: 5px; }\u003c/style\u003e\n\u003cdiv\u003eTry eg \u003cpre\u003ews.proc('logMsg', 'Hello world')\u003c/pre\u003e or \u003cpre\u003eawait ws.func('addNums', [3, 5])\u003c/pre\u003e in the console.\u003c/div\u003e\n\u003cscript type=\"module\" src=\"client.js\"\u003e\u003c/script\u003e\n```\n\n`client.js`:\n```js\nimport { wsClient } from 'websocket-functions'\n\nglobalThis.ws = wsClient(new WebSocket('ws://localhost:3000/ws'))\n\nws.onopen = async () =\u003e {\n\n  // run a procedure that doesn't have a return value\n  ws.proc('logMsg', 'From client, running on server')\n\n  // run a function and do something with the value returned\n  console.log(await ws.func('addNums', [3, 5]))\n}\n```\n\nStart the server with `bun server.js` then open in a browser and in the developer tools console run eg `await ws.func('addNums', [3, 5])`.\n\n## API documentation\n\nAssuming imported with `import * as wsFunc from 'websocket-functions'`.\n\n### `wsFunc.wsClient(ws, handlers = {})`\n\nFor usage on WebSocket clients (Bun and web browsers).\n\nAttaches extra methods `proc()` and `func()` to the `WebSocket` object passed as the first argument. Also adds a `message` event listener to handle JSON messages with `{ jsonrpc: '2.0' }` in them.\n\nIf attaching your own `message` event listener, do so with `ws.addEventListener('message', func)` rather than `ws.onmessage = func` so as not to override our event listener. You'll probably want to ignore messages with `{ jsonrpc: '2.0' }` in them.\n\nTo provide functions which the server can invoke over a WebSocket connection, send an optional second argument being an object of functions.\n\n### `wsFunc.wsServer(serve, handlers, opt)`\n\nFor usage with Bun servers.\n\nStarts your Bun server as normal, but with extra methods `proc()`, `func()` and `publishProc()` attached to each WebSocket connection and a `publishProc()` method attached to the server instance. Also adds an additional `message` handler to handle JSON messages with `{ jsonrpc: '2.0' }` in them.\n\nPass `Bun.serve` as the first argument, an object of functions callable by clients as the second argument, and the normal Bun server configuration options as the third argument. Adds a `websocket` property to Bun's server configuration options if you don't provide one, or adds to yours if you do.\n\nProvide functions which connected WebSocket clients can invoke over a WebSocket connection as the second argument, or an empty `{}` object if none required.\n\n### `wsFunc.config`\n\nA configuration object. Currently with just one property `timeout` that defaults to `30_000`ms. Change the value of `timeout` to set a global maximum time in ms before `ws.func()` calls are rejected. \n\n### `handlers` object\n\nAn object of functions which can be provided to server or clients which can then be invoked by the other over a WebSocket connection, eg:\n\n```js\nlet handlers = {\n  logMsg: (msg) =\u003e console.log('Your message:', msg),\n  addNums: (nums) =\u003e nums.reduce((acc, v) =\u003e acc + v, 0)\n}\n```\n\nYou might chose to put these in a separate file and import them with eg `import * as handlers from './handlers.js'`:\n\n```js\n// File: handlers.js\n\nexport function logMsg (msg) {\n  console.log('Your message', msg)\n}\n\nexport function addNums (nums) {\n  return nums.reduce((acc, v) =\u003e acc + v, 0)\n}\n\nexport function logName (_, ws) {\n  console.log('User is called', ws.data.userName)\n}\n\nexport function getUserProp (prop, ws) {\n  return ws.data.user?.[prop]\n}\n```\n\n### `handler(params, ws)`\n\nEach handler in your `handlers` object will be called with two parameters:\n\n  - `params` the value provided by eg `ws.proc('funcName', params)`; can be of any type\n  - `ws` the WebSocket connection, allowing you to eg get data from it with `ws.data.userName`\n\nIf called with `ws.func()` as opposed to `ws.proc()` be sure to provide a `return` value, which will be sent back over the WebSocket connection. If no return value is needed, use `ws.proc()` instead, or you'll be unnecessarily sending an empty response back.\n\n### `ws.proc(method, params)`\n\nWhen called from the client, will invoke the given handler `method` on the server, and vice-versa.\n\nDoes not receive a return value, unlike `ws.func()`.\n\n### `await ws.func(method, params, timeout)`\n\nWhen called from the client, will invoke the given handler `method` on the server, and vice-versa.\n\nAn asynchronous function which returns a `Promise` which you should `await`. Once the other side of the WebSocket connection has a return value from the function, it will be sent back over the WebSocket connection and the `Promise` will resolve.\n\nThe `Promise` will `reject()` if a response isn't received within `timeout` (default: `30_000`ms or whatever is set in `wsFunc.config.timeout`).\n\nUse `ws.proc()` as opposed to `ws.func()` when your method has no return value to prevent an unnecessary message being exchanged.\n\n### `ws.publishProc(room, method, params)`\n\nOnly available from servers, uses Bun's `ws.publish()` method to to invoke `method` on all clients subscribed to the `room` channel (other than `ws` themselves). Does not receive a return value.\n\n### `server.publishProc(room, method, params)`\n\nAs per `ws.publishProc()`, but for all subscribers to `room` (while `ws.publishProc()` doesn't send to self). `server` is a Bun server object.\n\n## About\n\nProvides an incomplete implementation of [JSON-RPC 2.0](https://www.jsonrpc.org/specification) - exchanges JSON messages over WebSocket connections with property `{ jsonrpc: '2.0' }`.\n\nWhile HTTP provides _requests_ and associated _responses_, WebSockets only provides _messages_. This modules gives each message a unique `id` so when a message is sent back as a response (to provide a `return` value for a function) it can be associated with the outgoing message.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakeg%2Fwebsocket-functions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjakeg%2Fwebsocket-functions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjakeg%2Fwebsocket-functions/lists"}