diff --git a/ui/package.json b/ui/package.json index b5a62a129..a927667a3 100644 --- a/ui/package.json +++ b/ui/package.json @@ -80,7 +80,6 @@ "react-resizable-panels": "2.1.6", "react-yjs": "2.0.1", "use-resize-observer": "9.1.0", - "y-websocket": "2.0.4", "yaml": "2.6.0", "yjs": "13.6.20" }, diff --git a/ui/src/lib/gql/__gen__/gql.ts b/ui/src/lib/gql/__gen__/gql.ts index 74cc216f0..80c436607 100644 --- a/ui/src/lib/gql/__gen__/gql.ts +++ b/ui/src/lib/gql/__gen__/gql.ts @@ -11,6 +11,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ * 3. It does not support dead code elimination, so it will add unused operations. * * Therefore it is highly recommended to use the babel or swc plugin for production. + * Learn more about it here: https://the-guild.dev/graphql/codegen/plugins/presets/preset-client#reducing-bundle-size */ const documents = { "mutation CreateDeployment($input: CreateDeploymentInput!) {\n createDeployment(input: $input) {\n deployment {\n ...Deployment\n }\n }\n}\n\nmutation UpdateDeployment($input: UpdateDeploymentInput!) {\n updateDeployment(input: $input) {\n deployment {\n ...Deployment\n }\n }\n}\n\nmutation DeleteDeployment($input: DeleteDeploymentInput!) {\n deleteDeployment(input: $input) {\n deploymentId\n }\n}\n\nmutation ExecuteDeployment($input: ExecuteDeploymentInput!) {\n executeDeployment(input: $input) {\n job {\n ...Job\n }\n }\n}\n\nquery GetDeployments($workspaceId: ID!, $pagination: Pagination) {\n deployments(workspaceId: $workspaceId, pagination: $pagination) {\n totalCount\n nodes {\n ...Deployment\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nquery GetJobs($workspaceId: ID!, $pagination: Pagination) {\n jobs(workspaceId: $workspaceId, pagination: $pagination) {\n totalCount\n nodes {\n ...Job\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n}\n\nquery GetJob($id: ID!) {\n job(id: $id) {\n ...Job\n }\n}": types.CreateDeploymentDocument, diff --git a/ui/src/lib/i18n/locales/en.json b/ui/src/lib/i18n/locales/en.json index 9546b79cd..6a0d5c006 100644 --- a/ui/src/lib/i18n/locales/en.json +++ b/ui/src/lib/i18n/locales/en.json @@ -225,6 +225,7 @@ "Member has been successfully added to the workspace.": "", "Member Removed": "", "Member has been successfully removed from the workspace.": "", + "Connection Error": "", "Empty workflow detected": "", "You cannot create a deployment without a workflow.": "", "Reload": "" diff --git a/ui/src/lib/i18n/locales/es.json b/ui/src/lib/i18n/locales/es.json index 74268c6dc..d5f54dfd8 100644 --- a/ui/src/lib/i18n/locales/es.json +++ b/ui/src/lib/i18n/locales/es.json @@ -225,6 +225,7 @@ "Member has been successfully added to the workspace.": "El miembro ha sido agregado exitosamente al espacio de trabajo.", "Member Removed": "Miembro eliminado", "Member has been successfully removed from the workspace.": "El miembro ha sido eliminado exitosamente del espacio de trabajo.", + "Connection Error": "", "Empty workflow detected": "Se detectó un flujo de trabajo vacío", "You cannot create a deployment without a workflow.": "No puedes crear un despliegue sin un flujo de trabajo.", "Reload": "Recargar" diff --git a/ui/src/lib/i18n/locales/fr.json b/ui/src/lib/i18n/locales/fr.json index c0cc73a14..091c39a14 100644 --- a/ui/src/lib/i18n/locales/fr.json +++ b/ui/src/lib/i18n/locales/fr.json @@ -225,6 +225,7 @@ "Member has been successfully added to the workspace.": "Le membre a été ajouté avec succès à l'espace de travail.", "Member Removed": "Membre supprimé", "Member has been successfully removed from the workspace.": "Le membre a été supprimé avec succès de l'espace de travail.", + "Connection Error": "", "Empty workflow detected": "Flux de travail vide détecté", "You cannot create a deployment without a workflow.": "Vous ne pouvez pas créer de déploiement sans flux de travail.", "Reload": "Recharger" diff --git a/ui/src/lib/i18n/locales/ja.json b/ui/src/lib/i18n/locales/ja.json index f30f1443e..fb3c63946 100644 --- a/ui/src/lib/i18n/locales/ja.json +++ b/ui/src/lib/i18n/locales/ja.json @@ -225,6 +225,7 @@ "Member has been successfully added to the workspace.": "メンバーがワークスペースに正常に追加されました。", "Member Removed": "メンバー削除", "Member has been successfully removed from the workspace.": "メンバーがワークスペースから正常に削除されました。", + "Connection Error": "", "Empty workflow detected": "空のワークフローが検出されました", "You cannot create a deployment without a workflow.": "ワークフローがないとデプロイメントを作成できません。", "Reload": "リロード" diff --git a/ui/src/lib/i18n/locales/zh.json b/ui/src/lib/i18n/locales/zh.json index cdab61366..2a86455f8 100644 --- a/ui/src/lib/i18n/locales/zh.json +++ b/ui/src/lib/i18n/locales/zh.json @@ -225,6 +225,7 @@ "Member has been successfully added to the workspace.": "成员已成功添加到工作区。", "Member Removed": "成员已移除", "Member has been successfully removed from the workspace.": "成员已成功从工作区中移除。", + "Connection Error": "", "Empty workflow detected": "检测到空工作流", "You cannot create a deployment without a workflow.": "没有工作流,您无法创建部署。", "Reload": "重新加载" diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts index cffaf97a5..2885a4968 100644 --- a/ui/src/lib/utils.ts +++ b/ui/src/lib/utils.ts @@ -5,3 +5,7 @@ import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/ui/src/lib/yjs/socketYjsManager.ts b/ui/src/lib/yjs/socketYjsManager.ts new file mode 100644 index 000000000..b66ba3b48 --- /dev/null +++ b/ui/src/lib/yjs/socketYjsManager.ts @@ -0,0 +1,375 @@ +import * as Y from "yjs"; + +import { sleep } from "../utils"; + +import { MessageType, type FlowMessage } from "./types"; + +export type AccessTokenProvider = () => Promise | string; + +export class SocketYjsManager { + protected ws!: WebSocket; + protected doc: Y.Doc; + protected socketReady = false; + protected firstSyncComplete = false; + protected accessTokenProvider: AccessTokenProvider | undefined; + protected projectId: string | undefined; + protected onUpdateHandlers: ((update: Uint8Array) => void)[] = []; + protected reconnectAttempts = 0; + protected maxReconnectAttempts = 5; + protected reconnectDelay = 1000; + protected reconnectTimer: ReturnType | null = null; + + constructor(doc: Y.Doc) { + this.doc = doc; + + // Bind methods + this.onConnectionEstablished = this.onConnectionEstablished.bind(this); + this.onConnectionDisconnect = this.onConnectionDisconnect.bind(this); + this.onConnectionError = this.onConnectionError.bind(this); + this.onAuthenticateRequest = this.onAuthenticateRequest.bind(this); + this.onDocUpdate = this.onDocUpdate.bind(this); + this.onReady = this.onReady.bind(this); + this.onPeerUpdate = this.onPeerUpdate.bind(this); + this.handleMessage = this.handleMessage.bind(this); + this.reconnect = this.reconnect.bind(this); + } + + public getDoc(): Y.Doc { + return this.doc; + } + + // Replace the setupSocket method + async setupSocket(data: { + url: string; + roomId: string; + projectId: string; + accessTokenProvider: AccessTokenProvider; + }) { + this.accessTokenProvider = data.accessTokenProvider; + this.projectId = data.projectId; + + try { + const token = await this.accessTokenProvider(); + const wsUrl = new URL(data.url); + wsUrl.protocol = wsUrl.protocol.replace("http", "ws"); + wsUrl.pathname = `/${data.roomId}`; + + // Add query parameters for authentication + wsUrl.searchParams.set("user_id", this.doc.clientID.toString()); + wsUrl.searchParams.set("project_id", data.projectId); + wsUrl.searchParams.set("token", token); // Pass token as query param since we can't set headers + + this.ws = new WebSocket(wsUrl.href); + this.ws.binaryType = "arraybuffer"; + + this.setupWebSocketListeners(); + this.setupDocListeners(); + + console.log("Attempting WebSocket connection to:", wsUrl.origin + wsUrl.pathname); + } catch (error) { + console.error("Failed to setup WebSocket:", error); + throw error; + } + } + + // Update the reconnect method + private reconnect() { + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + } + + if (this.reconnectAttempts < this.maxReconnectAttempts) { + this.reconnectAttempts++; + const delay = + this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1); + + this.reconnectTimer = setTimeout(async () => { + try { + if (this.ws) { + const originalUrl = new URL(this.ws.url); + const baseUrl = `${originalUrl.protocol}//${originalUrl.host}`; + const roomId = originalUrl.pathname.slice(1); + await this.setupSocket({ + url: baseUrl, + roomId, + projectId: this.projectId || "", + accessTokenProvider: this.accessTokenProvider || (() => ""), + }); + } + } catch (error) { + console.error("Reconnection failed:", error); + } + }, delay); + } + } + + private setupWebSocketListeners() { + this.ws.addEventListener("open", this.onConnectionEstablished); + this.ws.addEventListener("close", this.onConnectionDisconnect); + this.ws.addEventListener("error", this.onConnectionError); + this.ws.addEventListener("message", this.handleMessage); + } + + private setupDocListeners() { + this.doc.on("updateV2", this.onDocUpdate); + } + + protected onConnectionEstablished() { + this.reconnectAttempts = 0; + this.socketReady = true; + this.initializeRoom().catch(console.error); + } + + protected onConnectionDisconnect() { + this.socketReady = false; + this.firstSyncComplete = false; + this.reconnect(); + } + + protected onConnectionError(error: Event) { + console.error("WebSocket error:", error); + this.reconnect(); + } + + protected async onAuthenticateRequest() { + const token = await this.accessTokenProvider?.(); + if (token && this.ws.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify({ type: "authenticate", token })); + } + } + + protected async handleMessage(event: MessageEvent) { + try { + if (event.data instanceof ArrayBuffer) { + // Handle binary message (Yjs update) + const update = new Uint8Array(event.data); + await this.onPeerUpdate({ update }); + } else if (typeof event.data === "string") { + // Handle text message + const data = JSON.parse(event.data); + if (data.type === "authenticate") { + await this.onAuthenticateRequest(); + } else if (data.type === "ready") { + this.onReady(); + } + } + } catch (error) { + console.error("Error handling message:", error); + } + } + + protected async initializeRoom() { + try { + await this.sendFlowMessage({ + event: { + tag: "Create", + content: { room_id: this.doc.clientID.toString() }, + }, + }); + + await this.sendFlowMessage({ + event: { + tag: "Join", + content: { room_id: this.doc.clientID.toString() }, + }, + }); + + await this.sendFlowMessage({ + event: { + tag: "Emit", + content: { data: "" }, + }, + session_command: { + tag: "Start", + content: { + project_id: this.projectId, + user: { + id: this.doc.clientID.toString(), + name: "", + email: "", + tenant_id: "123" + } + }, + }, + }); + + await this.sendFlowMessage({ + event: { + tag: "Emit", + content: { data: "" }, + }, + session_command: { + tag: "AddTask", + content: { + project_id: this.projectId, + }, + }, + }); + + await this.syncData(); + } catch (error) { + console.error("Failed to initialize room:", error); + } + } + + async isReady(): Promise { + if (this.socketReady) return true; + await sleep(100); + return await this.isReady(); + } + + protected onReady() { + this.socketReady = true; + } + + protected onPeerUpdate(data: { update: ArrayBuffer | Uint8Array }) { + const update = data.update instanceof ArrayBuffer + ? new Uint8Array(data.update) + : data.update; + + console.log("Received peer update:", { + updateLength: update.length, + rawUpdate: Array.from(update) + }); + + const currentState = Y.encodeStateAsUpdateV2(this.doc); + console.log("Current state before diff:", { + stateLength: currentState.length, + rawState: Array.from(currentState) + }); + + const diffUpdate = Y.diffUpdateV2(update, currentState); + console.log("Diff update:", { + diffLength: diffUpdate.length, + rawDiff: Array.from(diffUpdate) + }); + + Y.applyUpdateV2(this.doc, diffUpdate, 'peer'); + this.onUpdateHandlers.forEach((handler) => handler(update)); + } + + async syncData() { + await this.isReady(); + + const stateVector = Y.encodeStateVector(this.doc); + console.log("State vector:", { + vectorLength: stateVector.length, + rawVector: Array.from(stateVector) + }); + + if (this.ws.readyState === WebSocket.OPEN) { + const syncMessage = createBinaryMessage(MessageType.SYNC, stateVector); + console.log("Sending sync message:", { + messageLength: syncMessage.length, + messageType: syncMessage[0], + rawMessage: Array.from(syncMessage) + }); + this.ws.send(syncMessage); + } + + if (!this.firstSyncComplete) { + this.firstSyncComplete = true; + queueMicrotask(() => this.syncData()); + } + } + + private async sendFlowMessage(message: FlowMessage): Promise { + return new Promise((resolve, reject) => { + if (this.ws.readyState !== WebSocket.OPEN) { + reject(new Error("WebSocket is not connected")); + return; + } + + try { + const messageStr = JSON.stringify(message); + console.log("Sending message:", { + type: message.event.tag, + sessionCommand: message.session_command?.tag, + content: message, + rawMessage: messageStr + }); + this.ws.send(messageStr); + resolve(); + } catch (error) { + console.error("Failed to send message:", error); + reject(error); + } + }); + } + + protected onDocUpdate(update: Uint8Array, origin: unknown) { + if (origin === this.doc.clientID && this.ws.readyState === WebSocket.OPEN) { + console.log("Received doc update from self doc:", this.doc); + console.log("Received doc update:", { + updateLength: update.length, + rawUpdate: Array.from(update) + }); + + const updateMessage = createBinaryMessage(MessageType.UPDATE, update); + console.log("Sending update message:", { + messageLength: updateMessage.length, + messageType: updateMessage[0] + }); + console.log("Raw update message:", { + rawMessage: Array.from(updateMessage) + }); + + this.ws.send(updateMessage); + } + } + + onUpdate(handler: (update: Uint8Array) => void) { + this.onUpdateHandlers.push(handler); + } + + protected async destroy() { + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer); + } + + if (this.ws && this.ws.readyState === WebSocket.OPEN) { + console.log("Starting cleanup process..."); + try { + await this.sendFlowMessage({ + event: { + tag: "Emit", + content: { + data: "", + user: { + id: this.doc.clientID.toString(), + name: "", + email: "" + } + }, + }, + session_command: { + tag: "End", + content: { + project_id: this.projectId, + user: { + id: this.doc.clientID.toString(), + name: "", + email: "" + } + }, + }, + }); + console.log("Cleanup message sent successfully"); + } catch (error) { + console.error("Failed to send cleanup message:", error); + } finally { + this.ws.close(); + console.log("WebSocket connection closed"); + } + } + this.doc.destroy(); + console.log("Document destroyed"); + } +} + +function createBinaryMessage(type: MessageType, data: Uint8Array): Uint8Array { + const message = new Uint8Array(data.length + 1); + message[0] = type; + message.set(data, 1); + return message; +} diff --git a/ui/src/lib/yjs/types.ts b/ui/src/lib/yjs/types.ts index 710889a6c..1cc57afc9 100644 --- a/ui/src/lib/yjs/types.ts +++ b/ui/src/lib/yjs/types.ts @@ -1,3 +1,5 @@ +import { User } from "@flow/types/user"; + export type YJsonPrimitive = string | number | boolean | null | Uint8Array; export type YJsonValue = @@ -6,3 +8,37 @@ export type YJsonValue = | { [key: string]: YJsonValue; }; + +export type FlowMessage = { + event: { + tag: "Create" | "Join" | "Leave" | "Emit"; + content: { + room_id?: string; + data?: string; + user?: User; + }; + }; + session_command?: SessionCommand; +}; + +export type SessionCommand = { + tag: + | "Start" + | "End" + | "Complete" + | "CheckStatus" + | "AddTask" + | "RemoveTask" + | "ListAllSnapshotsVersions" + | "MergeUpdates"; + content: { + project_id?: string; + user?: User; + data?: Uint8Array; + }; +}; + +export enum MessageType { + UPDATE = 1, + SYNC = 2, +} diff --git a/ui/src/lib/yjs/useYjsStore.ts b/ui/src/lib/yjs/useYjsStore.ts index 81f61acdc..afeb8a6c8 100644 --- a/ui/src/lib/yjs/useYjsStore.ts +++ b/ui/src/lib/yjs/useYjsStore.ts @@ -1,6 +1,5 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { useY } from "react-yjs"; -import { WebsocketProvider } from "y-websocket"; import * as Y from "yjs"; import { config } from "@flow/config"; @@ -13,6 +12,7 @@ import { createWorkflowsYaml } from "@flow/utils/engineWorkflowYaml/workflowYaml import { useDeployment } from "../gql/deployment"; import { useT } from "../i18n"; +import { SocketYjsManager } from "./socketYjsManager"; import useWorkflowTabs from "./useWorkflowTabs"; import useYEdge from "./useYEdge"; import useYNode from "./useYNode"; @@ -28,67 +28,77 @@ export default ({ }) => { const { toast } = useToast(); const t = useT(); - const [currentProject] = useCurrentProject(); const { createDeployment, useUpdateDeployment } = useDeployment(); - - const yWebSocketRef = useRef(null); - useEffect(() => () => yWebSocketRef.current?.destroy(), []); - + const managerRef = useRef(null); const [undoManager, setUndoManager] = useState(null); - const [{ yWorkflows, currentUserClientId, undoTrackerActionWrapper }] = - useState(() => { - const yDoc = new Y.Doc(); - const { websocket, websocketToken } = config(); - if (workflowId && websocket && websocketToken) { - yWebSocketRef.current = new WebsocketProvider( - websocket, - workflowId, - yDoc, - { params: { token: websocketToken } }, - ); - } + // Initialize store and connect + const [{ yWorkflows, currentUserClientId }] = useState(() => { + const doc = new Y.Doc(); + const yWorkflows = doc.getArray("workflows"); - const yWorkflows = yDoc.getArray("workflows"); + // Initialize with default workflow if empty + if (yWorkflows.length === 0) { const yWorkflow = yWorkflowBuilder("main", "Main Workflow"); yWorkflows.push([yWorkflow]); + } - const currentUserClientId = yDoc.clientID; - - // NOTE: any changes to the yDoc should be wrapped in a transact - const undoTrackerActionWrapper = (callback: () => void) => - yDoc.transact(callback, currentUserClientId); - - return { yWorkflows, currentUserClientId, undoTrackerActionWrapper }; - }); + return { + yWorkflows, + currentUserClientId: doc.clientID, + }; + }); useEffect(() => { - if (yWorkflows) { - const manager = new Y.UndoManager(yWorkflows, { - trackedOrigins: new Set([currentUserClientId]), // Only track local changes - captureTimeout: 200, // default is 500. 200ms is a good balance between performance and user experience + const { websocket: websocketUrl, websocketToken } = config(); + if (!websocketUrl || !websocketToken || !workflowId || !currentProject?.id) + return; + + const doc = managerRef.current?.getDoc() || yWorkflows.doc; + + // Create and setup socket manager + if (!doc) return; + const manager = new SocketYjsManager(doc); + manager + .setupSocket({ + url: websocketUrl, + roomId: workflowId, + projectId: currentProject.id, + accessTokenProvider: async () => websocketToken, + }) + .catch((error) => { + toast({ + title: t("Connection Error"), + description: error.message, + variant: "destructive", + }); }); - setUndoManager(manager); - - return () => { - manager.destroy(); // Clean up UndoManager on component unmount - }; - } - }, [yWorkflows, currentUserClientId]); - - const handleWorkflowUndo = useCallback(() => { - if (undoManager?.undoStack && undoManager.undoStack.length > 0) { - undoManager?.undo(); - } - }, [undoManager]); - - const handleWorkflowRedo = useCallback(() => { - if (undoManager?.redoStack && undoManager.redoStack.length > 0) { - undoManager?.redo(); - } - }, [undoManager]); + // Setup undo manager + const undoMngr = new Y.UndoManager(yWorkflows, { + trackedOrigins: new Set([currentUserClientId]), + captureTimeout: 200, + }); + setUndoManager(undoMngr); + + managerRef.current = manager; + + return () => { + manager.destroy(); + undoMngr.destroy(); + managerRef.current = null; + }; + }, [ + workflowId, + currentProject?.id, + toast, + t, + currentUserClientId, + yWorkflows, + ]); + + // Get the raw workflows using useY const rawWorkflows = useY(yWorkflows); const { @@ -99,7 +109,21 @@ export default ({ setOpenWorkflowIds, handleWorkflowOpen, handleWorkflowClose, - } = useWorkflowTabs({ workflowId, rawWorkflows, handleWorkflowIdChange }); + } = useWorkflowTabs({ + workflowId, + rawWorkflows: rawWorkflows as Record[], + handleWorkflowIdChange, + }); + + // Create wrapper for undoTrackerActionWrapper + const undoTrackerActionWrapper = useCallback( + (callback: () => void) => { + const doc = managerRef.current?.getDoc(); + if (!doc) return; + doc.transact(callback, currentUserClientId); + }, + [currentUserClientId], + ); const { currentYWorkflow, handleWorkflowAdd, handleWorkflowsRemove } = useYWorkflow({ @@ -120,6 +144,29 @@ export default ({ currentYWorkflow?.get("edges") ?? new Y.Array(), ) as Edge[]; + const { handleNodesUpdate } = useYNode({ + currentYWorkflow, + undoTrackerActionWrapper, + handleWorkflowsRemove, + }); + + const { handleEdgesUpdate } = useYEdge({ + currentYWorkflow, + undoTrackerActionWrapper, + }); + + const handleWorkflowUndo = useCallback(() => { + if (undoManager?.undoStack.length) { + undoManager.undo(); + } + }, [undoManager]); + + const handleWorkflowRedo = useCallback(() => { + if (undoManager?.redoStack.length) { + undoManager.redo(); + } + }, [undoManager]); + const handleWorkflowDeployment = useCallback( async (deploymentId?: string, description?: string) => { const { @@ -135,12 +182,13 @@ export default ({ projectName, rawWorkflows .map((w): Workflow | undefined => { - if (!w || w.nodes.length < 1) return undefined; - const id = w.id as string; - const name = w.name as string; - const n = w.nodes as Node[]; - const e = w.edges as Edge[]; - return { id, name, nodes: n, edges: e }; + if (!w || (w.nodes as Node[]).length < 1) return undefined; + return { + id: w.id as string, + name: w.name as string, + nodes: w.nodes as Node[], + edges: w.edges as Edge[], + }; }) .filter(isDefined), ) ?? {}; @@ -180,17 +228,6 @@ export default ({ ], ); - const { handleNodesUpdate } = useYNode({ - currentYWorkflow, - undoTrackerActionWrapper, - handleWorkflowsRemove, - }); - - const { handleEdgesUpdate } = useYEdge({ - currentYWorkflow, - undoTrackerActionWrapper, - }); - return { nodes, edges, diff --git a/ui/src/types/user.ts b/ui/src/types/user.ts index a457e3864..f4d20fa99 100644 --- a/ui/src/types/user.ts +++ b/ui/src/types/user.ts @@ -15,6 +15,7 @@ export type User = { id: string; name: string; email: string; + tenant_id?: string; }; export type SearchUser = { diff --git a/ui/yarn.lock b/ui/yarn.lock index 557f395a8..9ea52fd21 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -3706,7 +3706,6 @@ __metadata: use-resize-observer: "npm:9.1.0" vite: "npm:5.4.10" vitest: "npm:2.1.4" - y-websocket: "npm:2.0.4" yaml: "npm:2.6.0" yjs: "npm:13.6.20" languageName: unknown @@ -5424,32 +5423,6 @@ __metadata: languageName: node linkType: hard -"abstract-leveldown@npm:^6.2.1": - version: 6.3.0 - resolution: "abstract-leveldown@npm:6.3.0" - dependencies: - buffer: "npm:^5.5.0" - immediate: "npm:^3.2.3" - level-concat-iterator: "npm:~2.0.0" - level-supports: "npm:~1.0.0" - xtend: "npm:~4.0.0" - checksum: 10c0/441c7e6765b6c2e9a36999e2bda3f4421d09348c0e925e284d873bcbf5ecad809788c9eda416aed37fe5b6e6a9e75af6e27142d1fcba460b8757d70028e307b1 - languageName: node - linkType: hard - -"abstract-leveldown@npm:~6.2.1, abstract-leveldown@npm:~6.2.3": - version: 6.2.3 - resolution: "abstract-leveldown@npm:6.2.3" - dependencies: - buffer: "npm:^5.5.0" - immediate: "npm:^3.2.3" - level-concat-iterator: "npm:~2.0.0" - level-supports: "npm:~1.0.0" - xtend: "npm:~4.0.0" - checksum: 10c0/a7994531a4618a409ee016dabf132014be9a2d07a3438f835c1eb5607f77f6cf12abc437e8f5bff353b1d8dcb31628c8ae65b41e7533bf606c6f7213ab61c1d1 - languageName: node - linkType: hard - "acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -5824,13 +5797,6 @@ __metadata: languageName: node linkType: hard -"async-limiter@npm:~1.0.0": - version: 1.0.1 - resolution: "async-limiter@npm:1.0.1" - checksum: 10c0/0693d378cfe86842a70d4c849595a0bb50dc44c11649640ca982fa90cbfc74e3cc4753b5a0847e51933f2e9c65ce8e05576e75e5e1fd963a086e673735b35969 - languageName: node - linkType: hard - "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -6092,7 +6058,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.5.0, buffer@npm:^5.6.0": +"buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -6993,16 +6959,6 @@ __metadata: languageName: node linkType: hard -"deferred-leveldown@npm:~5.3.0": - version: 5.3.0 - resolution: "deferred-leveldown@npm:5.3.0" - dependencies: - abstract-leveldown: "npm:~6.2.1" - inherits: "npm:^2.0.3" - checksum: 10c0/b1021314bfd5875b10e4c8c69429a69d37affc79df53aedf3c18a4bcd7460619220fa6b1bc309bcd85851c2c9c2b4da6cb03127abc08b715ff56da8aeae6b74f - languageName: node - linkType: hard - "define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.4": version: 1.1.4 resolution: "define-data-property@npm:1.1.4" @@ -7290,18 +7246,6 @@ __metadata: languageName: node linkType: hard -"encoding-down@npm:^6.3.0": - version: 6.3.0 - resolution: "encoding-down@npm:6.3.0" - dependencies: - abstract-leveldown: "npm:^6.2.1" - inherits: "npm:^2.0.3" - level-codec: "npm:^9.0.0" - level-errors: "npm:^2.0.0" - checksum: 10c0/f7e92149863863c11e04d71ceb71baa1772270dc9ef15cbdbb155fed0a7d31c823682e043af3100f96ce8ab2e0a70a2464c1fa4902d4dce9a0584498f40d07bf - languageName: node - linkType: hard - "encoding-sniffer@npm:^0.2.0": version: 0.2.0 resolution: "encoding-sniffer@npm:0.2.0" @@ -7356,17 +7300,6 @@ __metadata: languageName: node linkType: hard -"errno@npm:~0.1.1": - version: 0.1.8 - resolution: "errno@npm:0.1.8" - dependencies: - prr: "npm:~1.0.1" - bin: - errno: cli.js - checksum: 10c0/83758951967ec57bf00b5f5b7dc797e6d65a6171e57ea57adcf1bd1a0b477fd9b5b35fae5be1ff18f4090ed156bce1db749fe7e317aac19d485a5d150f6a4936 - languageName: node - linkType: hard - "error-ex@npm:^1.3.1": version: 1.3.2 resolution: "error-ex@npm:1.3.2" @@ -9161,13 +9094,6 @@ __metadata: languageName: node linkType: hard -"immediate@npm:^3.2.3": - version: 3.3.0 - resolution: "immediate@npm:3.3.0" - checksum: 10c0/40eab095d5944ad79af054700beee97000271fde8743720932d8eb41ccbf2cb8c855ff95b128cf9a7fec523a4f11ee2e392b9f2fa6456b055b1160f1b4ad3e3b - languageName: node - linkType: hard - "immutable@npm:~3.7.6": version: 3.7.6 resolution: "immutable@npm:3.7.6" @@ -10094,109 +10020,6 @@ __metadata: languageName: node linkType: hard -"level-codec@npm:^9.0.0": - version: 9.0.2 - resolution: "level-codec@npm:9.0.2" - dependencies: - buffer: "npm:^5.6.0" - checksum: 10c0/38a7eb8beed37969ad93160251d5be8e667d4ea0ee199d5e72e61739e552987a71acaa2daa1d2dbc7541f0cfb64e2bd8b50c3d8757cfa41468d8631aa45cc0eb - languageName: node - linkType: hard - -"level-concat-iterator@npm:~2.0.0": - version: 2.0.1 - resolution: "level-concat-iterator@npm:2.0.1" - checksum: 10c0/b0a55ec63137b159fdb69204fbac02f33fbfbaa61563db21121300f6da6a35dd4654dc872df6ca1067c0ca4f98074ccbb59c28044d0043600a940a506c3d4a71 - languageName: node - linkType: hard - -"level-errors@npm:^2.0.0, level-errors@npm:~2.0.0": - version: 2.0.1 - resolution: "level-errors@npm:2.0.1" - dependencies: - errno: "npm:~0.1.1" - checksum: 10c0/9e664afb98febe22e6ccde063be85e2b6e472414325bea87f0b2288bec589ef97658028f8654dc4716a06cda15c205e910b6cf5eb3906ed3ca06ea84d370002f - languageName: node - linkType: hard - -"level-iterator-stream@npm:~4.0.0": - version: 4.0.2 - resolution: "level-iterator-stream@npm:4.0.2" - dependencies: - inherits: "npm:^2.0.4" - readable-stream: "npm:^3.4.0" - xtend: "npm:^4.0.2" - checksum: 10c0/29994d5449428c246dc7d983220ca333ddaaa9e0fe9a664bb23750693db6cff4be62e8e31b6e8a0e1057c09a94580b965206c048701f96c3e8e97720a3c1e7c8 - languageName: node - linkType: hard - -"level-js@npm:^5.0.0": - version: 5.0.2 - resolution: "level-js@npm:5.0.2" - dependencies: - abstract-leveldown: "npm:~6.2.3" - buffer: "npm:^5.5.0" - inherits: "npm:^2.0.3" - ltgt: "npm:^2.1.2" - checksum: 10c0/2f5ccc96541ee9191bb9f8fd970918ade426d3dbb68e0414267a3c204d04952ef72651be241d40579dcb178948c14be5c36758da99df76a04a31d6227949f8a6 - languageName: node - linkType: hard - -"level-packager@npm:^5.1.0": - version: 5.1.1 - resolution: "level-packager@npm:5.1.1" - dependencies: - encoding-down: "npm:^6.3.0" - levelup: "npm:^4.3.2" - checksum: 10c0/dc3ad1d3bc1fc85154ab85ac8220ffc7ff4da7b2be725c53ea8716780a2ef37d392c7dffae769a0419341f19febe78d86da998981b4ba3be673db1cb95051e95 - languageName: node - linkType: hard - -"level-supports@npm:~1.0.0": - version: 1.0.1 - resolution: "level-supports@npm:1.0.1" - dependencies: - xtend: "npm:^4.0.2" - checksum: 10c0/6e8eb2be4c2c55e04e71dff831421a81d95df571e0004b6e6e0cee8421e792e22b3ab94ecaa925dc626164f442185b2e2bfb5d52b257d1639f8f9da8714c8335 - languageName: node - linkType: hard - -"level@npm:^6.0.1": - version: 6.0.1 - resolution: "level@npm:6.0.1" - dependencies: - level-js: "npm:^5.0.0" - level-packager: "npm:^5.1.0" - leveldown: "npm:^5.4.0" - checksum: 10c0/275068a7c58e6fd3eb53be2eeaf90d6ba6d566904591b68fe10022affc16b28575a2ccf580f384d38ea64c116b2c8046e517a34a0c3b5da12c635a4732eccf6d - languageName: node - linkType: hard - -"leveldown@npm:^5.4.0": - version: 5.6.0 - resolution: "leveldown@npm:5.6.0" - dependencies: - abstract-leveldown: "npm:~6.2.1" - napi-macros: "npm:~2.0.0" - node-gyp: "npm:latest" - node-gyp-build: "npm:~4.1.0" - checksum: 10c0/d5c4569aca5a856da4012ecaf507a7ba3e28ef18bde000a1826bf7a485357ddd18ea9ea335e94b9f12274d19b9b7d28d3268ac330f2e96d0067115edf8dc3396 - languageName: node - linkType: hard - -"levelup@npm:^4.3.2": - version: 4.4.0 - resolution: "levelup@npm:4.4.0" - dependencies: - deferred-leveldown: "npm:~5.3.0" - level-errors: "npm:~2.0.0" - level-iterator-stream: "npm:~4.0.0" - level-supports: "npm:~1.0.0" - xtend: "npm:~4.0.0" - checksum: 10c0/e67eeb72cf10face92f73527b63ea0754bc3ab7ced76f8bf909fb51db1a2e687e2206415807c2de6862902eb00046e5bf34d64d587e3892d4cb89db687c2a957 - languageName: node - linkType: hard - "levn@npm:^0.4.1": version: 0.4.1 resolution: "levn@npm:0.4.1" @@ -10207,7 +10030,7 @@ __metadata: languageName: node linkType: hard -"lib0@npm:^0.2.31, lib0@npm:^0.2.52, lib0@npm:^0.2.85, lib0@npm:^0.2.94, lib0@npm:^0.2.98": +"lib0@npm:^0.2.94, lib0@npm:^0.2.98": version: 0.2.98 resolution: "lib0@npm:0.2.98" dependencies: @@ -10287,13 +10110,6 @@ __metadata: languageName: node linkType: hard -"lodash.debounce@npm:^4.0.8": - version: 4.0.8 - resolution: "lodash.debounce@npm:4.0.8" - checksum: 10c0/762998a63e095412b6099b8290903e0a8ddcb353ac6e2e0f2d7e7d03abd4275fe3c689d88960eb90b0dde4f177554d51a690f22a343932ecbc50a5d111849987 - languageName: node - linkType: hard - "lodash.lowercase@npm:^4.3.0": version: 4.3.0 resolution: "lodash.lowercase@npm:4.3.0" @@ -10396,13 +10212,6 @@ __metadata: languageName: node linkType: hard -"ltgt@npm:^2.1.2": - version: 2.2.1 - resolution: "ltgt@npm:2.2.1" - checksum: 10c0/60fdad732c3aa6acf37e927a5ef58c0d1776192321d55faa1f8775c134c27fbf20ef8ec542fb7f7f33033f79c2a2df75cac39b43e274b32e9d95400154cd41f3 - languageName: node - linkType: hard - "lz-string@npm:^1.5.0": version: 1.5.0 resolution: "lz-string@npm:1.5.0" @@ -10792,13 +10601,6 @@ __metadata: languageName: node linkType: hard -"napi-macros@npm:~2.0.0": - version: 2.0.0 - resolution: "napi-macros@npm:2.0.0" - checksum: 10c0/583ef5084b43e49a12488cdcd4c5142f11e114e249b359161579b64f06776ed523c209d96e4ee2689e2e824c92445d0f529d817cc153f7cec549210296ec4be6 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -10846,17 +10648,6 @@ __metadata: languageName: node linkType: hard -"node-gyp-build@npm:~4.1.0": - version: 4.1.1 - resolution: "node-gyp-build@npm:4.1.1" - bin: - node-gyp-build: ./bin.js - node-gyp-build-optional: ./optional.js - node-gyp-build-test: ./build-test.js - checksum: 10c0/8c652daa855612c9ed8aa95dca5e7dc34d72f3849ef415412aef261e44956c0d73ba197401be5c605a709e47fbaa80c76bb31ce29c6bba4f829efbaf457b4698 - languageName: node - linkType: hard - "node-gyp@npm:latest": version: 10.2.0 resolution: "node-gyp@npm:10.2.0" @@ -11597,13 +11388,6 @@ __metadata: languageName: node linkType: hard -"prr@npm:~1.0.1": - version: 1.0.1 - resolution: "prr@npm:1.0.1" - checksum: 10c0/5b9272c602e4f4472a215e58daff88f802923b84bc39c8860376bb1c0e42aaf18c25d69ad974bd06ec6db6f544b783edecd5502cd3d184748d99080d68e4be5f - languageName: node - linkType: hard - "punycode@npm:^2.1.0, punycode@npm:^2.3.1": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -14259,15 +14043,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^6.2.1": - version: 6.2.3 - resolution: "ws@npm:6.2.3" - dependencies: - async-limiter: "npm:~1.0.0" - checksum: 10c0/56a35b9799993cea7ce2260197e7879f21bbbb194a967f31acbbda6f7f46ecda4365951966fb062044c95197e19fb2f053be6f65c172435455186835f494de41 - languageName: node - linkType: hard - "ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.2.3": version: 8.18.0 resolution: "ws@npm:8.18.0" @@ -14297,59 +14072,13 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.2, xtend@npm:~4.0.0, xtend@npm:~4.0.1": +"xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: 10c0/366ae4783eec6100f8a02dff02ac907bf29f9a00b82ac0264b4d8b832ead18306797e283cf19de776538babfdcb2101375ec5646b59f08c52128ac4ab812ed0e languageName: node linkType: hard -"y-leveldb@npm:^0.1.0": - version: 0.1.2 - resolution: "y-leveldb@npm:0.1.2" - dependencies: - level: "npm:^6.0.1" - lib0: "npm:^0.2.31" - peerDependencies: - yjs: ^13.0.0 - checksum: 10c0/c1bc82a7ac31406c36b41dcb7a01273323f8b353dec289512c1116cc4df54673ee112c0546b49b6431be4918c8267a3aeb891ba727a7adef8638eb4baa83fa57 - languageName: node - linkType: hard - -"y-protocols@npm:^1.0.5": - version: 1.0.6 - resolution: "y-protocols@npm:1.0.6" - dependencies: - lib0: "npm:^0.2.85" - peerDependencies: - yjs: ^13.0.0 - checksum: 10c0/fae7d63d4609e75eac8114320aa3581311993cd0c1b7ac03d13dc30c9a95d7b1680e4500786bb417c1fb3d11a69e1bed3e50d08e15febde9b8abf70f133eb917 - languageName: node - linkType: hard - -"y-websocket@npm:2.0.4": - version: 2.0.4 - resolution: "y-websocket@npm:2.0.4" - dependencies: - lib0: "npm:^0.2.52" - lodash.debounce: "npm:^4.0.8" - ws: "npm:^6.2.1" - y-leveldb: "npm:^0.1.0" - y-protocols: "npm:^1.0.5" - peerDependencies: - yjs: ^13.5.6 - dependenciesMeta: - ws: - optional: true - y-leveldb: - optional: true - bin: - y-websocket: bin/server.cjs - y-websocket-server: bin/server.cjs - checksum: 10c0/756d86169749970f4d31cd17bc45c90ef2198388db9c09cfdda85594ed6bd9b5255838930287b1e207cbb3852054eeb4dfbb49dde4a5844f5b936a398ab0e386 - languageName: node - linkType: hard - "y18n@npm:^4.0.0": version: 4.0.3 resolution: "y18n@npm:4.0.3"