{"_id":"@anycable/serverless-js","_rev":"8-13c98508cbf35e030c288882c77119d0","name":"@anycable/serverless-js","dist-tags":{"latest":"0.2.3"},"versions":{"0.0.1":{"name":"@anycable/serverless-js","version":"0.0.1","keywords":["anycable","actioncable","serverless"],"author":{"name":"Vladimir Dementyev"},"license":"MIT","_id":"@anycable/serverless-js@0.0.1","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"homepage":"https://anycable.io/","bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"dist":{"shasum":"95e59547bde737272ee7d0be69f620dfc9dc45ec","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.0.1.tgz","fileCount":9,"integrity":"sha512-Ye9pRX0q/IqMdGOfiJUdzRU88QSl8oXJccrdcOXbux2Jqt2JfOw5gpPgInnPtLyNzGWM/VxIbHTlt/XQm1Vg2Q==","signatures":[{"sig":"MEUCIQCkxWBp8Mx00rF0SxUNVa+eFbZhwoqX5Q04LlEwZcobvQIgRFUQj8YJtVsBqjAWKu5k2iW1kOfhv5IZD77GTYU0nOg=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":21375},"main":"index.ts","type":"module","engines":{"node":">=18.0.0"},"exports":{".":"./index.ts","./package.json":"./package.json"},"gitHead":"97b7dc1f4d6f42e23f36d867aa14d77cc0087e19","_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"repository":{"url":"git+https://github.com/anycable/anycable-serverless-js.git","type":"git"},"_npmVersion":"9.6.7","description":"AnyCable channels for serverless JS apps","directories":{},"sideEffects":false,"_nodeVersion":"20.3.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{},"peerDependencies":{"jose":"^4.14.6"},"_npmOperationalInternal":{"tmp":"tmp/serverless-js_0.0.1_1695349933451_0.9267296184556788","host":"s3://npm-registry-packages"}},"0.0.2":{"name":"@anycable/serverless-js","version":"0.0.2","keywords":["anycable","actioncable","serverless"],"author":{"name":"Vladimir Dementyev"},"license":"MIT","_id":"@anycable/serverless-js@0.0.2","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"homepage":"https://anycable.io/","bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"dist":{"shasum":"83993933006de530e8ab488d7f5a1d514648c9cc","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.0.2.tgz","fileCount":9,"integrity":"sha512-OAM0XVA2EXEAmer0LSEy/cONt9dCjjaI7uCeBdJQ/DGXCkz/YPxIqMvgAnMqAby/K1QFVBcGi3YPZaCnldslPA==","signatures":[{"sig":"MEUCIDh0GySLWPyAbGRzpHsV7aHc5USSO0z1JnWuE7uPU/FLAiEAzS0hiGPvs9GJXrBR0YIGx7v+OpfM8JcAFowD+U9lTK0=","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":21365},"main":"index.ts","type":"module","engines":{"node":">=18.0.0"},"exports":{".":"./index.ts","./package.json":"./package.json"},"gitHead":"6daa1cc44ee4062389fe737a7526cf0ad88397aa","_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"repository":{"url":"git+https://github.com/anycable/anycable-serverless-js.git","type":"git"},"_npmVersion":"9.6.7","description":"AnyCable channels for serverless JS apps","directories":{},"sideEffects":false,"_nodeVersion":"20.3.0","dependencies":{},"_hasShrinkwrap":false,"devDependencies":{},"peerDependencies":{"jose":"^4.14.6"},"_npmOperationalInternal":{"tmp":"tmp/serverless-js_0.0.2_1695355270068_0.5345978315524906","host":"s3://npm-registry-packages"}},"0.0.3":{"name":"@anycable/serverless-js","version":"0.0.3","keywords":["anycable","actioncable","serverless"],"author":{"name":"Vladimir Dementyev"},"license":"MIT","_id":"@anycable/serverless-js@0.0.3","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"homepage":"https://anycable.io/","bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"dist":{"shasum":"73bdc434effe0786434fa1b85ef76ba637548b86","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.0.3.tgz","fileCount":23,"integrity":"sha512-9eH0Zo1dDd5HYohyNJGUUD85Mj7airY8GJGNUjGTKN8SWMMRhY2L9Hm0Byp5A1HYQdosQ1DJx9ZELsJ9iFajUw==","signatures":[{"sig":"MEQCIBQez7KfEfana0y8cLvquho4rgxIUC1Y02McCvK9jv07AiB7dFQ1nDedYlxXJNaVda8SBFTOBNmabFTrDFQ6T1rv6g==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":34758},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0","pnpm":">=8.7.5"},"exports":{".":"./dist/index.js"},"gitHead":"b9de5af4a4da03d4561cb95490a466f302ae8fd6","scripts":{"lint":"eslint src/","test":"uvu -r @esbuild-kit/cjs-loader tests/","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"pnpm build"},"_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"repository":{"url":"git+https://github.com/anycable/anycable-serverless-js.git","type":"git"},"_npmVersion":"9.6.7","description":"AnyCable channels for serverless JS apps","directories":{},"sideEffects":false,"_nodeVersion":"20.3.0","dependencies":{"jose":"^4.14.6"},"_hasShrinkwrap":false,"devDependencies":{"uvu":"^0.5.6","eslint":"^8.49.0","prettier":"^3.0.3","node-fetch":"^3.3.2","typescript":"^5.2.2","@types/node":"^20.6.3","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","@esbuild-kit/cjs-loader":"^2.4.4","@typescript-eslint/parser":"^6.7.2","@typescript-eslint/eslint-plugin":"^6.7.2"},"_npmOperationalInternal":{"tmp":"tmp/serverless-js_0.0.3_1698202038817_0.9775374179509311","host":"s3://npm-registry-packages"}},"0.1.0":{"name":"@anycable/serverless-js","version":"0.1.0","keywords":["anycable","actioncable","serverless"],"author":{"name":"Vladimir Dementyev"},"license":"MIT","_id":"@anycable/serverless-js@0.1.0","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"homepage":"https://anycable.io/","bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"dist":{"shasum":"05d0c15a62ac88f4906a4961a15d3034bacdae28","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.1.0.tgz","fileCount":23,"integrity":"sha512-SuX8Nr4kC01QsfulAyX4krLb3xy+c/Fze22/0TYmUe+uzFBTloC0N2wThj4k5b8qKEzOSWLCztuRmvyzMC5eow==","signatures":[{"sig":"MEYCIQCzXaa5ESnIDY1EDDL/PamYfb1To70P30+eElC6wS4cuQIhAJtLMgc3jJqzYajVDcyx+d10zIZsTBy/1+k/zbx6xTlx","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":34758},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0","pnpm":">=8.7.5"},"exports":{".":"./dist/index.js"},"gitHead":"8ffc9c8ff4cbfa27c9a15ae5a120fd546e8af9db","scripts":{"lint":"eslint src/","test":"uvu -r @esbuild-kit/cjs-loader tests/","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"pnpm build"},"_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"repository":{"url":"git+https://github.com/anycable/anycable-serverless-js.git","type":"git"},"_npmVersion":"9.6.7","description":"AnyCable channels for serverless JS apps","directories":{},"sideEffects":false,"_nodeVersion":"20.3.0","dependencies":{"jose":"^4.14.6"},"_hasShrinkwrap":false,"devDependencies":{"uvu":"^0.5.6","eslint":"^8.49.0","prettier":"^3.0.3","node-fetch":"^3.3.2","typescript":"^5.2.2","@types/node":"^20.6.3","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","@esbuild-kit/cjs-loader":"^2.4.4","@typescript-eslint/parser":"^6.7.2","@typescript-eslint/eslint-plugin":"^6.7.2"},"_npmOperationalInternal":{"tmp":"tmp/serverless-js_0.1.0_1698278872891_0.40529105342273075","host":"s3://npm-registry-packages"}},"0.2.0":{"name":"@anycable/serverless-js","version":"0.2.0","keywords":["anycable","actioncable","serverless"],"author":{"name":"Vladimir Dementyev"},"license":"MIT","_id":"@anycable/serverless-js@0.2.0","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"homepage":"https://anycable.io/","bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"dist":{"shasum":"9424aa2f7d7150f6e8ea2c15508b005e46993bef","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.2.0.tgz","fileCount":26,"integrity":"sha512-gMMFqnadlmGoVEIUgHaPDsu8n1bLDdoFxXysOdJ+fLJmWw7ErCBupdki8LLiUAE6B41drbbpFo+HqqA+SOLeXQ==","signatures":[{"sig":"MEQCIFVHbEA61u6RRQoPjYOLyA2Hw9IliCrTdLR8Qd8TFYcAAiA6WA2bT51lDOfVyVGsQkb0EBh/y6rV4eqJFwXFOuAzvA==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":38339},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0","pnpm":">=8.7.5"},"exports":{".":"./dist/index.js"},"gitHead":"15f9478751c60c3c6dcc1ce2608c43405e582362","scripts":{"lint":"eslint src/","test":"uvu -r @esbuild-kit/cjs-loader tests/","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"pnpm build"},"_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"repository":{"url":"git+https://github.com/anycable/anycable-serverless-js.git","type":"git"},"_npmVersion":"10.2.4","description":"AnyCable channels for serverless JS apps","directories":{},"sideEffects":false,"_nodeVersion":"21.6.0","dependencies":{"jose":"^4.14.6"},"_hasShrinkwrap":false,"devDependencies":{"uvu":"^0.5.6","eslint":"^8.49.0","prettier":"^3.0.3","node-fetch":"^3.3.2","typescript":"^5.2.2","@types/node":"^20.6.3","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","@esbuild-kit/cjs-loader":"^2.4.4","@typescript-eslint/parser":"^6.7.2","@typescript-eslint/eslint-plugin":"^6.7.2"},"_npmOperationalInternal":{"tmp":"tmp/serverless-js_0.2.0_1713311496403_0.32235241827256744","host":"s3://npm-registry-packages"}},"0.2.1":{"name":"@anycable/serverless-js","version":"0.2.1","keywords":["anycable","actioncable","serverless"],"author":{"name":"Vladimir Dementyev"},"license":"MIT","_id":"@anycable/serverless-js@0.2.1","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"homepage":"https://anycable.io/","bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"dist":{"shasum":"9f6de33ce8616e475453b33682e8111741049df4","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.2.1.tgz","fileCount":26,"integrity":"sha512-fdW4w0sR5dTW1ZXPgY5y2Oa/ewH4VMDd5YRA5PM3FZBEtw3C6RWOOrWA6xbbYPAOEzu63MbWAuZvhTJyksA0nA==","signatures":[{"sig":"MEYCIQCq0W9BCN54F5XxDpuxlU2Pj1G4WVkVyj31loneEJyW9wIhAOIJCNz2/zcI+D5SSn8S/g1LuvidaaFdeE2ng53kSSfg","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":38496},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0","pnpm":">=8.7.5"},"exports":{".":"./dist/index.js"},"gitHead":"99465df983f7c38cc33f6c844eba9448e84461b5","scripts":{"lint":"eslint src/","test":"uvu -r @esbuild-kit/cjs-loader tests/","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"pnpm build"},"_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"repository":{"url":"git+https://github.com/anycable/anycable-serverless-js.git","type":"git"},"_npmVersion":"10.9.0","description":"AnyCable channels for serverless JS apps","directories":{},"sideEffects":false,"_nodeVersion":"23.3.0","dependencies":{"jose":"^4.14.6"},"_hasShrinkwrap":false,"devDependencies":{"uvu":"^0.5.6","eslint":"^8.49.0","prettier":"^3.0.3","node-fetch":"^3.3.2","typescript":"^5.2.2","@types/node":"^20.6.3","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","@esbuild-kit/cjs-loader":"^2.4.4","@typescript-eslint/parser":"^6.7.2","@typescript-eslint/eslint-plugin":"^6.7.2"},"_npmOperationalInternal":{"tmp":"tmp/serverless-js_0.2.1_1733533194418_0.6981382701745991","host":"s3://npm-registry-packages"}},"0.2.2":{"name":"@anycable/serverless-js","version":"0.2.2","keywords":["anycable","actioncable","serverless"],"author":{"name":"Vladimir Dementyev"},"license":"MIT","_id":"@anycable/serverless-js@0.2.2","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"homepage":"https://anycable.io/","bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"dist":{"shasum":"ae4112f0b5a6add84a5a717cb0cb711545442b0a","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.2.2.tgz","fileCount":26,"integrity":"sha512-2r6LVlIPMX5Y5Fzb+tHcqSv6Yc3O7+1kW4vmLiFLJTO7zW+YfEjC4FxU+vqz0W7TzqlkogUH1H8RnP9I1dBMBw==","signatures":[{"sig":"MEUCICPNkN/QMomFrzPYgFlfr59HOZPnmyTn+bfv3pAi6WCnAiEA1wjbJyLdSN8kpZ9RgijoaIZiVlFD/Lt447HxPxPjsbs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":38776},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.0.0","pnpm":">=8.7.5"},"exports":{".":"./dist/index.js"},"gitHead":"c5fbc07e43e1040d530431f15a79137312b0a51f","scripts":{"lint":"eslint src/","test":"uvu -r @esbuild-kit/cjs-loader tests/","build":"tsc","typecheck":"tsc --noEmit","prepublishOnly":"pnpm build"},"_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"repository":{"url":"git+https://github.com/anycable/anycable-serverless-js.git","type":"git"},"_npmVersion":"11.8.0","description":"AnyCable channels for serverless JS apps","directories":{},"sideEffects":false,"_nodeVersion":"25.5.0","dependencies":{"jose":"^4.14.6"},"_hasShrinkwrap":false,"devDependencies":{"uvu":"^0.5.6","eslint":"^8.49.0","prettier":"^3.0.3","node-fetch":"^3.3.2","typescript":"^5.2.2","@types/node":"^20.6.3","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","@esbuild-kit/cjs-loader":"^2.4.4","@typescript-eslint/parser":"^6.7.2","@typescript-eslint/eslint-plugin":"^6.7.2"},"_npmOperationalInternal":{"tmp":"tmp/serverless-js_0.2.2_1770916571029_0.652925397794081","host":"s3://npm-registry-packages-npm-production"}},"0.2.3":{"name":"@anycable/serverless-js","version":"0.2.3","description":"AnyCable channels for serverless JS apps","engines":{"node":">=18.0.0","pnpm":">=8.7.5"},"scripts":{"prepublishOnly":"pnpm build","test":"uvu -r @esbuild-kit/cjs-loader tests/","lint":"eslint src/","typecheck":"tsc --noEmit","build":"tsc"},"keywords":["anycable","actioncable","serverless"],"homepage":"https://anycable.io/","repository":{"type":"git","url":"git+https://github.com/anycable/anycable-serverless-js.git"},"bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"author":{"name":"Vladimir Dementyev"},"license":"MIT","sideEffects":false,"type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":"./dist/index.js"},"dependencies":{"jose":"^4.14.6"},"devDependencies":{"@esbuild-kit/cjs-loader":"^2.4.4","@types/node":"^20.6.3","@typescript-eslint/eslint-plugin":"^6.7.2","@typescript-eslint/parser":"^6.7.2","eslint":"^8.49.0","eslint-config-prettier":"^9.0.0","eslint-plugin-prettier":"^5.0.0","node-fetch":"^3.3.2","prettier":"^3.0.3","typescript":"^5.2.2","uvu":"^0.5.6"},"gitHead":"e3b9ee42523cb7f2275943c6e26614fd7123bccc","_id":"@anycable/serverless-js@0.2.3","_nodeVersion":"25.5.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-ItGIlBWDwTmyrKANbOYFp5HYLyPHx7SGNp0nG77GbHv3pWBVYKGTDfopJOsAXjoJ3IEk7+scKVJEnV86u5hI6g==","shasum":"9547b7c4bf35ff2cded460ee630a7fa6e567812d","tarball":"https://registry.npmjs.org/@anycable/serverless-js/-/serverless-js-0.2.3.tgz","fileCount":26,"unpackedSize":39767,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIC4+5qlX5M4VEdmJQFG2EcoUM6b3trcnWCZrqc5YE3OuAiAHAguhA0Dw8e/XhySxpIIhK3LCedTThCZaUS4PvVASNA=="}]},"_npmUser":{"name":"palkan","email":"dementiev.vm@gmail.com"},"directories":{},"maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/serverless-js_0.2.3_1786731337529_0.8720640010180913"},"_hasShrinkwrap":false}},"time":{"created":"2023-09-22T02:32:13.283Z","modified":"2026-08-14T18:15:37.860Z","0.0.1":"2023-09-22T02:32:13.677Z","0.0.2":"2023-09-22T04:01:10.364Z","0.0.3":"2023-10-25T02:47:18.986Z","0.1.0":"2023-10-26T00:07:53.228Z","0.2.0":"2024-04-16T23:51:36.562Z","0.2.1":"2024-12-07T00:59:54.673Z","0.2.2":"2026-02-12T17:16:11.168Z","0.2.3":"2026-08-14T18:15:37.676Z"},"bugs":{"url":"https://github.com/anycable/anycable-serverless-js/issues"},"author":{"name":"Vladimir Dementyev"},"license":"MIT","homepage":"https://anycable.io/","keywords":["anycable","actioncable","serverless"],"repository":{"type":"git","url":"git+https://github.com/anycable/anycable-serverless-js.git"},"description":"AnyCable channels for serverless JS apps","maintainers":[{"name":"hellsquirrel","email":"zloymult@gmail.com"},{"name":"palkan","email":"dementiev.vm@gmail.com"}],"readme":"[![npm version](https://badge.fury.io/js/@anycable%2Fserverless-js.svg)](https://badge.fury.io/js/@anycable%2Fserverless-js)\n\n# AnyCable Serverless\n\nThis package provides modules to implement [AnyCable](https://anycable.io) backend APIs to be executed in serverless Node.js environments. (Works with serverful apps, too, of course.)\n\n> See our [demo application](https://github.com/anycable/vercel-anycable-demo) for a working example.\n\n## Architecture and components\n\nThis package provides functionality to work with AnyCable server from a JS/TS backend applications and includes support for the following features:\n\n- [JWT authentication](#using-anycable-jwt)\n- [Signed streams](#signed-streams)\n- [Broadcasting](#broadcasting)\n\nThe package also comes with HTTP handlers to handle [AnyCable RPC-over-HTTP](https://docs.anycable.io/anycable-go/rpc) requests and provides **channels** and **application** abstractions to describe real-time features of your application.\n\n## Usage\n\nInstall the `@anycable/serverless-js` package using your tool of choice, e.g.:\n\n```sh\nnpm install @anycable/serverless-js\n```\n\n### Using AnyCable JWT\n\n[AnyCable JWT](https://docs.anycable.io/anycable-go/jwt_identification) is a recommended way to authenticate your clients. To get started, you can use the identificator object and generate auth tokens with it:\n\n```js\nimport { identificator } from \"@anycable/serverless-js\";\n\nconst jwtSecret = \"very-secret\";\nconst jwtTTL = \"1h\";\n\nexport const identifier = identificator(jwtSecret, jwtTTL);\n\n// Then, somewhere in your code, generate a token and provide it to the client\nconst userId = authenticatedUser.id;\nconst token = await identifier.generateToken({ userId });\n\nconst cableURL = `${CABLE_URL}?jid=${token}`\n```\n\nYou can pass any identification information to the token. It can be later used in _channels_ (see below).\n\n### Signed streams\n\nYou can create a _signer_ instance to generate signed streams names and use them with [AnyCable pub/sub](https://docs.anycable.io/anycable-go/signed_streams):\n\n```js\nimport { signer } from \"@anycable/serverless-js\";\n\nconst streamsSecret = process.env.ANYCABLE_STREAMS_SECRET;\n\nconst sign = signer(secret);\n\nconst signedStreamName = sign(\"room/13\");\n```\n\nThen, you can use the generated stream name with your client (using [AnyCable JS client SDK](https://github.com/anycable/anycable-client)):\n\n```js\nimport { createCable } from \"@anycable/web\";\n\nconst cable = createCable(WEBSOCKET_URL);\nconst stream = await fetchStreamForRoom(\"13\");\n\nconst channel = cable.streamFromSigned(stream);\nchannel.on(\"message\", (msg) => {\n  // handle notification\n})\n```\n\n### Broadcasting\n\nTo **broadcast** messages to connected clients, you must use a broadcaster instance:\n\n```js\nimport { broadcaster } from \"@anycable/serverless-js\";\n\n// Broadcasting configuration\nconst broadcastURL =\n  process.env.ANYCABLE_BROADCAST_URL || \"http://127.0.0.1:8090/_broadcast\";\nconst broadcastToken = process.env.ANYCABLE_HTTP_BROADCAST_SECRET || \"\";\n\n// Create a broadcasting function to send broadcast messages via HTTP API\nexport const broadcastTo = broadcaster(broadcastURL, broadcastToken);\n```\n\nWhen AnyCable runs with a single application secret (`ANYCABLE_SECRET`), the broadcast endpoint is protected by a key derived from that secret. Use the `broadcastKey` function to obtain it:\n\n```js\nimport { broadcaster, broadcastKey } from \"@anycable/serverless-js\";\n\nexport const broadcastTo = broadcaster(\n  broadcastURL,\n  broadcastKey(process.env.ANYCABLE_SECRET)\n);\n```\n\nCurrently, this package only supports broadcasting over HTTP. However, AnyCable provides different [broadcasting adapters](https://docs.anycable.io/anycable-go/broadcasting) (e.g., Redis, NATS, etc.) that you can integrate yourself.\n\n### Using channels (AnyCable RPC)\n\nAn **application** instance is responsible for handling the connection lifecycle and dispatching messages to the appropriate channels.\n\n```js\n// api/cable.ts\nimport {\n  Application,\n  ConnectionHandle,\n  broadcaster,\n} from \"@anycable/serverless-js\";\n\n// Some custom authentication logic\nimport { verifyToken } from \"./auth\";\n\n// The identifiers  type describe connection identifiers—e.g., user ID, username, etc.\nexport type CableIdentifiers = {\n  userId: string;\n};\n\n// Application instance handles connection lifecycle events\nclass CableApplication extends Application<CableIdentifiers> {\n  // IMPORTANT: When using AnyCable JWT, you don't need to define\n  // the connect() callback, authentication doesn't hit your server\n  async connect(handle: ConnectionHandle<CableIdentifiers>) {\n    const url = handle.env.url;\n    const params = new URL(url).searchParams;\n\n    if (params.has(\"token\")) {\n      const payload = await verifyToken(params.get(\"token\")!);\n\n      if (payload) {\n        const { userId } = payload;\n\n        handle.identifiedBy({ userId });\n      }\n      return;\n    }\n\n    // Reject connection if not authenticated\n    handle.reject();\n  }\n\n  async disconnect(handle: ConnectionHandle<CableIdentifiers>) {\n    // Here you can perform any cleanup work\n    console.log(`User ${handle.identifiers!.userId} disconnected`);\n  }\n}\n\n// Create and instance of the class to use in HTTP handlers (see the next section)\nconst app = new CableApplication();\n\n// Register channels (see below)\n\nexport default app;\n```\n\n**Channels** instances reflect particular features (e.g, chat room, notifications, etc.) and are responsible for handling incoming commands and subscription lifecycle events:\n\n```js\nimport { Channel, ChannelHandle } from \"@anycable/serverless-js\";\n// We re-using the identifiers type from the cable application\nimport type { CableIdentifiers } from \"../cable\";\n\n// Define the channel params (used by the client according to Action Cable protocol)\ntype ChatChannelParams = {\n  roomId: string;\n};\n\nexport type ChatMessage = {\n  id: string;\n  username: string;\n  body: string;\n  createdAt: string;\n};\n\nexport default class ChatChannel\n  extends Channel<CableIdentifiers, ChatChannelParams, ChatMessage>\n{\n  // The `subscribed` method is called when the client subscribes to the channel\n  // You can use it to authorize the subscription and setup streaming\n  async subscribed(\n    handle: ChannelHandle<CableIdentifiers>,\n    params: ChatChannelParams | null,\n  ) {\n    if (!params) {\n      handle.reject();\n      return;\n    }\n\n    if (!params.roomId) {\n      handle.reject();\n      return;\n    }\n\n    handle.streamFrom(`room:${params.roomId}`);\n  }\n\n  // This method is called by the client\n  async sendMessage(\n    handle: ChannelHandle<CableIdentifiers>,\n    params: ChatChannelParams,\n    data: SentMessage,\n  ) {\n    const { body } = data;\n\n    if (!body) {\n      throw new Error(\"Body is required\");\n    }\n\n    console.log(\n      `User ${handle.identifiers!.username} sent message: ${data.body}`,\n    );\n\n    const message: ChatMessage = {\n      id: Math.random().toString(36).substr(2, 9),\n      username: handle.identifiers!.username,\n      body,\n      createdAt: new Date().toISOString(),\n    };\n\n    // Broadcast the message to all subscribers (see below)\n    await broadcastTo(`room:${params.roomId}`, message);\n  }\n}\n\n// You MUST register a channel instance within the application\n// The client MUST use the provided identifier to subscribe to the channel.\napp.registerChannel(\"chat\", new ChatChannel());\n```\n\n### HTTP handlers\n\nTo glue our HTTP layer with the channels, we need to configure HTTP handlers. Below you can find an examples for popular serverless platforms.\n\n#### Vercel\n\nDefine [Vercel](https://vercel.com) serverless functions as follows:\n\n```js\n// api/anycable/connect/route.ts\nimport { NextResponse } from \"next/server\";\nimport { connectHandler, Status } from \"@anycable/serverless-js\";\nimport app from \"../../cable\";\n\nexport async function POST(request: Request) {\n  try {\n    const response = await connectHandler(request, app);\n    return NextResponse.json(response, {\n      status: 200,\n    });\n  } catch (e) {\n    console.error(e);\n    return NextResponse.json({\n      status: Status.ERROR,\n      error_msg: \"Server error\",\n    });\n  }\n}\n\n// api/anycable/command/route.ts\nimport { NextResponse } from \"next/server\";\nimport { commandHandler, Status } from \"@anycable/serverless-js\";\nimport app from \"../../cable\";\n\nexport async function POST(request: Request) {\n  try {\n    const response = await commandHandler(request, app);\n    return NextResponse.json(response, {\n      status: 200,\n    });\n  } catch (e) {\n    console.error(e);\n    return NextResponse.json({\n      status: Status.ERROR,\n      error_msg: \"Server error\",\n    });\n  }\n}\n\n// api/anycable/disconnect/route.ts\nimport { NextResponse } from \"next/server\";\nimport { disconnectHandler, Status } from \"@anycable/serverless-js\";\nimport app from \"../../cable\";\n\nexport async function POST(request: Request) {\n  try {\n    const response = await disconnectHandler(request, app);\n    return NextResponse.json(response, {\n      status: 200,\n    });\n  } catch (e) {\n    console.error(e);\n    return NextResponse.json({\n      status: Status.ERROR,\n      error_msg: \"Server error\",\n    });\n  }\n}\n```\n\nYou can also avoid repeatition by using a universal handler and a bit of configuration:\n\n```js\n// next.config.js\nconst nextConfig = {\n  // ...\n  rewrites: async () => {\n    return [\n      {\n        source: \"/api/anycable/:path*\",\n        destination: \"/api/anycable\",\n      },\n    ];\n  },\n};\n\n// ...\n```\n\nAnd then you can use the following handler:\n\n```js\n// api/anycable/route.ts\nimport { NextResponse } from \"next/server\";\nimport { handler, Status } from \"@anycable/serverless-js\";\nimport app from \"../../cable\";\n\nexport async function POST(request: Request) {\n  try {\n    const response = await handler(request, app);\n    return NextResponse.json(response, {\n      status: 200,\n    });\n  } catch (e) {\n    console.error(e);\n    return NextResponse.json({\n      status: Status.ERROR,\n      error_msg: \"Server error\",\n    });\n  }\n}\n```\n","readmeFilename":"README.md"}