decentralised sync engine

feat: initial implementation

serenity 7365e077 dbc793ab

Changed files
+98 -4
src
+48 -4
src/index.ts
···
-
const main = () => {
-
console.log("Hello!!");
-
};
+
import { rawDataToString } from "@/lib/utils";
+
import { validateShardMessage, type ShardMessage } from "@/lib/validator";
+
import type { RawData } from "ws";
+
import WebSocket from "ws";
+
+
const wss = new WebSocket.Server({ port: 8080 });
+
+
const messages: ShardMessage[] = []; // Store message history
+
const clients = new Set<WebSocket>();
-
main();
+
wss.on("connection", (ws) => {
+
clients.add(ws);
+
+
// Send history to new client
+
ws.send(
+
JSON.stringify({
+
type: "shard/history",
+
messages: messages,
+
}),
+
);
+
+
ws.on("message", (data: RawData) => {
+
const jsonText = rawDataToString(data);
+
const jsonData: unknown = JSON.parse(jsonText);
+
const {
+
success,
+
error,
+
data: shardMessage,
+
} = validateShardMessage(jsonData);
+
+
if (!success) {
+
console.log(error);
+
} else {
+
messages.push(shardMessage);
+
}
+
+
clients.forEach((client) => {
+
if (client.readyState === WebSocket.OPEN) {
+
client.send(JSON.stringify(shardMessage));
+
}
+
});
+
});
+
+
ws.on("close", () => {
+
clients.delete(ws);
+
});
+
});
+
+
console.log("Server running on ws://localhost:8080");
+11
src/lib/utils.ts
···
+
import type { RawData } from "ws";
+
+
export const rawDataToString = (data: RawData): string => {
+
if (Buffer.isBuffer(data)) {
+
return data.toString("utf-8");
+
}
+
if (Array.isArray(data)) {
+
return Buffer.concat(data).toString("utf-8");
+
}
+
return new TextDecoder().decode(data);
+
};
+39
src/lib/validator.ts
···
+
export function assertShardMessage(
+
json: unknown,
+
): asserts json is ShardMessage {
+
if (typeof json !== "object") {
+
throw new Error("not a js object");
+
}
+
+
const candidate = json as Record<string, unknown>;
+
+
if (candidate.type !== "shard/message") {
+
throw new Error("Invalid type");
+
}
+
+
if (typeof candidate.text !== "string") {
+
throw new Error("Invalid text");
+
}
+
+
const timestamp = new Date(candidate.timestamp as string);
+
+
if (!(timestamp instanceof Date)) {
+
throw new Error("Invalid timestamp");
+
}
+
}
+
+
// example. we will use zod in the future.
+
export const validateShardMessage = (json: unknown) => {
+
try {
+
assertShardMessage(json);
+
return { success: true, data: json };
+
} catch (e: unknown) {
+
return { error: e };
+
}
+
};
+
+
export interface ShardMessage {
+
type: "shard/message";
+
text: string;
+
timestamp: Date;
+
}