Template
1
0

feat: initial boilerplate

This commit is contained in:
2025-08-11 20:45:41 +02:00
parent d98524254f
commit 1215a98afc
148 changed files with 6935 additions and 2060 deletions

View File

@@ -0,0 +1,81 @@
import type { Params } from "@valkyr/json-rpc";
import { Sockets } from "./sockets.ts";
export class Channels {
readonly #channels = new Map<string, Sockets>();
/**
* Add a new channel.
*
* @param channel
*/
add(channel: string): this {
this.#channels.set(channel, new Sockets());
return this;
}
/**
* Deletes a channel.
*
* @param channel
*/
del(channel: string): this {
this.#channels.delete(channel);
return this;
}
/**
* Add socket to the given channel. If the channel does not exist it is
* automatically created.
*
* @param channel - Channel to add socket to.
* @param socket - Socket to add to the channel.
*/
join(channel: string, socket: WebSocket): this {
const sockets = this.#channels.get(channel);
if (sockets === undefined) {
this.#channels.set(channel, new Sockets().add(socket));
} else {
sockets.add(socket);
}
return this;
}
/**
* Remove a socket from the given channel.
*
* @param channel - Channel to leave.
* @param socket - Socket to remove from the channel.
*/
leave(channel: string, socket: WebSocket): this {
this.#channels.get(channel)?.del(socket);
return this;
}
/**
* Sends a JSON-RPC notification to all sockets in given channel.
*
* @param channel - Channel to emit method to.
* @param method - Method to send the notification to.
* @param params - Message data to send to the clients.
*/
notify(channel: string, method: string, params: Params): this {
this.#channels.get(channel)?.notify(method, params);
return this;
}
/**
* Transmits data to all registered WebSocket connections in the given channel.
* Data can be a string, a Blob, an ArrayBuffer, or an ArrayBufferView.
*
* @param channel - Channel to emit message to.
* @param data - Data to send to each connected socket in the channel.
*/
send(channel: string, data: string | ArrayBufferLike | Blob | ArrayBufferView): this {
this.#channels.get(channel)?.send(data);
return this;
}
}
export const channels = new Channels();

View File

@@ -0,0 +1 @@
export { Sockets } from "./sockets.ts";

View File

@@ -0,0 +1,49 @@
import type { Params } from "@valkyr/json-rpc";
export class Sockets {
readonly #sockets = new Set<WebSocket>();
/**
* Add a socket to the pool.
*
* @param socket - WebSocket to add.
*/
add(socket: WebSocket): this {
this.#sockets.add(socket);
return this;
}
/**
* Remove a socket from the pool.
*
* @param socket - WebSocket to remove.
*/
del(socket: WebSocket): this {
this.#sockets.delete(socket);
return this;
}
/**
* Sends a JSON-RPC notification to all connected sockets.
*
* @param method - Method to send the notification to.
* @param params - Message data to send to the clients.
*/
notify(method: string, params: Params): this {
this.send(JSON.stringify({ jsonrpc: "2.0", method, params }));
return this;
}
/**
* Transmits data to all registered WebSocket connections. Data can be a string,
* a Blob, an ArrayBuffer, or an ArrayBufferView.
*
* @param data - Data to send to each connected socket.
*/
send(data: string | ArrayBufferLike | Blob | ArrayBufferView): this {
this.#sockets.forEach((socket) => socket.send(data));
return this;
}
}
export const sockets = new Sockets();

View File

@@ -0,0 +1,60 @@
import { toJsonRpc } from "@valkyr/json-rpc";
import { Session } from "~libraries/auth/mod.ts";
import { logger } from "~libraries/logger/mod.ts";
import { sockets } from "./sockets.ts";
export function upgrade(request: Request, session?: Session) {
const { socket, response } = Deno.upgradeWebSocket(request);
socket.addEventListener("open", () => {
logger.prefix("Socket").info("socket connected", { session });
sockets.add(socket);
});
socket.addEventListener("close", () => {
logger.prefix("Socket").info("socket disconnected", { session });
sockets.del(socket);
});
socket.addEventListener("message", (event) => {
if (event.data === "ping") {
return;
}
const body = toJsonRpc(event.data);
logger.prefix("Socket").info(body);
asyncLocalStorage.run(
{
session,
info: {
method: body.method!,
start: Date.now(),
},
socket,
response: {
headers: new Headers(),
},
},
async () => {
api
.handleCommand(body)
.then((response) => {
if (response !== undefined) {
logger.info({ response });
socket.send(JSON.stringify(response));
}
})
.catch((error) => {
logger.info({ error });
socket.send(JSON.stringify(error));
});
},
);
});
return response;
}