[{"data":1,"prerenderedAt":2034},["ShallowReactive",2],{"docs:/docs/high-level-sdks/signal":3},{"id":4,"title":5,"body":6,"description":17,"extension":2028,"meta":2029,"navigation":202,"path":2030,"seo":2031,"stem":2032,"__hash__":2033},"docs/docs/high-level-sdks/signal.md","Signal SDK",{"type":7,"value":8,"toc":2016},"minimark",[9,14,18,23,26,36,41,60,64,96,126,134,161,334,338,362,384,388,1359,1363,1366,1369,1374,1468,1472,1599,1603,1714,1717,1883,1897,1900,1984,2012],[10,11,13],"h1",{"id":12},"nolagsignal","@nolag/signal",[15,16,17],"p",{},"WebRTC signalling over NoLag: peer discovery, offer/answer exchange, and ICE candidate relay.",[19,20,22],"h2",{"id":21},"overview","Overview",[15,24,25],{},"WebRTC needs a signalling channel before a peer connection can exist. Two browsers cannot exchange session descriptions or ICE candidates over WebRTC itself, because that is the very thing they are negotiating. Something else has to carry those messages, and it has to know who is in the call.",[15,27,28,31,32,35],{},[29,30,13],"code",{}," is that channel. It handles peer discovery through presence, delivers offers, answers and ICE candidates addressed to a specific peer, and tells you when someone joins or leaves. It does not touch ",[29,33,34],{},"RTCPeerConnection",": the media is yours, this moves the negotiation.",[37,38,40],"h3",{"id":39},"key-features","Key Features",[42,43,44,48,51,54,57],"ul",{},[45,46,47],"li",{},"Peer discovery through room presence, so you know who to call",[45,49,50],{},"Offer, answer, ICE candidate and hangup messages addressed to one peer",[45,52,53],{},"Join and leave events driven by presence",[45,55,56],{},"Arbitrary metadata per peer, for display names or capabilities",[45,58,59],{},"Automatic reconnection with presence restored",[19,61,63],{"id":62},"how-it-works","How It Works",[15,65,66,69,70,73,74,77,78,81,82,85,86,89,90,95],{},[29,67,68],{},"NoLagSignal"," attaches to an injected ",[29,71,72],{},"@nolag/js-sdk"," client. Joining a room returns a ",[29,75,76],{},"SignalRoom",", which subscribes to one topic and advertises the local peer through presence. Every message carries a ",[29,79,80],{},"toPeerId",". By default the broker delivers it to every peer in the room and each receiver drops anything not addressed to it, which is fine for a handful of participants. To make the broker do the addressing instead, join with your own peer id as a subscription filter (",[29,83,84],{},"signal.joinRoom('call-room', { filters: [signal.localPeer!.peerId] })",") and send with ",[29,87,88],{},"{ filter: peer.peerId }","; a filtered peer no longer receives unfiltered broadcasts, so adopt it on every peer or none. See ",[91,92,94],"a",{"href":93},"/docs/concepts/filters","Filters",".",[97,98,99,112],"table",{},[100,101,102],"thead",{},[103,104,105,109],"tr",{},[106,107,108],"th",{},"Topic",[106,110,111],{},"Purpose",[113,114,115],"tbody",{},[103,116,117,123],{},[118,119,120],"td",{},[29,121,122],{},"signaling",[118,124,125],{},"Offers, answers, ICE candidates, renegotiation requests, hangup",[15,127,128,129,133],{},"Signalling is deliberately ephemeral, and the broker never replays it: a replayed offer is worse than no offer, because the peer connection it referred to is long gone. See ",[91,130,132],{"href":131},"/docs/concepts/replay","Replay"," for the one case where replay applies.",[135,136,138],"callout",{"type":137},"info",[15,139,140,144,145,148,149,152,153,156,157,160],{},[141,142,143],"strong",{},"Before you start."," Create an app from the ",[29,146,147],{},"nolag-signal-sdk"," blueprint. That seeds the room this SDK expects (",[29,150,151],{},"call-room",") and the ",[29,154,155],{},"online"," lobby, and returns an app slug with a random suffix. That slug is the ",[29,158,159],{},"appName"," you pass to the wrapper. You can also do this in the portal: Apps, New App, pick the blueprint.",[162,163,164],"code-tabs",{},[165,166,172],"pre",{"className":167,"code":168,"filename":169,"language":170,"meta":171,"style":171},"language-typescript shiki shiki-themes github-light github-dark","import { NoLagApi } from \"@nolag/js-sdk\";\n\nconst api = new NoLagApi(process.env.NOLAG_API_KEY); // nlg_live_...\nconst app = await api.apps.create({ name: \"My App\", blueprintId: \"nolag-signal-sdk\" });\nconsole.log(app.slug); // e.g. \"my-app-a3f9\": this is your appName\n\nconst actor = await api.actors.create({ name: \"web-client\", actorType: \"user\" });\nconsole.log(actor.accessToken); // shown once; keep it\n","Setup (server side, once)","typescript","",[29,173,174,197,204,237,271,286,291,321],{"__ignoreMap":171},[175,176,179,183,187,190,194],"span",{"class":177,"line":178},"line",1,[175,180,182],{"class":181},"szBVR","import",[175,184,186],{"class":185},"sVt8B"," { NoLagApi } ",[175,188,189],{"class":181},"from",[175,191,193],{"class":192},"sZZnC"," \"@nolag/js-sdk\"",[175,195,196],{"class":185},";\n",[175,198,200],{"class":177,"line":199},2,[175,201,203],{"emptyLinePlaceholder":202},true,"\n",[175,205,207,210,214,217,220,224,227,230,233],{"class":177,"line":206},3,[175,208,209],{"class":181},"const",[175,211,213],{"class":212},"sj4cs"," api",[175,215,216],{"class":181}," =",[175,218,219],{"class":181}," new",[175,221,223],{"class":222},"sScJk"," NoLagApi",[175,225,226],{"class":185},"(process.env.",[175,228,229],{"class":212},"NOLAG_API_KEY",[175,231,232],{"class":185},"); ",[175,234,236],{"class":235},"sJ8bj","// nlg_live_...\n",[175,238,240,242,245,247,250,253,256,259,262,265,268],{"class":177,"line":239},4,[175,241,209],{"class":181},[175,243,244],{"class":212}," app",[175,246,216],{"class":181},[175,248,249],{"class":181}," await",[175,251,252],{"class":185}," api.apps.",[175,254,255],{"class":222},"create",[175,257,258],{"class":185},"({ name: ",[175,260,261],{"class":192},"\"My App\"",[175,263,264],{"class":185},", blueprintId: ",[175,266,267],{"class":192},"\"nolag-signal-sdk\"",[175,269,270],{"class":185}," });\n",[175,272,274,277,280,283],{"class":177,"line":273},5,[175,275,276],{"class":185},"console.",[175,278,279],{"class":222},"log",[175,281,282],{"class":185},"(app.slug); ",[175,284,285],{"class":235},"// e.g. \"my-app-a3f9\": this is your appName\n",[175,287,289],{"class":177,"line":288},6,[175,290,203],{"emptyLinePlaceholder":202},[175,292,294,296,299,301,303,306,308,310,313,316,319],{"class":177,"line":293},7,[175,295,209],{"class":181},[175,297,298],{"class":212}," actor",[175,300,216],{"class":181},[175,302,249],{"class":181},[175,304,305],{"class":185}," api.actors.",[175,307,255],{"class":222},[175,309,258],{"class":185},[175,311,312],{"class":192},"\"web-client\"",[175,314,315],{"class":185},", actorType: ",[175,317,318],{"class":192},"\"user\"",[175,320,270],{"class":185},[175,322,324,326,328,331],{"class":177,"line":323},8,[175,325,276],{"class":185},[175,327,279],{"class":222},[175,329,330],{"class":185},"(actor.accessToken); ",[175,332,333],{"class":235},"// shown once; keep it\n",[19,335,337],{"id":336},"installation","Installation",[162,339,340],{},[165,341,346],{"className":342,"code":343,"filename":344,"language":345,"meta":171,"style":171},"language-bash shiki shiki-themes github-light github-dark","npm install @nolag/signal @nolag/js-sdk\n","Terminal","bash",[29,347,348],{"__ignoreMap":171},[175,349,350,353,356,359],{"class":177,"line":178},[175,351,352],{"class":222},"npm",[175,354,355],{"class":192}," install",[175,357,358],{"class":192}," @nolag/signal",[175,360,361],{"class":192}," @nolag/js-sdk\n",[135,363,364],{"type":137},[15,365,366,369,370,372,373,376,377,380,381,95],{},[141,367,368],{},"Shared connection."," One core NoLag client can back several wrapper SDKs at once, as long as each uses a distinct ",[29,371,159],{},". Each wrapper attaches its handlers on construction and releases them with ",[29,374,375],{},"detach()",", and never touches the socket itself. Your app owns ",[29,378,379],{},"connect()"," and ",[29,382,383],{},"disconnect()",[19,385,387],{"id":386},"quick-start","Quick Start",[162,389,390],{},[165,391,394],{"className":167,"code":392,"filename":393,"language":170,"meta":171,"style":171},"import { NoLag } from '@nolag/js-sdk'\nimport { NoLagSignal } from '@nolag/signal'\n\nconst client = NoLag(async () => (await (await fetch('/api/nolag-token')).json()).token)\n\nconst signal = new NoLagSignal({\n  client,\n  appName: APP_SLUG, // the slug returned when you created the app\n  metadata: { displayName: 'Alice' },\n})\n\nawait client.connect()\nawait signal.ready()\n\n// Join the seeded room\nconst room = signal.joinRoom('call-room')\n\n// One RTCPeerConnection per remote peer, created on demand.\nconst peers = new Map\u003Cstring, RTCPeerConnection>()\n\nfunction peerConnectionFor(peerId: string): RTCPeerConnection {\n  let pc = peers.get(peerId)\n  if (pc) return pc\n  pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })\n  // Trickle ICE as candidates are discovered.\n  pc.onicecandidate = ({ candidate }) => {\n    if (candidate) room.sendIceCandidate(peerId, candidate)\n  }\n  peers.set(peerId, pc)\n  return pc\n}\n\nfunction teardown(peerId: string) {\n  peers.get(peerId)?.close()\n  peers.delete(peerId)\n}\n\n// Someone new arrived: offer them a connection.\nroom.on('peerJoined', async (peer) => {\n  const pc = peerConnectionFor(peer.peerId)\n  const offer = await pc.createOffer()\n  await pc.setLocalDescription(offer)\n  room.sendOffer(peer.peerId, offer)\n})\n\n// Everything addressed to us arrives here.\nroom.on('signal', async (message) => {\n  const pc = peerConnectionFor(message.fromPeerId)\n\n  if (message.type === 'offer') {\n    await pc.setRemoteDescription(message.payload as RTCSessionDescriptionInit)\n    const answer = await pc.createAnswer()\n    await pc.setLocalDescription(answer)\n    room.sendAnswer(message.fromPeerId, answer)\n  }\n\n  if (message.type === 'answer') {\n    await pc.setRemoteDescription(message.payload as RTCSessionDescriptionInit)\n  }\n\n  if (message.type === 'ice-candidate') {\n    await pc.addIceCandidate(message.payload as RTCIceCandidateInit)\n  }\n\n  // A hangup is a signal, not a presence change: peerLeft does not fire for it.\n  if (message.type === 'bye') {\n    teardown(message.fromPeerId)\n  }\n})\n\n// Presence says they are gone (closed tab, lost connection).\nroom.on('peerLeft', (peer) => teardown(peer.peerId))\n\n// Hang up on everyone, then release the wrapper and close the socket.\nfor (const peerId of peers.keys()) {\n  room.sendBye(peerId)\n  teardown(peerId)\n}\nsignal.detach()\nclient.disconnect()\n","TypeScript",[29,395,396,408,420,424,475,479,496,501,515,527,533,538,552,565,570,576,599,604,610,638,643,675,696,711,732,738,762,777,783,795,803,809,814,833,848,858,863,868,874,904,920,940,954,966,971,976,982,1009,1023,1028,1044,1066,1086,1098,1110,1115,1120,1134,1151,1156,1161,1175,1194,1199,1204,1210,1224,1232,1237,1242,1247,1253,1279,1284,1290,1314,1324,1332,1337,1348],{"__ignoreMap":171},[175,397,398,400,403,405],{"class":177,"line":178},[175,399,182],{"class":181},[175,401,402],{"class":185}," { NoLag } ",[175,404,189],{"class":181},[175,406,407],{"class":192}," '@nolag/js-sdk'\n",[175,409,410,412,415,417],{"class":177,"line":199},[175,411,182],{"class":181},[175,413,414],{"class":185}," { NoLagSignal } ",[175,416,189],{"class":181},[175,418,419],{"class":192}," '@nolag/signal'\n",[175,421,422],{"class":177,"line":206},[175,423,203],{"emptyLinePlaceholder":202},[175,425,426,428,431,433,436,439,442,445,448,451,454,456,458,461,463,466,469,472],{"class":177,"line":239},[175,427,209],{"class":181},[175,429,430],{"class":212}," client",[175,432,216],{"class":181},[175,434,435],{"class":222}," NoLag",[175,437,438],{"class":185},"(",[175,440,441],{"class":181},"async",[175,443,444],{"class":185}," () ",[175,446,447],{"class":181},"=>",[175,449,450],{"class":185}," (",[175,452,453],{"class":181},"await",[175,455,450],{"class":185},[175,457,453],{"class":181},[175,459,460],{"class":222}," fetch",[175,462,438],{"class":185},[175,464,465],{"class":192},"'/api/nolag-token'",[175,467,468],{"class":185},")).",[175,470,471],{"class":222},"json",[175,473,474],{"class":185},"()).token)\n",[175,476,477],{"class":177,"line":273},[175,478,203],{"emptyLinePlaceholder":202},[175,480,481,483,486,488,490,493],{"class":177,"line":288},[175,482,209],{"class":181},[175,484,485],{"class":212}," signal",[175,487,216],{"class":181},[175,489,219],{"class":181},[175,491,492],{"class":222}," NoLagSignal",[175,494,495],{"class":185},"({\n",[175,497,498],{"class":177,"line":293},[175,499,500],{"class":185},"  client,\n",[175,502,503,506,509,512],{"class":177,"line":323},[175,504,505],{"class":185},"  appName: ",[175,507,508],{"class":212},"APP_SLUG",[175,510,511],{"class":185},", ",[175,513,514],{"class":235},"// the slug returned when you created the app\n",[175,516,518,521,524],{"class":177,"line":517},9,[175,519,520],{"class":185},"  metadata: { displayName: ",[175,522,523],{"class":192},"'Alice'",[175,525,526],{"class":185}," },\n",[175,528,530],{"class":177,"line":529},10,[175,531,532],{"class":185},"})\n",[175,534,536],{"class":177,"line":535},11,[175,537,203],{"emptyLinePlaceholder":202},[175,539,541,543,546,549],{"class":177,"line":540},12,[175,542,453],{"class":181},[175,544,545],{"class":185}," client.",[175,547,548],{"class":222},"connect",[175,550,551],{"class":185},"()\n",[175,553,555,557,560,563],{"class":177,"line":554},13,[175,556,453],{"class":181},[175,558,559],{"class":185}," signal.",[175,561,562],{"class":222},"ready",[175,564,551],{"class":185},[175,566,568],{"class":177,"line":567},14,[175,569,203],{"emptyLinePlaceholder":202},[175,571,573],{"class":177,"line":572},15,[175,574,575],{"class":235},"// Join the seeded room\n",[175,577,579,581,584,586,588,591,593,596],{"class":177,"line":578},16,[175,580,209],{"class":181},[175,582,583],{"class":212}," room",[175,585,216],{"class":181},[175,587,559],{"class":185},[175,589,590],{"class":222},"joinRoom",[175,592,438],{"class":185},[175,594,595],{"class":192},"'call-room'",[175,597,598],{"class":185},")\n",[175,600,602],{"class":177,"line":601},17,[175,603,203],{"emptyLinePlaceholder":202},[175,605,607],{"class":177,"line":606},18,[175,608,609],{"class":235},"// One RTCPeerConnection per remote peer, created on demand.\n",[175,611,613,615,618,620,622,625,628,631,633,635],{"class":177,"line":612},19,[175,614,209],{"class":181},[175,616,617],{"class":212}," peers",[175,619,216],{"class":181},[175,621,219],{"class":181},[175,623,624],{"class":222}," Map",[175,626,627],{"class":185},"\u003C",[175,629,630],{"class":212},"string",[175,632,511],{"class":185},[175,634,34],{"class":222},[175,636,637],{"class":185},">()\n",[175,639,641],{"class":177,"line":640},20,[175,642,203],{"emptyLinePlaceholder":202},[175,644,646,649,652,654,658,661,664,667,669,672],{"class":177,"line":645},21,[175,647,648],{"class":181},"function",[175,650,651],{"class":222}," peerConnectionFor",[175,653,438],{"class":185},[175,655,657],{"class":656},"s4XuR","peerId",[175,659,660],{"class":181},":",[175,662,663],{"class":212}," string",[175,665,666],{"class":185},")",[175,668,660],{"class":181},[175,670,671],{"class":222}," RTCPeerConnection",[175,673,674],{"class":185}," {\n",[175,676,678,681,684,687,690,693],{"class":177,"line":677},22,[175,679,680],{"class":181},"  let",[175,682,683],{"class":185}," pc ",[175,685,686],{"class":181},"=",[175,688,689],{"class":185}," peers.",[175,691,692],{"class":222},"get",[175,694,695],{"class":185},"(peerId)\n",[175,697,699,702,705,708],{"class":177,"line":698},23,[175,700,701],{"class":181},"  if",[175,703,704],{"class":185}," (pc) ",[175,706,707],{"class":181},"return",[175,709,710],{"class":185}," pc\n",[175,712,714,717,719,721,723,726,729],{"class":177,"line":713},24,[175,715,716],{"class":185},"  pc ",[175,718,686],{"class":181},[175,720,219],{"class":181},[175,722,671],{"class":222},[175,724,725],{"class":185},"({ iceServers: [{ urls: ",[175,727,728],{"class":192},"'stun:stun.l.google.com:19302'",[175,730,731],{"class":185}," }] })\n",[175,733,735],{"class":177,"line":734},25,[175,736,737],{"class":235},"  // Trickle ICE as candidates are discovered.\n",[175,739,741,744,747,749,752,755,758,760],{"class":177,"line":740},26,[175,742,743],{"class":185},"  pc.",[175,745,746],{"class":222},"onicecandidate",[175,748,216],{"class":181},[175,750,751],{"class":185}," ({ ",[175,753,754],{"class":656},"candidate",[175,756,757],{"class":185}," }) ",[175,759,447],{"class":181},[175,761,674],{"class":185},[175,763,765,768,771,774],{"class":177,"line":764},27,[175,766,767],{"class":181},"    if",[175,769,770],{"class":185}," (candidate) room.",[175,772,773],{"class":222},"sendIceCandidate",[175,775,776],{"class":185},"(peerId, candidate)\n",[175,778,780],{"class":177,"line":779},28,[175,781,782],{"class":185},"  }\n",[175,784,786,789,792],{"class":177,"line":785},29,[175,787,788],{"class":185},"  peers.",[175,790,791],{"class":222},"set",[175,793,794],{"class":185},"(peerId, pc)\n",[175,796,798,801],{"class":177,"line":797},30,[175,799,800],{"class":181},"  return",[175,802,710],{"class":185},[175,804,806],{"class":177,"line":805},31,[175,807,808],{"class":185},"}\n",[175,810,812],{"class":177,"line":811},32,[175,813,203],{"emptyLinePlaceholder":202},[175,815,817,819,822,824,826,828,830],{"class":177,"line":816},33,[175,818,648],{"class":181},[175,820,821],{"class":222}," teardown",[175,823,438],{"class":185},[175,825,657],{"class":656},[175,827,660],{"class":181},[175,829,663],{"class":212},[175,831,832],{"class":185},") {\n",[175,834,836,838,840,843,846],{"class":177,"line":835},34,[175,837,788],{"class":185},[175,839,692],{"class":222},[175,841,842],{"class":185},"(peerId)?.",[175,844,845],{"class":222},"close",[175,847,551],{"class":185},[175,849,851,853,856],{"class":177,"line":850},35,[175,852,788],{"class":185},[175,854,855],{"class":222},"delete",[175,857,695],{"class":185},[175,859,861],{"class":177,"line":860},36,[175,862,808],{"class":185},[175,864,866],{"class":177,"line":865},37,[175,867,203],{"emptyLinePlaceholder":202},[175,869,871],{"class":177,"line":870},38,[175,872,873],{"class":235},"// Someone new arrived: offer them a connection.\n",[175,875,877,880,883,885,888,890,892,894,897,900,902],{"class":177,"line":876},39,[175,878,879],{"class":185},"room.",[175,881,882],{"class":222},"on",[175,884,438],{"class":185},[175,886,887],{"class":192},"'peerJoined'",[175,889,511],{"class":185},[175,891,441],{"class":181},[175,893,450],{"class":185},[175,895,896],{"class":656},"peer",[175,898,899],{"class":185},") ",[175,901,447],{"class":181},[175,903,674],{"class":185},[175,905,907,910,913,915,917],{"class":177,"line":906},40,[175,908,909],{"class":181},"  const",[175,911,912],{"class":212}," pc",[175,914,216],{"class":181},[175,916,651],{"class":222},[175,918,919],{"class":185},"(peer.peerId)\n",[175,921,923,925,928,930,932,935,938],{"class":177,"line":922},41,[175,924,909],{"class":181},[175,926,927],{"class":212}," offer",[175,929,216],{"class":181},[175,931,249],{"class":181},[175,933,934],{"class":185}," pc.",[175,936,937],{"class":222},"createOffer",[175,939,551],{"class":185},[175,941,943,946,948,951],{"class":177,"line":942},42,[175,944,945],{"class":181},"  await",[175,947,934],{"class":185},[175,949,950],{"class":222},"setLocalDescription",[175,952,953],{"class":185},"(offer)\n",[175,955,957,960,963],{"class":177,"line":956},43,[175,958,959],{"class":185},"  room.",[175,961,962],{"class":222},"sendOffer",[175,964,965],{"class":185},"(peer.peerId, offer)\n",[175,967,969],{"class":177,"line":968},44,[175,970,532],{"class":185},[175,972,974],{"class":177,"line":973},45,[175,975,203],{"emptyLinePlaceholder":202},[175,977,979],{"class":177,"line":978},46,[175,980,981],{"class":235},"// Everything addressed to us arrives here.\n",[175,983,985,987,989,991,994,996,998,1000,1003,1005,1007],{"class":177,"line":984},47,[175,986,879],{"class":185},[175,988,882],{"class":222},[175,990,438],{"class":185},[175,992,993],{"class":192},"'signal'",[175,995,511],{"class":185},[175,997,441],{"class":181},[175,999,450],{"class":185},[175,1001,1002],{"class":656},"message",[175,1004,899],{"class":185},[175,1006,447],{"class":181},[175,1008,674],{"class":185},[175,1010,1012,1014,1016,1018,1020],{"class":177,"line":1011},48,[175,1013,909],{"class":181},[175,1015,912],{"class":212},[175,1017,216],{"class":181},[175,1019,651],{"class":222},[175,1021,1022],{"class":185},"(message.fromPeerId)\n",[175,1024,1026],{"class":177,"line":1025},49,[175,1027,203],{"emptyLinePlaceholder":202},[175,1029,1031,1033,1036,1039,1042],{"class":177,"line":1030},50,[175,1032,701],{"class":181},[175,1034,1035],{"class":185}," (message.type ",[175,1037,1038],{"class":181},"===",[175,1040,1041],{"class":192}," 'offer'",[175,1043,832],{"class":185},[175,1045,1047,1050,1052,1055,1058,1061,1064],{"class":177,"line":1046},51,[175,1048,1049],{"class":181},"    await",[175,1051,934],{"class":185},[175,1053,1054],{"class":222},"setRemoteDescription",[175,1056,1057],{"class":185},"(message.payload ",[175,1059,1060],{"class":181},"as",[175,1062,1063],{"class":222}," RTCSessionDescriptionInit",[175,1065,598],{"class":185},[175,1067,1069,1072,1075,1077,1079,1081,1084],{"class":177,"line":1068},52,[175,1070,1071],{"class":181},"    const",[175,1073,1074],{"class":212}," answer",[175,1076,216],{"class":181},[175,1078,249],{"class":181},[175,1080,934],{"class":185},[175,1082,1083],{"class":222},"createAnswer",[175,1085,551],{"class":185},[175,1087,1089,1091,1093,1095],{"class":177,"line":1088},53,[175,1090,1049],{"class":181},[175,1092,934],{"class":185},[175,1094,950],{"class":222},[175,1096,1097],{"class":185},"(answer)\n",[175,1099,1101,1104,1107],{"class":177,"line":1100},54,[175,1102,1103],{"class":185},"    room.",[175,1105,1106],{"class":222},"sendAnswer",[175,1108,1109],{"class":185},"(message.fromPeerId, answer)\n",[175,1111,1113],{"class":177,"line":1112},55,[175,1114,782],{"class":185},[175,1116,1118],{"class":177,"line":1117},56,[175,1119,203],{"emptyLinePlaceholder":202},[175,1121,1123,1125,1127,1129,1132],{"class":177,"line":1122},57,[175,1124,701],{"class":181},[175,1126,1035],{"class":185},[175,1128,1038],{"class":181},[175,1130,1131],{"class":192}," 'answer'",[175,1133,832],{"class":185},[175,1135,1137,1139,1141,1143,1145,1147,1149],{"class":177,"line":1136},58,[175,1138,1049],{"class":181},[175,1140,934],{"class":185},[175,1142,1054],{"class":222},[175,1144,1057],{"class":185},[175,1146,1060],{"class":181},[175,1148,1063],{"class":222},[175,1150,598],{"class":185},[175,1152,1154],{"class":177,"line":1153},59,[175,1155,782],{"class":185},[175,1157,1159],{"class":177,"line":1158},60,[175,1160,203],{"emptyLinePlaceholder":202},[175,1162,1164,1166,1168,1170,1173],{"class":177,"line":1163},61,[175,1165,701],{"class":181},[175,1167,1035],{"class":185},[175,1169,1038],{"class":181},[175,1171,1172],{"class":192}," 'ice-candidate'",[175,1174,832],{"class":185},[175,1176,1178,1180,1182,1185,1187,1189,1192],{"class":177,"line":1177},62,[175,1179,1049],{"class":181},[175,1181,934],{"class":185},[175,1183,1184],{"class":222},"addIceCandidate",[175,1186,1057],{"class":185},[175,1188,1060],{"class":181},[175,1190,1191],{"class":222}," RTCIceCandidateInit",[175,1193,598],{"class":185},[175,1195,1197],{"class":177,"line":1196},63,[175,1198,782],{"class":185},[175,1200,1202],{"class":177,"line":1201},64,[175,1203,203],{"emptyLinePlaceholder":202},[175,1205,1207],{"class":177,"line":1206},65,[175,1208,1209],{"class":235},"  // A hangup is a signal, not a presence change: peerLeft does not fire for it.\n",[175,1211,1213,1215,1217,1219,1222],{"class":177,"line":1212},66,[175,1214,701],{"class":181},[175,1216,1035],{"class":185},[175,1218,1038],{"class":181},[175,1220,1221],{"class":192}," 'bye'",[175,1223,832],{"class":185},[175,1225,1227,1230],{"class":177,"line":1226},67,[175,1228,1229],{"class":222},"    teardown",[175,1231,1022],{"class":185},[175,1233,1235],{"class":177,"line":1234},68,[175,1236,782],{"class":185},[175,1238,1240],{"class":177,"line":1239},69,[175,1241,532],{"class":185},[175,1243,1245],{"class":177,"line":1244},70,[175,1246,203],{"emptyLinePlaceholder":202},[175,1248,1250],{"class":177,"line":1249},71,[175,1251,1252],{"class":235},"// Presence says they are gone (closed tab, lost connection).\n",[175,1254,1256,1258,1260,1262,1265,1268,1270,1272,1274,1276],{"class":177,"line":1255},72,[175,1257,879],{"class":185},[175,1259,882],{"class":222},[175,1261,438],{"class":185},[175,1263,1264],{"class":192},"'peerLeft'",[175,1266,1267],{"class":185},", (",[175,1269,896],{"class":656},[175,1271,899],{"class":185},[175,1273,447],{"class":181},[175,1275,821],{"class":222},[175,1277,1278],{"class":185},"(peer.peerId))\n",[175,1280,1282],{"class":177,"line":1281},73,[175,1283,203],{"emptyLinePlaceholder":202},[175,1285,1287],{"class":177,"line":1286},74,[175,1288,1289],{"class":235},"// Hang up on everyone, then release the wrapper and close the socket.\n",[175,1291,1293,1296,1298,1300,1303,1306,1308,1311],{"class":177,"line":1292},75,[175,1294,1295],{"class":181},"for",[175,1297,450],{"class":185},[175,1299,209],{"class":181},[175,1301,1302],{"class":212}," peerId",[175,1304,1305],{"class":181}," of",[175,1307,689],{"class":185},[175,1309,1310],{"class":222},"keys",[175,1312,1313],{"class":185},"()) {\n",[175,1315,1317,1319,1322],{"class":177,"line":1316},76,[175,1318,959],{"class":185},[175,1320,1321],{"class":222},"sendBye",[175,1323,695],{"class":185},[175,1325,1327,1330],{"class":177,"line":1326},77,[175,1328,1329],{"class":222},"  teardown",[175,1331,695],{"class":185},[175,1333,1335],{"class":177,"line":1334},78,[175,1336,808],{"class":185},[175,1338,1340,1343,1346],{"class":177,"line":1339},79,[175,1341,1342],{"class":185},"signal.",[175,1344,1345],{"class":222},"detach",[175,1347,551],{"class":185},[175,1349,1351,1354,1357],{"class":177,"line":1350},80,[175,1352,1353],{"class":185},"client.",[175,1355,1356],{"class":222},"disconnect",[175,1358,551],{"class":185},[19,1360,1362],{"id":1361},"api-reference","API Reference",[37,1364,68],{"id":1365},"nolagsignal-1",[15,1367,1368],{},"The main class. Attaches to the injected core client and manages the room lifecycle.",[1370,1371,1373],"h4",{"id":1372},"constructor-options","Constructor Options",[97,1375,1376,1392],{},[100,1377,1378],{},[103,1379,1380,1383,1386,1389],{},[106,1381,1382],{},"Option",[106,1384,1385],{},"Type",[106,1387,1388],{},"Default",[106,1390,1391],{},"Description",[113,1393,1394,1412,1430,1448],{},[103,1395,1396,1401,1406,1409],{},[118,1397,1398],{},[29,1399,1400],{},"client",[118,1402,1403],{},[29,1404,1405],{},"NoLagSocket",[118,1407,1408],{},"required",[118,1410,1411],{},"The injected core client",[103,1413,1414,1419,1424,1427],{},[118,1415,1416],{},[29,1417,1418],{},"metadata",[118,1420,1421],{},[29,1422,1423],{},"Record\u003Cstring, unknown>",[118,1425,1426],{},"none",[118,1428,1429],{},"Advertised with this peer's presence",[103,1431,1432,1436,1440,1445],{},[118,1433,1434],{},[29,1435,159],{},[118,1437,1438],{},[29,1439,630],{},[118,1441,1442],{},[29,1443,1444],{},"\"signal\"",[118,1446,1447],{},"The app slug returned when you created the app; the default only works if an app with exactly that slug exists",[103,1449,1450,1455,1460,1465],{},[118,1451,1452],{},[29,1453,1454],{},"debug",[118,1456,1457],{},[29,1458,1459],{},"boolean",[118,1461,1462],{},[29,1463,1464],{},"false",[118,1466,1467],{},"Verbose logging",[1370,1469,1471],{"id":1470},"methods","Methods",[97,1473,1474,1486],{},[100,1475,1476],{},[103,1477,1478,1481,1484],{},[106,1479,1480],{},"Method",[106,1482,1483],{},"Returns",[106,1485,1391],{},[113,1487,1488,1503,1523,1538,1553,1568,1586],{},[103,1489,1490,1495,1500],{},[118,1491,1492],{},[29,1493,1494],{},"ready()",[118,1496,1497],{},[29,1498,1499],{},"Promise\u003Cvoid>",[118,1501,1502],{},"Resolves once wrapper setup has completed",[103,1504,1505,1510,1514],{},[118,1506,1507],{},[29,1508,1509],{},"joinRoom(name, opts?)",[118,1511,1512],{},[29,1513,76],{},[118,1515,1516,1517,1520,1521],{},"Join a signalling room; ",[29,1518,1519],{},"opts.filters"," limits delivery to matching signals; throws before ",[29,1522,1494],{},[103,1524,1525,1530,1535],{},[118,1526,1527],{},[29,1528,1529],{},"leaveRoom(name)",[118,1531,1532],{},[29,1533,1534],{},"void",[118,1536,1537],{},"Leave a room",[103,1539,1540,1545,1550],{},[118,1541,1542],{},[29,1543,1544],{},"getRooms()",[118,1546,1547],{},[29,1548,1549],{},"SignalRoom[]",[118,1551,1552],{},"All joined rooms",[103,1554,1555,1560,1565],{},[118,1556,1557],{},[29,1558,1559],{},"getOnlinePeers()",[118,1561,1562],{},[29,1563,1564],{},"Peer[]",[118,1566,1567],{},"Peers visible in the lobby",[103,1569,1570,1575,1580],{},[118,1571,1572],{},[29,1573,1574],{},"localPeer",[118,1576,1577],{},[29,1578,1579],{},"Peer | null",[118,1581,1582,1583,1585],{},"This peer, available once ",[29,1584,1494],{}," resolves",[103,1587,1588,1592,1596],{},[118,1589,1590],{},[29,1591,375],{},[118,1593,1594],{},[29,1595,1534],{},[118,1597,1598],{},"Release handlers and topics; never closes the socket",[1370,1600,1602],{"id":1601},"events","Events",[97,1604,1605,1617],{},[100,1606,1607],{},[103,1608,1609,1612,1615],{},[106,1610,1611],{},"Event",[106,1613,1614],{},"Payload",[106,1616,1391],{},[113,1618,1619,1631,1646,1658,1670,1685,1700],{},[103,1620,1621,1626,1628],{},[118,1622,1623],{},[29,1624,1625],{},"connected",[118,1627,1426],{},[118,1629,1630],{},"Wrapper setup completed on a fresh connection",[103,1632,1633,1638,1643],{},[118,1634,1635],{},[29,1636,1637],{},"disconnected",[118,1639,1640],{},[29,1641,1642],{},"reason: string",[118,1644,1645],{},"Connection closed",[103,1647,1648,1653,1655],{},[118,1649,1650],{},[29,1651,1652],{},"reconnecting",[118,1654,1426],{},[118,1656,1657],{},"The client is attempting to reconnect",[103,1659,1660,1665,1667],{},[118,1661,1662],{},[29,1663,1664],{},"reconnected",[118,1666,1426],{},[118,1668,1669],{},"Reconnection successful; rooms and presence restored",[103,1671,1672,1677,1682],{},[118,1673,1674],{},[29,1675,1676],{},"error",[118,1678,1679],{},[29,1680,1681],{},"error: Error",[118,1683,1684],{},"Unrecoverable error occurred",[103,1686,1687,1692,1697],{},[118,1688,1689],{},[29,1690,1691],{},"peerOnline",[118,1693,1694],{},[29,1695,1696],{},"Peer",[118,1698,1699],{},"A peer appeared in the lobby",[103,1701,1702,1707,1711],{},[118,1703,1704],{},[29,1705,1706],{},"peerOffline",[118,1708,1709],{},[29,1710,1696],{},[118,1712,1713],{},"A peer left the lobby",[37,1715,76],{"id":1716},"signalroom",[97,1718,1719,1729],{},[100,1720,1721],{},[103,1722,1723,1725,1727],{},[106,1724,1480],{},[106,1726,1483],{},[106,1728,1391],{},[113,1730,1731,1748,1762,1779,1793,1826,1840,1855,1869],{},[103,1732,1733,1738,1742],{},[118,1734,1735],{},[29,1736,1737],{},"sendOffer(toPeerId, offer, opts?)",[118,1739,1740],{},[29,1741,1534],{},[118,1743,1744,1745,927],{},"Send an ",[29,1746,1747],{},"RTCSessionDescriptionInit",[103,1749,1750,1755,1759],{},[118,1751,1752],{},[29,1753,1754],{},"sendAnswer(toPeerId, answer, opts?)",[118,1756,1757],{},[29,1758,1534],{},[118,1760,1761],{},"Send an answer",[103,1763,1764,1769,1773],{},[118,1765,1766],{},[29,1767,1768],{},"sendIceCandidate(toPeerId, candidate, opts?)",[118,1770,1771],{},[29,1772,1534],{},[118,1774,1775,1776],{},"Send one ",[29,1777,1778],{},"RTCIceCandidateInit",[103,1780,1781,1786,1790],{},[118,1782,1783],{},[29,1784,1785],{},"sendBye(toPeerId, opts?)",[118,1787,1788],{},[29,1789,1534],{},[118,1791,1792],{},"Tell a peer you are hanging up",[103,1794,1795,1800,1804],{},[118,1796,1797],{},[29,1798,1799],{},"signal(toPeerId, type, payload, opts?)",[118,1801,1802],{},[29,1803,1534],{},[118,1805,1806,1807,1810,1811,511,1814,511,1817,511,1820,511,1823],{},"Send any ",[29,1808,1809],{},"SignalType",": ",[29,1812,1813],{},"'offer'",[29,1815,1816],{},"'answer'",[29,1818,1819],{},"'ice-candidate'",[29,1821,1822],{},"'renegotiate'",[29,1824,1825],{},"'bye'",[103,1827,1828,1833,1837],{},[118,1829,1830],{},[29,1831,1832],{},"getPeers()",[118,1834,1835],{},[29,1836,1564],{},[118,1838,1839],{},"Peers currently in this room",[103,1841,1842,1847,1852],{},[118,1843,1844],{},[29,1845,1846],{},"getPeer(peerId)",[118,1848,1849],{},[29,1850,1851],{},"Peer | undefined",[118,1853,1854],{},"One peer",[103,1856,1857,1862,1866],{},[118,1858,1859],{},[29,1860,1861],{},"localPeerId",[118,1863,1864],{},[29,1865,630],{},[118,1867,1868],{},"This peer's id, the value to filter on for directed delivery",[103,1870,1871,1876,1880],{},[118,1872,1873],{},[29,1874,1875],{},"setFilters(values)",[118,1877,1878],{},[29,1879,1534],{},[118,1881,1882],{},"Replace this room's subscription filters",[15,1884,1885,1888,1889,1892,1893,1896],{},[29,1886,1887],{},"opts"," on every send method accepts ",[29,1890,1891],{},"filter"," (one value) or ",[29,1894,1895],{},"filters"," (an AND group), so the broker delivers only to peers subscribed with that value.",[1370,1898,1602],{"id":1899},"events-1",[97,1901,1902,1912],{},[100,1903,1904],{},[103,1905,1906,1908,1910],{},[106,1907,1611],{},[106,1909,1614],{},[106,1911,1391],{},[113,1913,1914,1946,1960],{},[103,1915,1916,1921,1926],{},[118,1917,1918],{},[29,1919,1920],{},"signal",[118,1922,1923],{},[29,1924,1925],{},"SignalMessage",[118,1927,1928,1929,511,1932,511,1935,511,1938,511,1940,511,1943],{},"A message addressed to you: ",[29,1930,1931],{},"id",[29,1933,1934],{},"type",[29,1936,1937],{},"fromPeerId",[29,1939,80],{},[29,1941,1942],{},"payload",[29,1944,1945],{},"timestamp",[103,1947,1948,1953,1957],{},[118,1949,1950],{},[29,1951,1952],{},"peerJoined",[118,1954,1955],{},[29,1956,1696],{},[118,1958,1959],{},"Someone's presence appeared in the room",[103,1961,1962,1967,1971],{},[118,1963,1964],{},[29,1965,1966],{},"peerLeft",[118,1968,1969],{},[29,1970,1696],{},[118,1972,1973,1974,1977,1978,1980,1981,1983],{},"Someone's presence left the room. A ",[29,1975,1976],{},"bye"," arrives as a ",[29,1979,1920],{}," of type ",[29,1982,1825],{}," instead",[135,1985,1986],{"type":137},[15,1987,1988,1991,1992,1995,1996,1999,2000,2003,2004,2007,2008,95],{},[141,1989,1990],{},"On React Native."," ",[29,1993,1994],{},"@nolag/react-native"," does not re-export ",[29,1997,1998],{},"WebRTCManager",", and the core SDK's React Native build omits it, because its Node path bare-requires ",[29,2001,2002],{},"wrtc"," and Metro would fail the build. Signalling itself works fine there; the media side needs ",[29,2005,2006],{},"react-native-webrtc",". See the ",[91,2009,2011],{"href":2010},"/docs/sdks/react-native","React Native SDK",[2013,2014,2015],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":171,"searchDepth":199,"depth":199,"links":2017},[2018,2021,2022,2023,2024],{"id":21,"depth":199,"text":22,"children":2019},[2020],{"id":39,"depth":206,"text":40},{"id":62,"depth":199,"text":63},{"id":336,"depth":199,"text":337},{"id":386,"depth":199,"text":387},{"id":1361,"depth":199,"text":1362,"children":2025},[2026,2027],{"id":1365,"depth":206,"text":68},{"id":1716,"depth":206,"text":76},"md",{},"/docs/high-level-sdks/signal",{"title":5,"description":17},"docs/high-level-sdks/signal","u1gSqrR2oVTJwUSEkljoU5a2N03zX_JQGklYMqJfp00",1789870600715]