Files
multiverse/jumpa-chat/lib/xcu-moq.ts
T

129 lines
3.7 KiB
TypeScript

// [TSM.ID].[11031972] -- All Rights Reserved. Proprietary & Confidential.
export type SocketCallback = (data: unknown) => void;
/// XCU Media over QUIC (MoQ) / WebSocket Adapter
/// Menggantikan `ZeroSocket` dan `Socket.IO`
/// Fitur:
/// - Terhubung langsung ke mesin Rust (`xcu-neural-chat`) di port 8443
/// - Tidak menggunakan Redis PubSub
/// - Berjalan via WebSocket (fallback untuk WebTransport tanpa HTTPS certs)
export class XcuMoq {
private url: string;
private listeners: Record<string, SocketCallback[]> = {};
private ws: WebSocket | null = null;
public id: string;
private reconnectTimer: any;
constructor(url: string) {
this.url = url;
this.id = Math.random().toString(36).substring(2, 15);
}
connect() {
if (this.ws && (this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN)) return;
// Sambungkan langsung ke Alpha VPS atau localhost
let wsUrl = "ws://160.187.143.253:8443";
if (typeof window !== "undefined" && window.location.hostname === "localhost") {
wsUrl = "ws://127.0.0.1:8443";
}
try {
this.ws = new WebSocket(wsUrl);
this.ws.onopen = () => {
console.log("[XCU MoQ] Terhubung ke Neural Mesh Rust Engine.");
if (this.listeners["connect"]) {
this.listeners["connect"].forEach(fn => fn({}));
}
};
this.ws.onmessage = (e) => {
try {
const data = JSON.parse(e.data);
if (data.event && this.listeners[data.event]) {
this.listeners[data.event].forEach(fn => fn(data.payload));
}
} catch {
// Abaikan parse error
}
};
this.ws.onclose = () => {
console.warn("[XCU MoQ] Koneksi terputus. Neural link lost.");
if (this.listeners["disconnect"]) {
this.listeners["disconnect"].forEach(fn => fn({}));
}
// Auto-reconnect
clearTimeout(this.reconnectTimer);
this.reconnectTimer = setTimeout(() => this.connect(), 3000);
};
this.ws.onerror = (err) => {
console.error("[XCU MoQ] WebSocket Error:", err);
};
} catch (e) {
console.error("[XCU MoQ] Gagal inisialisasi:", e);
}
}
on(event: string, callback: SocketCallback) {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(callback);
return this;
}
once(event: string, callback: SocketCallback) {
const onceWrapper = (data: unknown) => {
callback(data);
this.off(event, onceWrapper);
};
this.on(event, onceWrapper);
return this;
}
off(event: string, callback?: SocketCallback) {
if (!this.listeners[event]) return this;
if (callback) {
this.listeners[event] = this.listeners[event].filter(cb => cb !== callback);
} else {
delete this.listeners[event];
}
return this;
}
emit(event: string, payload: string | Record<string, unknown> = {}) {
const normalizedPayload = typeof payload === 'string' ? { value: payload } : payload;
// Kirim pesan ke mesin Rust via WebSocket
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
const msg = JSON.stringify({
client_id: this.id,
event,
payload: normalizedPayload
});
this.ws.send(msg);
} else {
console.warn("[XCU MoQ] Pesan di-drop: Tidak terhubung ke Neural Mesh.");
}
return this;
}
disconnect() {
if (this.ws) {
this.ws.close();
this.ws = null;
}
clearTimeout(this.reconnectTimer);
}
}
export const io = (url?: string, options?: any) => {
const socket = new XcuMoq(url || "");
socket.connect();
return socket;
};
export type Socket = XcuMoq;