{"_id":"@emdzej/swsrs-client","_rev":"5-e0c3887c244cc255a66f912d1ff2ffab","name":"@emdzej/swsrs-client","dist-tags":{"latest":"0.3.0"},"versions":{"0.1.0":{"name":"@emdzej/swsrs-client","version":"0.1.0","keywords":["websocket","relay","tunnel","swsrs","nat-traversal"],"license":"MIT","_id":"@emdzej/swsrs-client@0.1.0","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"homepage":"https://github.com/emdzej/swsrs#readme","bugs":{"url":"https://github.com/emdzej/swsrs/issues"},"dist":{"shasum":"489f1c5521ad110d2be7e862cff5a18a24c72686","tarball":"https://registry.npmjs.org/@emdzej/swsrs-client/-/swsrs-client-0.1.0.tgz","fileCount":19,"integrity":"sha512-lGwpR59TXLlD7LZKL25fxNIqDYSiFLd5CvOgI5hwlmLUrXDd0i5hCLvZmIENx8VghJRRYxG88LwNjx5XR/9tSA==","signatures":[{"sig":"MEYCIQDHk4LTY75mZogLxeV3WgF9i2nkUK2D4MShmNW30FOpNwIhAIad/MOlTQJp0JsUpqvKJiqn/BeCVZwZDPZ2+eSmS2Ki","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":23346},"main":"./dist/index.js","type":"module","_from":"file:emdzej-swsrs-client-0.1.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc","clean":"rm -rf dist"},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/52b08c9d4372d5f02de5380ba3e3a977/emdzej-swsrs-client-0.1.0.tgz","_integrity":"sha512-lGwpR59TXLlD7LZKL25fxNIqDYSiFLd5CvOgI5hwlmLUrXDd0i5hCLvZmIENx8VghJRRYxG88LwNjx5XR/9tSA==","repository":{"url":"git+https://github.com/emdzej/swsrs.git","type":"git","directory":"clients/typescript"},"_npmVersion":"11.14.1","description":"Client SDK for swsrs (simple websocket relay service): admin API + peer connection helpers for browser and Node 22+.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.6.0"},"_npmOperationalInternal":{"tmp":"tmp/swsrs-client_0.1.0_1780480026792_0.18860725394003253","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@emdzej/swsrs-client","version":"0.2.0","keywords":["websocket","relay","tunnel","swsrs","nat-traversal"],"license":"MIT","_id":"@emdzej/swsrs-client@0.2.0","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"homepage":"https://github.com/emdzej/swsrs#readme","bugs":{"url":"https://github.com/emdzej/swsrs/issues"},"dist":{"shasum":"6d012cb4923ecf404d61295d5170afe1a01d1a1f","tarball":"https://registry.npmjs.org/@emdzej/swsrs-client/-/swsrs-client-0.2.0.tgz","fileCount":35,"integrity":"sha512-LD6zSYhZdOKBUz3jAZBlvANtlNisnvpk1YCltSu+oPPprnKnJ/7F66W1K/x70nEEHOcM0X5xvQVr8MiF2dgiMg==","signatures":[{"sig":"MEUCIDd5MDyUfSbE8X91EYFThTjbNvkpdSMVhDSVtEzkSu4gAiEArEQlfigjSMek8FyjB1edpQD0Mc7bUyuXN0ppbRB6HQM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48477},"main":"./dist/index.js","type":"module","_from":"file:emdzej-swsrs-client-0.2.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./node":{"types":"./dist/node.d.ts","import":"./dist/node.js"}},"scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest"},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/46c01730604123ab2b77aa35c6140428/emdzej-swsrs-client-0.2.0.tgz","_integrity":"sha512-LD6zSYhZdOKBUz3jAZBlvANtlNisnvpk1YCltSu+oPPprnKnJ/7F66W1K/x70nEEHOcM0X5xvQVr8MiF2dgiMg==","repository":{"url":"git+https://github.com/emdzej/swsrs.git","type":"git","directory":"clients/typescript"},"_npmVersion":"11.14.1","description":"Client SDK for swsrs (simple websocket relay service): admin API + peer connection helpers for browser and Node 22+.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.8","typescript":"^5.6.0","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/swsrs-client_0.2.0_1780483411118_0.8000462028586828","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@emdzej/swsrs-client","version":"0.2.1","keywords":["websocket","relay","tunnel","swsrs","nat-traversal"],"license":"MIT","_id":"@emdzej/swsrs-client@0.2.1","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"homepage":"https://github.com/emdzej/swsrs#readme","bugs":{"url":"https://github.com/emdzej/swsrs/issues"},"dist":{"shasum":"0a6e2906b155d122143b69e37ba3b3b031f842c1","tarball":"https://registry.npmjs.org/@emdzej/swsrs-client/-/swsrs-client-0.2.1.tgz","fileCount":35,"integrity":"sha512-dBDaRuWMRZ8BZ8TSCMOfCBRwctPNR0rahD6UP3Ai4oecjdVRa+2X5rZhJObvG8PBO33jKPvZsUXt1y7k4LMGsQ==","signatures":[{"sig":"MEQCIBfncjcieEXbkjtdZUVMp6yPmpL75cFjqoJG3xcRNcY5AiAaKCWdZ/mUQFJ9GQdBmNm+14uIu9N88z0NRKxNylkW5A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48477},"main":"./dist/index.js","type":"module","_from":"file:emdzej-swsrs-client-0.2.1.tgz","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./node":{"types":"./dist/node.d.ts","import":"./dist/node.js"}},"scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest"},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/67bbf6ec238ed413256e4e3be022cd5b/emdzej-swsrs-client-0.2.1.tgz","_integrity":"sha512-dBDaRuWMRZ8BZ8TSCMOfCBRwctPNR0rahD6UP3Ai4oecjdVRa+2X5rZhJObvG8PBO33jKPvZsUXt1y7k4LMGsQ==","repository":{"url":"git+https://github.com/emdzej/swsrs.git","type":"git","directory":"clients/typescript"},"_npmVersion":"11.16.0","description":"Client SDK for swsrs (simple websocket relay service): admin API + peer connection helpers for browser and Node 22+.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.8","typescript":"^5.6.0","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/swsrs-client_0.2.1_1780484224330_0.8985367552133761","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@emdzej/swsrs-client","version":"0.2.2","keywords":["websocket","relay","tunnel","swsrs","nat-traversal"],"license":"MIT","_id":"@emdzej/swsrs-client@0.2.2","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"homepage":"https://github.com/emdzej/swsrs#readme","bugs":{"url":"https://github.com/emdzej/swsrs/issues"},"dist":{"shasum":"40d146c5a29296ab0a87aefe45acf8e9335d7077","tarball":"https://registry.npmjs.org/@emdzej/swsrs-client/-/swsrs-client-0.2.2.tgz","fileCount":35,"integrity":"sha512-PFcXPEcsQdDOv8ew0nVrhLVCzJ7U+S7j0pk6e9vhHuPxlZrEh2zwksRgOCQ4aOzO5L2e1Zg3vxrTVW1UYwUTUw==","signatures":[{"sig":"MEUCICwAr8f1qxibZohKKW5Gx/zJx0LRCmNJIU27BsE60333AiEA9PXor3KSWxJTdsYHafJc18Pw2O5g1ZjQDmK4jqMAwAE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":48477},"main":"./dist/index.js","type":"module","_from":"file:emdzej-swsrs-client-0.2.2.tgz","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./node":{"types":"./dist/node.d.ts","import":"./dist/node.js"}},"scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest"},"_npmUser":{"name":"emdzej","email":"michal@jaskolski.pro"},"_resolved":"/private/var/folders/7h/x_w_580x4s9dq3sq11tpvzkwy3nbj8/T/1d00fd542b08aaab03831427a254335a/emdzej-swsrs-client-0.2.2.tgz","_integrity":"sha512-PFcXPEcsQdDOv8ew0nVrhLVCzJ7U+S7j0pk6e9vhHuPxlZrEh2zwksRgOCQ4aOzO5L2e1Zg3vxrTVW1UYwUTUw==","repository":{"url":"git+https://github.com/emdzej/swsrs.git","type":"git","directory":"clients/typescript"},"_npmVersion":"11.16.0","description":"Client SDK for swsrs (Simple WebSocket Relay Service): admin API + peer connection helpers for browser and Node 22+.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.1","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.1.8","typescript":"^5.6.0","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/swsrs-client_0.2.2_1780497419200_0.6312168667721605","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"_id":"@emdzej/swsrs-client@0.3.0","bugs":{"url":"https://github.com/emdzej/swsrs/issues"},"dist":{"shasum":"dfb10e4ff11d1a005c2c67f515af7085a56668c2","tarball":"https://registry.npmjs.org/@emdzej/swsrs-client/-/swsrs-client-0.3.0.tgz","fileCount":35,"integrity":"sha512-YgeLYWtNdSIAq0hWvLOXS5cagP75IG9ExrHOS+PyWJq33IUY0fXgJBnf/uq18wgOG0949kuO7CRTunQf4E18yw==","signatures":[{"sig":"MEUCIQCBn3+4XeUKAh20NIQDuOSL76rwa1w8eqqwnKfuennFYwIgGZtVzdTIVWc6weTCDe4GPK2YvtyCiYTH6fGLluuab8Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDO4NSKQOoflkAwJKtltxosUIlUavtUKPSzguNFq95paAiBW747lXCnadm/5Z1Ir+XgnLub71ILzXRr2KRDgnSaNDw=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@emdzej%2fswsrs-client@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":50319},"main":"./dist/index.js","name":"@emdzej/swsrs-client","type":"module","_from":"file:emdzej-swsrs-client-0.3.0.tgz","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./node":{"types":"./dist/node.d.ts","import":"./dist/node.js"}},"license":"MIT","scripts":{"test":"vitest run","build":"tsc","clean":"rm -rf dist","test:watch":"vitest"},"version":"0.3.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a1fc3974-4e6e-4e51-bcd5-8770b721f939"}},"homepage":"https://github.com/emdzej/swsrs#readme","keywords":["websocket","relay","tunnel","swsrs","nat-traversal"],"_resolved":"/tmp/dc53a8a503e96827026fdab1cc693527/emdzej-swsrs-client-0.3.0.tgz","_integrity":"sha512-YgeLYWtNdSIAq0hWvLOXS5cagP75IG9ExrHOS+PyWJq33IUY0fXgJBnf/uq18wgOG0949kuO7CRTunQf4E18yw==","repository":{"url":"git+https://github.com/emdzej/swsrs.git","type":"git","directory":"clients/typescript"},"_npmVersion":"11.19.0","description":"Client SDK for swsrs (Simple WebSocket Relay Service): admin API + peer connection helpers for browser and Node 22+.","directories":{},"maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"sideEffects":false,"_nodeVersion":"24.21.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^5.0.2","typescript":"^7.0.2","@types/node":"^22.10.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/swsrs-client_0.3.0_1790330385118_0.03900708235280037"}}},"time":{"created":"2026-06-03T09:47:06.451Z","modified":"2026-09-25T09:59:45.558Z","0.1.0":"2026-06-03T09:47:06.940Z","0.2.0":"2026-06-03T10:43:31.249Z","0.2.1":"2026-06-03T10:57:04.513Z","0.2.2":"2026-06-03T14:36:59.339Z","0.3.0":"2026-09-25T09:59:45.233Z"},"bugs":{"url":"https://github.com/emdzej/swsrs/issues"},"license":"MIT","homepage":"https://github.com/emdzej/swsrs#readme","keywords":["websocket","relay","tunnel","swsrs","nat-traversal"],"repository":{"url":"git+https://github.com/emdzej/swsrs.git","type":"git","directory":"clients/typescript"},"description":"Client SDK for swsrs (Simple WebSocket Relay Service): admin API + peer connection helpers for browser and Node 22+.","maintainers":[{"name":"emdzej","email":"michal@jaskolski.pro"}],"readme":"# @emdzej/swsrs-client\n\nTypeScript client for the [swsrs](https://github.com/emdzej/swsrs) relay.\nWorks in browsers and Node 22+. Zero runtime dependencies — uses native\n`fetch` and `WebSocket`.\n\n## Install\n\n```bash\npnpm add @emdzej/swsrs-client    # or: npm install / yarn add\n```\n\nThis package is developed and published with pnpm. Consumers can install it\nwith any package manager — the published artifact is the same.\n\n## Admin API\n\n```ts\nimport { AdminClient } from \"@emdzej/swsrs-client\";\n\nconst admin = new AdminClient({\n  baseURL: \"https://relay.example.com\",\n  token: async () => await getOIDCToken(),  // called per request, may refresh\n});\n\nconst session = await admin.createSession();\n// { id, initiator_token, responder_token, expires_at, ... }\n\nconst status = await admin.getSession(session.id);\nconst all    = await admin.listSessions();\nawait admin.deleteSession(session.id);\n```\n\n`token` may be a sync function, async function, or plain string-returning\nfunction. It is invoked on every request so you can rotate without rebuilding\nthe client.\n\n## Peer connection\n\n`dial` and `accept` are wire-identical — the names express caller intent.\nBoth return a `PeerConnection` wrapping a native `WebSocket`.\n\n```ts\nimport { dial } from \"@emdzej/swsrs-client\";\n\nconst conn = await dial({\n  relayURL: \"wss://relay.example.com\",  // http(s):// auto-upgraded\n  sessionId: session.id,\n  token: session.initiator_token,\n});\n\nconn.socket.addEventListener(\"message\", (e) => {\n  // e.data is ArrayBuffer (socket.binaryType defaults to \"arraybuffer\")\n  console.log(\"got\", new Uint8Array(e.data as ArrayBuffer));\n});\n\nconn.send(new TextEncoder().encode(\"hello\"));\n\nawait conn.closed;  // resolves on disconnect with the CloseEvent\n```\n\n### Why the token is in the URL\n\nBrowsers cannot set the `Authorization` header on a WebSocket upgrade, so the\nSDK passes `?token=` instead. The swsrs server accepts either form. Tokens\nare short-lived and bound to a specific slot — but you should still always\nserve `wss://`, never `ws://`, to keep them out of plaintext.\n\n### Cancellation\n\nPass an `AbortSignal` to abort the handshake or close an open connection:\n\n```ts\nconst ctrl = new AbortController();\nsetTimeout(() => ctrl.abort(), 5_000);\n\nawait dial({ relayURL, sessionId, token, signal: ctrl.signal });\n```\n\n## End-to-end example: probe ↔ UI\n\n```ts\n// === UI side (browser) ===\nconst admin = new AdminClient({ baseURL: \"https://relay.example.com\", token: getOIDC });\nconst session = await admin.createSession();\n\n// hand session.responder_token to the probe via your existing control plane\nawait myBackend.tellProbe(probeId, { sessionId: session.id, token: session.responder_token });\n\nconst conn = await dial({\n  relayURL: \"wss://relay.example.com\",\n  sessionId: session.id,\n  token: session.initiator_token,\n});\n\nconn.socket.addEventListener(\"message\", handleProbeData);\nconn.send(encode({ cmd: \"runCheck\" }));\n```\n\n## API reference\n\n### `class AdminClient`\n- `new AdminClient({ baseURL, token, fetch? })`\n- `createSession(signal?): Promise<Session>`\n- `getSession(id, signal?): Promise<SessionStatus>`\n- `listSessions(signal?): Promise<SessionStatus[]>`\n- `deleteSession(id, signal?): Promise<void>`\n\n### `function dial(opts: PeerOptions): Promise<PeerConnection>`\n### `function accept(opts: PeerOptions): Promise<PeerConnection>`\n\n```ts\ninterface PeerOptions {\n  relayURL: string;\n  sessionId: string;\n  token: string;\n  signal?: AbortSignal;\n  WebSocketImpl?: typeof WebSocket;  // tests\n  protocols?: string | string[];\n}\n\ninterface PeerConnection {\n  readonly socket: WebSocket;\n  readonly opened: Promise<void>;\n  readonly closed: Promise<CloseEvent>;\n  send(data: ArrayBufferLike | ArrayBufferView | Blob | string): void;\n  close(code?: number, reason?: string): void;\n}\n```\n\n## Auth helpers\n\nDon't have an OIDC token? Discover the relay's IdP and run device flow:\n\n```ts\nimport { discoverConfig, deviceLogin, AdminClient } from \"@emdzej/swsrs-client\";\nimport { FileTokenStore } from \"@emdzej/swsrs-client/node\"; // Node only\n\nconst config = await discoverConfig(\"https://relay.example.com\");\n// config.issuer, config.token_endpoint, config.device_authorization_endpoint, ...\n\nconst tok = await deviceLogin({\n  config,\n  onPrompt: (p) => {\n    console.log(`Visit ${p.verificationUri} and enter ${p.userCode}`);\n  },\n});\n\nconst store = new FileTokenStore();        // defaults to ~/.config/swsrs/credentials.json\nawait store.save(tok);\n\nconst admin = new AdminClient({\n  baseURL: \"https://relay.example.com\",\n  token: async () => {\n    const t = await store.load();\n    if (!t) throw new Error(\"no token; run device login again\");\n    return t.access_token;\n  },\n});\n```\n\n`discoverConfig()` throws `AuthDisabledError` when the server is running\nwith `--no-auth` — callers can treat that as \"no token needed\".\n\n### Browser caveat\n\n`deviceLogin()` runs in Node and in Electron, but most IdPs **don't enable\nCORS** on the token endpoint for device flow (it was designed for CLIs).\nIf you call it from a browser context you'll see a CORS error. Browser\napps should run their own auth-code + PKCE flow with an IdP-supported\nlibrary and pass the resulting access token to `AdminClient.token`.\n\n## Known limitations\n\n- **No transparent reconnect** within the peer-wait grace window.\n- **No application-layer pings** (browsers can't send WS protocol pings). For\n  long-lived sessions, ensure the other peer or the server pings.\n\n## License\n\nMIT.\n","readmeFilename":"README.md"}