From cf01c62c8801a0e80e08a0b3a15af3ed6935120f Mon Sep 17 00:00:00 2001 From: Rizky Date: Sat, 4 Nov 2023 02:50:29 +0700 Subject: [PATCH] wip fix yjs --- app/srv/api/page-reload.ts | 1 - app/srv/global.d.ts | 2 +- app/srv/global.ts | 4 ++ app/srv/ws/edit/action/diff-local.ts | 1 - app/srv/ws/edit/action/get-comp.ts | 1 - app/srv/ws/edit/action/get-page.ts | 3 +- app/srv/ws/edit/action/sv-local.ts | 1 - app/srv/ws/edit/action/svdiff-remote.ts | 1 - app/srv/ws/edit/action/undo-redo.ts | 11 ++-- app/srv/ws/edit/edit-global.ts | 7 +-- app/srv/ws/edit/tools/load-site.ts | 1 - app/srv/ws/handler.ts | 1 - app/srv/ws/sync/actions/page_load.ts | 4 +- app/srv/ws/sync/entity/docs.ts | 2 - app/srv/ws/sync/entity/room.ts | 32 +++++----- app/srv/ws/sync/sync-handler.ts | 3 +- app/srv/y.d.ts | 10 ++++ package.json | 2 +- pkgs/core/index.ts | 79 ++++++++++++++----------- pkgs/core/server/create.ts | 5 +- pkgs/core/utils/global.ts | 5 +- pkgs/core/utils/sync-def.ts | 2 +- pkgs/web-utils/src/global.ts | 3 +- 23 files changed, 98 insertions(+), 83 deletions(-) create mode 100644 app/srv/y.d.ts diff --git a/app/srv/api/page-reload.ts b/app/srv/api/page-reload.ts index 7a5015ad..3e01ca95 100644 --- a/app/srv/api/page-reload.ts +++ b/app/srv/api/page-reload.ts @@ -1,5 +1,4 @@ import { syncronize } from "y-pojo"; -import * as Y from "yjs"; import { MPage } from "../../web/src/utils/types/general"; import { WS_MSG_SET_PAGE, WS_MSG_SV_LOCAL } from "../../web/src/utils/types/ws"; import { eg } from "../ws/edit/edit-global"; diff --git a/app/srv/global.d.ts b/app/srv/global.d.ts index 016b448f..3d0cbee7 100644 --- a/app/srv/global.d.ts +++ b/app/srv/global.d.ts @@ -1 +1 @@ -declare module "@surfy/multipart-parser"; +declare module "@surfy/multipart-parser"; \ No newline at end of file diff --git a/app/srv/global.ts b/app/srv/global.ts index 229d4065..dd81cd45 100644 --- a/app/srv/global.ts +++ b/app/srv/global.ts @@ -1,6 +1,10 @@ import { site, user } from "dbgen"; import { ExecaChildProcess } from "execa"; +declare global { + const Y: typeof Y; +} + export const glb = global as unknown as { lastUpdate: Record; prasiSrv: { diff --git a/app/srv/ws/edit/action/diff-local.ts b/app/srv/ws/edit/action/diff-local.ts index 8f1ea95c..4c1dc551 100644 --- a/app/srv/ws/edit/action/diff-local.ts +++ b/app/srv/ws/edit/action/diff-local.ts @@ -1,4 +1,3 @@ -import * as Y from "yjs"; import { eg } from "../edit-global"; export const diffLocal = (ws: any, msg: any) => { diff --git a/app/srv/ws/edit/action/get-comp.ts b/app/srv/ws/edit/action/get-comp.ts index aad14fe8..33fbc37c 100644 --- a/app/srv/ws/edit/action/get-comp.ts +++ b/app/srv/ws/edit/action/get-comp.ts @@ -1,7 +1,6 @@ import { ServerWebSocket } from "bun"; import { validate } from "uuid"; import { syncronize } from "y-pojo"; -import * as Y from "yjs"; import { WSData } from "../../../../../pkgs/core/server/create"; import { WS_MSG_GET_COMP, diff --git a/app/srv/ws/edit/action/get-page.ts b/app/srv/ws/edit/action/get-page.ts index e2a76a42..e110fde9 100644 --- a/app/srv/ws/edit/action/get-page.ts +++ b/app/srv/ws/edit/action/get-page.ts @@ -1,7 +1,6 @@ import { ServerWebSocket } from "bun"; import { validate } from "uuid"; import { syncronize } from "y-pojo"; -import * as Y from "yjs"; import { WSData } from "../../../../../pkgs/core/server/create"; import { MPage } from "../../../../web/src/utils/types/general"; import { @@ -10,8 +9,8 @@ import { WS_MSG_SV_LOCAL, } from "../../../../web/src/utils/types/ws"; import { eg } from "../edit-global"; -import { loadPage } from "../tools/load-page"; import { sendWS } from "../send"; +import { loadPage } from "../tools/load-page"; export const getPage = async ( ws: ServerWebSocket, diff --git a/app/srv/ws/edit/action/sv-local.ts b/app/srv/ws/edit/action/sv-local.ts index 219cfac2..3a208a9a 100644 --- a/app/srv/ws/edit/action/sv-local.ts +++ b/app/srv/ws/edit/action/sv-local.ts @@ -1,4 +1,3 @@ -import * as Y from "yjs"; import { WS_MSG_SVDIFF_REMOTE, WS_MSG_SV_LOCAL, diff --git a/app/srv/ws/edit/action/svdiff-remote.ts b/app/srv/ws/edit/action/svdiff-remote.ts index 1f74ac2e..d9fe451d 100644 --- a/app/srv/ws/edit/action/svdiff-remote.ts +++ b/app/srv/ws/edit/action/svdiff-remote.ts @@ -1,4 +1,3 @@ -import * as Y from "yjs"; import { WS_MSG_DIFF_LOCAL, WS_MSG_SVDIFF_REMOTE, diff --git a/app/srv/ws/edit/action/undo-redo.ts b/app/srv/ws/edit/action/undo-redo.ts index 7e18283a..437cf3f6 100644 --- a/app/srv/ws/edit/action/undo-redo.ts +++ b/app/srv/ws/edit/action/undo-redo.ts @@ -1,24 +1,21 @@ -import { Websocket } from "hyper-express"; 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); if (um && um.canUndo()) { um.undo(); } }; -export const redo = (ws: Websocket, msg: WS_MSG_REDO) => { +export const redo = (ws: any, msg: any) => { const um = getUndoManager(msg); if (um && um.canRedo()) { um.redo(); } }; -const getUndoManager = (msg: WS_MSG_UNDO | WS_MSG_REDO) => { - let undoManager = null as null | UndoManager; +const getUndoManager = (msg: any | any) => { + let undoManager = null as null | Y.UndoManager; if (msg.mode === "page") { if (eg.edit.page[msg.id]) { undoManager = eg.edit.page[msg.id].undoManager; diff --git a/app/srv/ws/edit/edit-global.ts b/app/srv/ws/edit/edit-global.ts index 0d5c06ef..a1e2b00b 100644 --- a/app/srv/ws/edit/edit-global.ts +++ b/app/srv/ws/edit/edit-global.ts @@ -1,6 +1,5 @@ import { ServerWebSocket } from "bun"; import { component } from "dbgen"; -import { UndoManager } from "yjs"; import { TypedArray, TypedDoc, TypedMap } from "yjs-types"; import type { WSData } from "../../../../pkgs/core/server/create"; @@ -18,7 +17,7 @@ export type SingleComp = { doc: TypedDoc<{ map: TypedMap }>; }>; - undoManager: UndoManager; + undoManager: Y.UndoManager; saveTimeout?: ReturnType; ws: Set>; }; @@ -49,7 +48,7 @@ export const eg = global as unknown as { Site & { page: TypedArray> } >; }>; - undoManager: UndoManager; + undoManager: Y.UndoManager; saveTimeout?: ReturnType; ws: Set>; } @@ -60,7 +59,7 @@ export const eg = global as unknown as { { id: string; doc: MPage; - undoManager: UndoManager; + undoManager: Y.UndoManager; saveTimeout?: ReturnType; ws: Set>; } diff --git a/app/srv/ws/edit/tools/load-site.ts b/app/srv/ws/edit/tools/load-site.ts index bf2909c2..2cd5db05 100644 --- a/app/srv/ws/edit/tools/load-site.ts +++ b/app/srv/ws/edit/tools/load-site.ts @@ -1,6 +1,5 @@ import { page, site } from "dbgen"; import { validate as isValidUUID } from "uuid"; -import * as Y from "yjs"; export type SiteConfig = { api_url?: string; diff --git a/app/srv/ws/handler.ts b/app/srv/ws/handler.ts index c1b85c31..2fc8b6c8 100644 --- a/app/srv/ws/handler.ts +++ b/app/srv/ws/handler.ts @@ -11,7 +11,6 @@ import { redo, undo } from "./edit/action/undo-redo"; import { eg } from "./edit/edit-global"; import { sendWS } from "./edit/send"; import { syncHandler } from "./sync/sync-handler"; -import * as Y from "yjs"; (globalThis as any).Y = Y; eg.edit = { diff --git a/app/srv/ws/sync/actions/page_load.ts b/app/srv/ws/sync/actions/page_load.ts index 64a6d0df..7e033b72 100644 --- a/app/srv/ws/sync/actions/page_load.ts +++ b/app/srv/ws/sync/actions/page_load.ts @@ -6,14 +6,14 @@ import { snapshot } from "../entity/snapshot"; import { user } from "../entity/user"; import { gzipAsync } from "../entity/zlib"; 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 ( this: SyncConnection, id: string ) { let snap = snapshot.get("page", id); - let ydoc = docs.page[id]; + let ydoc = docs.page[id]; const conf = this.conf; if (!conf) return undefined; diff --git a/app/srv/ws/sync/entity/docs.ts b/app/srv/ws/sync/entity/docs.ts index e0e0533f..57e1b75a 100644 --- a/app/srv/ws/sync/entity/docs.ts +++ b/app/srv/ws/sync/entity/docs.ts @@ -1,8 +1,6 @@ -import * as Y from "yjs"; import { TypedDoc, TypedMap } from "yjs-types"; import { MItem } from "../../../../web/src/utils/types/item"; import { DPage } from "../../../../web/src/utils/types/root"; -export * as Y from "yjs"; export const docs = { site: {} as Record< diff --git a/app/srv/ws/sync/entity/room.ts b/app/srv/ws/sync/entity/room.ts index 12f90751..86a172a4 100644 --- a/app/srv/ws/sync/entity/room.ts +++ b/app/srv/ws/sync/entity/room.ts @@ -20,13 +20,12 @@ export const RoomList = class> { }); }); return rooms; - } + } disconnect(ws: ServerWebSocket) { this.rooms.forEach((room) => { room.clients.forEach((_, roomws) => { if (roomws === ws) { - room.clients.delete(ws); - room.broadcastState("leave"); + room.leave({ ws }); } }); }); @@ -34,7 +33,7 @@ export const RoomList = class> { room(id: string) { let room = this.rooms.get(id); if (!room) { - this.rooms.set(id, new Room(this.name)); + this.rooms.set(id, new Room(this.name, id)); room = this.rooms.get(id); } @@ -44,10 +43,12 @@ export const RoomList = class> { export class Room> { name = ""; + id = ""; clients = new Map, Partial>(); - constructor(name: string) { + constructor(name: string, id: string) { this.name = name; + this.id = id; } findAll(where: Partial) { @@ -101,9 +102,18 @@ export class Room> { const ws = this.identify(client); if (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; 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 = ( @@ -130,17 +140,9 @@ export class Room> { this.clients.forEach((data, ws) => { sendWS(ws, { type: SyncType.Event, - event: `${this.name}_${event_name}`, + event: `${this.id}_${event_name}`, data: { clients }, }); }); }; - - leave(client: { ws?: ServerWebSocket; id?: string }) { - const ws = this.identify(client); - if (ws) { - this.clients.delete(ws); - } - this.broadcastState("leave", ws); - } } diff --git a/app/srv/ws/sync/sync-handler.ts b/app/srv/ws/sync/sync-handler.ts index 8bee31b8..c45a898c 100644 --- a/app/srv/ws/sync/sync-handler.ts +++ b/app/srv/ws/sync/sync-handler.ts @@ -32,10 +32,9 @@ export const syncHandler: WebSocketHandler = { close(ws) { const client_id = wconns.get(ws); if (client_id) { + activity.site.disconnect(ws); conns.delete(client_id); wconns.delete(ws); - - activity.site.disconnect(ws); } }, async message(ws, raw) { diff --git a/app/srv/y.d.ts b/app/srv/y.d.ts new file mode 100644 index 00000000..543dfa21 --- /dev/null +++ b/app/srv/y.d.ts @@ -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; diff --git a/package.json b/package.json index cfe719ad..8ca2908f 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "module": "src/index.ts", "type": "module", "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", "build": "bun run --silent ./pkgs/core/build.ts", "db-pull": "bun run ./pkgs/core/db-pull.ts", diff --git a/pkgs/core/index.ts b/pkgs/core/index.ts index 652d548d..a860e097 100644 --- a/pkgs/core/index.ts +++ b/pkgs/core/index.ts @@ -15,44 +15,57 @@ import { initSrv } from "../../app/srv/init"; g.status = "init"; -await createLogger(); -g.api = {}; -g.mode = process.argv.includes("dev") ? "dev" : "prod"; -g.datadir = g.mode == "prod" ? "../data" : "data"; -g.port = parseInt(process.env.PORT || "4550"); +if (!g.Y) { + g.Y = await import("yjs"); -g.log.info(g.mode === "dev" ? "DEVELOPMENT" : "PRODUCTION"); -if (g.mode === "dev") { - await startDevWatcher(); + await createLogger(); + g.api = {}; + 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 */ -user.conf.init(); -snapshot.init(); - -await preparePrisma(); -await ensureNotRunning(); - -if (g.db) { - g.db - .$connect() - .catch((e: any) => { - g.log.error(`[DB ERROR]\n${e.message}`); - }) - .then(() => { - g.log.info("Database connected"); - }); +const db = g.db; +if (!db) { + await preparePrisma(); + await ensureNotRunning(); + const db = g.db; + if (db) { + db.$connect() + .catch((e: any) => { + g.log.error(`[DB ERROR]\n${e.message}`); + }) + .then(() => { + g.log.info("Database connected"); + }); + } } -await initSrv(); -await syncActionDefinition(); -g.log.info("WS Action defined"); -await generateAPIFrm(); -await prepareApiRoutes(); -await prepareAPITypes(); -g.log.info("API Prepared"); +if (!g.apiPrepared) { + await initSrv(); + await syncActionDefinition(); + g.log.info("WS Action defined"); -await parcelBuild(); + await generateAPIFrm(); + await prepareApiRoutes(); + await prepareAPITypes(); + g.log.info("API Prepared"); + g.apiPrepared = true; +} + +if (!g.parcel) { + await parcelBuild(); +} + await createServer(); - g.status = "ready"; + \ No newline at end of file diff --git a/pkgs/core/server/create.ts b/pkgs/core/server/create.ts index 39239040..02a69920 100644 --- a/pkgs/core/server/create.ts +++ b/pkgs/core/server/create.ts @@ -1,10 +1,10 @@ -import { WebSocketHandler, gzipSync } from "bun"; +import { WebSocketHandler } from "bun"; +import { lookup } from "mime-types"; import { createRouter } from "radix3"; import { wsHandler } from "../../../app/srv/ws/handler"; import { dir } from "../utils/dir"; import { g } from "../utils/global"; import { serveAPI } from "./serve-api"; -import { lookup } from "mime-types"; export const cache = { static: {} as Record< @@ -64,7 +64,6 @@ export const createServer = async () => { } as WebSocketHandler, async fetch(req, server) { const url = new URL(req.url); - const response = async () => { if (wsHandler[url.pathname]) { if ( diff --git a/pkgs/core/utils/global.ts b/pkgs/core/utils/global.ts index adacd997..e2e41fc8 100644 --- a/pkgs/core/utils/global.ts +++ b/pkgs/core/utils/global.ts @@ -2,8 +2,9 @@ import { Server, Subprocess } from "bun"; import { Logger } from "pino"; import { RadixRouter } from "radix3"; import { PrismaClient } from "../../../app/db/db"; +import type * as Y from "yjs"; -type SingleRoute = { +type SingleRoute = { url: string; args: string[]; fn: (...arg: any[]) => Promise; @@ -27,4 +28,6 @@ export const g = global as unknown as { etag: string; }; parcel: Subprocess; + apiPrepared: boolean; + Y: typeof Y; }; diff --git a/pkgs/core/utils/sync-def.ts b/pkgs/core/utils/sync-def.ts index 4ee9c2e3..ed7292f0 100644 --- a/pkgs/core/utils/sync-def.ts +++ b/pkgs/core/utils/sync-def.ts @@ -1,6 +1,6 @@ import { dir } from "dir"; +import { readAsync, writeAsync } from "fs-jetpack"; import { SyncActions } from "../../../app/srv/ws/sync/actions"; -import { existsAsync, readAsync, writeAsync } from "fs-jetpack"; export const syncActionDefinition = async () => { const def: any = {}; diff --git a/pkgs/web-utils/src/global.ts b/pkgs/web-utils/src/global.ts index ffb46b20..7a17483e 100644 --- a/pkgs/web-utils/src/global.ts +++ b/pkgs/web-utils/src/global.ts @@ -1,6 +1,6 @@ import goober from "goober"; import type { PrismaClient } from "../../../app/db/db"; -import * as Yjs from "yjs"; + declare global { const navigate: (path: string) => void; const params: any; @@ -10,6 +10,5 @@ declare global { const db: PrismaClient; const prasiContext: any; const serverurl: string; - const Y: typeof Yjs; } export {};