Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | 4x 4x 4x 33x 33x 33x 384x 384x 384x 384x 384x 384x 22x 22x 362x 384x 51x 50x 4x 50x 258x 8x 14x 14x 8x | const DefaultBase = "/api/rendezvous";
const SecretHeader = "x-host-secret";
const AnswerPending = 202;
export class RendezvousError extends Error {
constructor(message, status) {
super(message);
this.name = "RendezvousError";
this.status = status;
}
}
/**
* The browser's side of the rendezvous API (rendezvous/handler.js).
*
* @param {object} [options]
* @param {string} [options.base]
* @param {typeof fetch} [options.fetch]
*/
export function createRendezvousClient({ base = DefaultBase, fetch: fetchFn = globalThis.fetch } = {}) {
async function request(what, method, path, { secret, body, keepalive = false } = {}) {
const headers = {};
if (secret !== undefined) headers[SecretHeader] = secret;
if (body !== undefined) headers["content-type"] = "application/json";
const response = await fetchFn(`${base}${path}`, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
cache: "no-store",
keepalive,
});
const json = await response.json().catch(() => ({}));
if (!response.ok) {
const detail = json.error ? `: ${json.error}` : "";
throw new RendezvousError(
`Rendezvous ${what} failed with status ${response.status}${detail}`,
response.status,
);
}
return { status: response.status, json };
}
const roomPath = (room) => `/room/${encodeURIComponent(room)}`;
return {
/** @returns {Promise<string>} the host secret */
async createRoom(room) {
return (await request("create room", "POST", roomPath(room))).json.secret;
},
/** Survives the page unloading, so a host can call it as it goes. */
async deleteRoom(room, secret) {
await request("delete room", "DELETE", roomPath(room), { secret, keepalive: true });
},
async postOffer(room, guest, sdp) {
await request("post offer", "POST", `${roomPath(room)}/offer`, { body: { guest, sdp } });
},
/** @returns {Promise<{guest: string, sdp: string}[]>} offers not yet answered */
async listOffers(room, secret) {
return (await request("list offers", "GET", `${roomPath(room)}/offers`, { secret })).json.offers;
},
async postAnswer(room, secret, guest, sdp) {
await request("post answer", "POST", `${roomPath(room)}/answer`, { secret, body: { guest, sdp } });
},
/** @returns {Promise<string | null>} the answer's SDP, or null while the host has not answered */
async getAnswer(room, guest) {
const path = `${roomPath(room)}/answer/${encodeURIComponent(guest)}`;
const { status, json } = await request("get answer", "GET", path);
return status === AnswerPending ? null : json.sdp;
},
};
}
|