wip fix yjs

This commit is contained in:
Rizky 2023-11-04 02:50:29 +07:00
parent 8f2b07872c
commit cf01c62c88
23 changed files with 98 additions and 83 deletions

View File

@ -1,5 +1,4 @@
import { syncronize } from "y-pojo"; import { syncronize } from "y-pojo";
import * as Y from "yjs";
import { MPage } from "../../web/src/utils/types/general"; import { MPage } from "../../web/src/utils/types/general";
import { WS_MSG_SET_PAGE, WS_MSG_SV_LOCAL } from "../../web/src/utils/types/ws"; import { WS_MSG_SET_PAGE, WS_MSG_SV_LOCAL } from "../../web/src/utils/types/ws";
import { eg } from "../ws/edit/edit-global"; import { eg } from "../ws/edit/edit-global";

View File

@ -1,6 +1,10 @@
import { site, user } from "dbgen"; import { site, user } from "dbgen";
import { ExecaChildProcess } from "execa"; import { ExecaChildProcess } from "execa";
declare global {
const Y: typeof Y;
}
export const glb = global as unknown as { export const glb = global as unknown as {
lastUpdate: Record<string, number>; lastUpdate: Record<string, number>;
prasiSrv: { prasiSrv: {

View File

@ -1,4 +1,3 @@
import * as Y from "yjs";
import { eg } from "../edit-global"; import { eg } from "../edit-global";
export const diffLocal = (ws: any, msg: any) => { export const diffLocal = (ws: any, msg: any) => {

View File

@ -1,7 +1,6 @@
import { ServerWebSocket } from "bun"; import { ServerWebSocket } from "bun";
import { validate } from "uuid"; import { validate } from "uuid";
import { syncronize } from "y-pojo"; import { syncronize } from "y-pojo";
import * as Y from "yjs";
import { WSData } from "../../../../../pkgs/core/server/create"; import { WSData } from "../../../../../pkgs/core/server/create";
import { import {
WS_MSG_GET_COMP, WS_MSG_GET_COMP,

View File

@ -1,7 +1,6 @@
import { ServerWebSocket } from "bun"; import { ServerWebSocket } from "bun";
import { validate } from "uuid"; import { validate } from "uuid";
import { syncronize } from "y-pojo"; import { syncronize } from "y-pojo";
import * as Y from "yjs";
import { WSData } from "../../../../../pkgs/core/server/create"; import { WSData } from "../../../../../pkgs/core/server/create";
import { MPage } from "../../../../web/src/utils/types/general"; import { MPage } from "../../../../web/src/utils/types/general";
import { import {
@ -10,8 +9,8 @@ import {
WS_MSG_SV_LOCAL, WS_MSG_SV_LOCAL,
} from "../../../../web/src/utils/types/ws"; } from "../../../../web/src/utils/types/ws";
import { eg } from "../edit-global"; import { eg } from "../edit-global";
import { loadPage } from "../tools/load-page";
import { sendWS } from "../send"; import { sendWS } from "../send";
import { loadPage } from "../tools/load-page";
export const getPage = async ( export const getPage = async (
ws: ServerWebSocket<WSData>, ws: ServerWebSocket<WSData>,

View File

@ -1,4 +1,3 @@
import * as Y from "yjs";
import { import {
WS_MSG_SVDIFF_REMOTE, WS_MSG_SVDIFF_REMOTE,
WS_MSG_SV_LOCAL, WS_MSG_SV_LOCAL,

View File

@ -1,4 +1,3 @@
import * as Y from "yjs";
import { import {
WS_MSG_DIFF_LOCAL, WS_MSG_DIFF_LOCAL,
WS_MSG_SVDIFF_REMOTE, WS_MSG_SVDIFF_REMOTE,

View File

@ -1,24 +1,21 @@
import { Websocket } from "hyper-express";
import { eg } from "../edit-global"; import { eg } from "../edit-global";
import { UndoManager } from "yjs";
import { WS_MSG_REDO, WS_MSG_UNDO } from "../../../web/src/utils/types/ws";
export const undo = (ws: Websocket, msg: WS_MSG_UNDO) => { export const undo = (ws: any, msg: any) => {
const um = getUndoManager(msg); const um = getUndoManager(msg);
if (um && um.canUndo()) { if (um && um.canUndo()) {
um.undo(); um.undo();
} }
}; };
export const redo = (ws: Websocket, msg: WS_MSG_REDO) => { export const redo = (ws: any, msg: any) => {
const um = getUndoManager(msg); const um = getUndoManager(msg);
if (um && um.canRedo()) { if (um && um.canRedo()) {
um.redo(); um.redo();
} }
}; };
const getUndoManager = (msg: WS_MSG_UNDO | WS_MSG_REDO) => { const getUndoManager = (msg: any | any) => {
let undoManager = null as null | UndoManager; let undoManager = null as null | Y.UndoManager;
if (msg.mode === "page") { if (msg.mode === "page") {
if (eg.edit.page[msg.id]) { if (eg.edit.page[msg.id]) {
undoManager = eg.edit.page[msg.id].undoManager; undoManager = eg.edit.page[msg.id].undoManager;

View File

@ -1,6 +1,5 @@
import { ServerWebSocket } from "bun"; import { ServerWebSocket } from "bun";
import { component } from "dbgen"; import { component } from "dbgen";
import { UndoManager } from "yjs";
import { TypedArray, TypedDoc, TypedMap } from "yjs-types"; import { TypedArray, TypedDoc, TypedMap } from "yjs-types";
import type { WSData } from "../../../../pkgs/core/server/create"; import type { WSData } from "../../../../pkgs/core/server/create";
@ -18,7 +17,7 @@ export type SingleComp = {
doc: TypedDoc<{ doc: TypedDoc<{
map: TypedMap<component & { content_tree: TypedMap<IItem> }>; map: TypedMap<component & { content_tree: TypedMap<IItem> }>;
}>; }>;
undoManager: UndoManager; undoManager: Y.UndoManager;
saveTimeout?: ReturnType<typeof setTimeout>; saveTimeout?: ReturnType<typeof setTimeout>;
ws: Set<ServerWebSocket<WSData>>; ws: Set<ServerWebSocket<WSData>>;
}; };
@ -49,7 +48,7 @@ export const eg = global as unknown as {
Site & { page: TypedArray<ArrayElement<Site["page"]>> } Site & { page: TypedArray<ArrayElement<Site["page"]>> }
>; >;
}>; }>;
undoManager: UndoManager; undoManager: Y.UndoManager;
saveTimeout?: ReturnType<typeof setTimeout>; saveTimeout?: ReturnType<typeof setTimeout>;
ws: Set<ServerWebSocket<WSData>>; ws: Set<ServerWebSocket<WSData>>;
} }
@ -60,7 +59,7 @@ export const eg = global as unknown as {
{ {
id: string; id: string;
doc: MPage; doc: MPage;
undoManager: UndoManager; undoManager: Y.UndoManager;
saveTimeout?: ReturnType<typeof setTimeout>; saveTimeout?: ReturnType<typeof setTimeout>;
ws: Set<ServerWebSocket<WSData>>; ws: Set<ServerWebSocket<WSData>>;
} }

View File

@ -1,6 +1,5 @@
import { page, site } from "dbgen"; import { page, site } from "dbgen";
import { validate as isValidUUID } from "uuid"; import { validate as isValidUUID } from "uuid";
import * as Y from "yjs";
export type SiteConfig = { export type SiteConfig = {
api_url?: string; api_url?: string;

View File

@ -11,7 +11,6 @@ import { redo, undo } from "./edit/action/undo-redo";
import { eg } from "./edit/edit-global"; import { eg } from "./edit/edit-global";
import { sendWS } from "./edit/send"; import { sendWS } from "./edit/send";
import { syncHandler } from "./sync/sync-handler"; import { syncHandler } from "./sync/sync-handler";
import * as Y from "yjs";
(globalThis as any).Y = Y; (globalThis as any).Y = Y;
eg.edit = { eg.edit = {

View File

@ -6,7 +6,7 @@ import { snapshot } from "../entity/snapshot";
import { user } from "../entity/user"; import { user } from "../entity/user";
import { gzipAsync } from "../entity/zlib"; import { gzipAsync } from "../entity/zlib";
import { sendWS } from "../sync-handler"; import { sendWS } from "../sync-handler";
import { Activity, SyncConnection, SyncType } from "../type"; import { SyncConnection, SyncType } from "../type";
export const page_load: SAction["page"]["load"] = async function ( export const page_load: SAction["page"]["load"] = async function (
this: SyncConnection, this: SyncConnection,

View File

@ -1,8 +1,6 @@
import * as Y from "yjs";
import { TypedDoc, TypedMap } from "yjs-types"; import { TypedDoc, TypedMap } from "yjs-types";
import { MItem } from "../../../../web/src/utils/types/item"; import { MItem } from "../../../../web/src/utils/types/item";
import { DPage } from "../../../../web/src/utils/types/root"; import { DPage } from "../../../../web/src/utils/types/root";
export * as Y from "yjs";
export const docs = { export const docs = {
site: {} as Record< site: {} as Record<

View File

@ -25,8 +25,7 @@ export const RoomList = class<T extends Record<string, any>> {
this.rooms.forEach((room) => { this.rooms.forEach((room) => {
room.clients.forEach((_, roomws) => { room.clients.forEach((_, roomws) => {
if (roomws === ws) { if (roomws === ws) {
room.clients.delete(ws); room.leave({ ws });
room.broadcastState("leave");
} }
}); });
}); });
@ -34,7 +33,7 @@ export const RoomList = class<T extends Record<string, any>> {
room(id: string) { room(id: string) {
let room = this.rooms.get(id); let room = this.rooms.get(id);
if (!room) { if (!room) {
this.rooms.set(id, new Room<T>(this.name)); this.rooms.set(id, new Room<T>(this.name, id));
room = this.rooms.get(id); room = this.rooms.get(id);
} }
@ -44,10 +43,12 @@ export const RoomList = class<T extends Record<string, any>> {
export class Room<T extends Record<string, any>> { export class Room<T extends Record<string, any>> {
name = ""; name = "";
id = "";
clients = new Map<ServerWebSocket<WSData>, Partial<T>>(); clients = new Map<ServerWebSocket<WSData>, Partial<T>>();
constructor(name: string) { constructor(name: string, id: string) {
this.name = name; this.name = name;
this.id = id;
} }
findAll(where: Partial<T>) { findAll(where: Partial<T>) {
@ -101,9 +102,18 @@ export class Room<T extends Record<string, any>> {
const ws = this.identify(client); const ws = this.identify(client);
if (ws) { if (ws) {
this.clients.set(ws, {}); this.clients.set(ws, {});
console.log("join", this.name, this.id, wconns.get(ws));
this.broadcastState("join", ws);
} }
}
this.broadcastState("join", ws); leave(client: { ws?: ServerWebSocket<WSData>; id?: string }) {
const ws = this.identify(client);
if (ws) {
this.clients.delete(ws);
console.log("leave", this.name, this.id, wconns.get(ws));
this.broadcastState("leave", ws);
}
} }
broadcastState = ( broadcastState = (
@ -130,17 +140,9 @@ export class Room<T extends Record<string, any>> {
this.clients.forEach((data, ws) => { this.clients.forEach((data, ws) => {
sendWS(ws, { sendWS(ws, {
type: SyncType.Event, type: SyncType.Event,
event: `${this.name}_${event_name}`, event: `${this.id}_${event_name}`,
data: { clients }, data: { clients },
}); });
}); });
}; };
leave(client: { ws?: ServerWebSocket<WSData>; id?: string }) {
const ws = this.identify(client);
if (ws) {
this.clients.delete(ws);
}
this.broadcastState("leave", ws);
}
} }

View File

@ -32,10 +32,9 @@ export const syncHandler: WebSocketHandler<WSData> = {
close(ws) { close(ws) {
const client_id = wconns.get(ws); const client_id = wconns.get(ws);
if (client_id) { if (client_id) {
activity.site.disconnect(ws);
conns.delete(client_id); conns.delete(client_id);
wconns.delete(ws); wconns.delete(ws);
activity.site.disconnect(ws);
} }
}, },
async message(ws, raw) { async message(ws, raw) {

10
app/srv/y.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
import type * as Y from "yjs";
export import Doc = Y.Doc;
export import UndoManager = Y.UndoManager;
export import applyUpdate = Y.applyUpdate;
export import encodeStateVector = Y.encodeStateVector;
export import encodeStateAsUpdate = Y.encodeStateAsUpdate;
export import Text = Y.Text;
export import Map = Y.Map;
export import Array = Y.Array;
export as namespace Y;

View File

@ -3,7 +3,7 @@
"module": "src/index.ts", "module": "src/index.ts",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bun run --silent --watch ./pkgs/core/index.ts dev", "dev": "bun run --hot --silent ./pkgs/core/index.ts dev",
"clean": "rm -rf data && rm -rf app/static && rm -rf app/web/.parcel-cache", "clean": "rm -rf data && rm -rf app/static && rm -rf app/web/.parcel-cache",
"build": "bun run --silent ./pkgs/core/build.ts", "build": "bun run --silent ./pkgs/core/build.ts",
"db-pull": "bun run ./pkgs/core/db-pull.ts", "db-pull": "bun run ./pkgs/core/db-pull.ts",

View File

@ -15,44 +15,57 @@ import { initSrv } from "../../app/srv/init";
g.status = "init"; g.status = "init";
await createLogger(); if (!g.Y) {
g.api = {}; g.Y = await import("yjs");
g.mode = process.argv.includes("dev") ? "dev" : "prod";
g.datadir = g.mode == "prod" ? "../data" : "data";
g.port = parseInt(process.env.PORT || "4550");
g.log.info(g.mode === "dev" ? "DEVELOPMENT" : "PRODUCTION"); await createLogger();
if (g.mode === "dev") { g.api = {};
await startDevWatcher(); g.mode = process.argv.includes("dev") ? "dev" : "prod";
g.datadir = g.mode == "prod" ? "../data" : "data";
g.port = parseInt(process.env.PORT || "4550");
g.log.info(g.mode === "dev" ? "DEVELOPMENT" : "PRODUCTION");
if (g.mode === "dev") {
await startDevWatcher();
}
/** init lmdb */
user.conf.init();
snapshot.init();
} }
/** init lmdb */ const db = g.db;
user.conf.init(); if (!db) {
snapshot.init(); await preparePrisma();
await ensureNotRunning();
await preparePrisma(); const db = g.db;
await ensureNotRunning(); if (db) {
db.$connect()
if (g.db) { .catch((e: any) => {
g.db g.log.error(`[DB ERROR]\n${e.message}`);
.$connect() })
.catch((e: any) => { .then(() => {
g.log.error(`[DB ERROR]\n${e.message}`); g.log.info("Database connected");
}) });
.then(() => { }
g.log.info("Database connected");
});
} }
await initSrv(); if (!g.apiPrepared) {
await syncActionDefinition(); await initSrv();
g.log.info("WS Action defined"); await syncActionDefinition();
await generateAPIFrm(); g.log.info("WS Action defined");
await prepareApiRoutes();
await prepareAPITypes(); await generateAPIFrm();
g.log.info("API Prepared"); await prepareApiRoutes();
await prepareAPITypes();
g.log.info("API Prepared");
g.apiPrepared = true;
}
if (!g.parcel) {
await parcelBuild();
}
await parcelBuild();
await createServer(); await createServer();
g.status = "ready"; g.status = "ready";

View File

@ -1,10 +1,10 @@
import { WebSocketHandler, gzipSync } from "bun"; import { WebSocketHandler } from "bun";
import { lookup } from "mime-types";
import { createRouter } from "radix3"; import { createRouter } from "radix3";
import { wsHandler } from "../../../app/srv/ws/handler"; import { wsHandler } from "../../../app/srv/ws/handler";
import { dir } from "../utils/dir"; import { dir } from "../utils/dir";
import { g } from "../utils/global"; import { g } from "../utils/global";
import { serveAPI } from "./serve-api"; import { serveAPI } from "./serve-api";
import { lookup } from "mime-types";
export const cache = { export const cache = {
static: {} as Record< static: {} as Record<
@ -64,7 +64,6 @@ export const createServer = async () => {
} as WebSocketHandler<WSData>, } as WebSocketHandler<WSData>,
async fetch(req, server) { async fetch(req, server) {
const url = new URL(req.url); const url = new URL(req.url);
const response = async () => { const response = async () => {
if (wsHandler[url.pathname]) { if (wsHandler[url.pathname]) {
if ( if (

View File

@ -2,6 +2,7 @@ import { Server, Subprocess } from "bun";
import { Logger } from "pino"; import { Logger } from "pino";
import { RadixRouter } from "radix3"; import { RadixRouter } from "radix3";
import { PrismaClient } from "../../../app/db/db"; import { PrismaClient } from "../../../app/db/db";
import type * as Y from "yjs";
type SingleRoute = { type SingleRoute = {
url: string; url: string;
@ -27,4 +28,6 @@ export const g = global as unknown as {
etag: string; etag: string;
}; };
parcel: Subprocess; parcel: Subprocess;
apiPrepared: boolean;
Y: typeof Y;
}; };

View File

@ -1,6 +1,6 @@
import { dir } from "dir"; import { dir } from "dir";
import { readAsync, writeAsync } from "fs-jetpack";
import { SyncActions } from "../../../app/srv/ws/sync/actions"; import { SyncActions } from "../../../app/srv/ws/sync/actions";
import { existsAsync, readAsync, writeAsync } from "fs-jetpack";
export const syncActionDefinition = async () => { export const syncActionDefinition = async () => {
const def: any = {}; const def: any = {};

View File

@ -1,6 +1,6 @@
import goober from "goober"; import goober from "goober";
import type { PrismaClient } from "../../../app/db/db"; import type { PrismaClient } from "../../../app/db/db";
import * as Yjs from "yjs";
declare global { declare global {
const navigate: (path: string) => void; const navigate: (path: string) => void;
const params: any; const params: any;
@ -10,6 +10,5 @@ declare global {
const db: PrismaClient; const db: PrismaClient;
const prasiContext: any; const prasiContext: any;
const serverurl: string; const serverurl: string;
const Y: typeof Yjs;
} }
export {}; export {};