diff --git a/src/frontend/apps/impress/package.json b/src/frontend/apps/impress/package.json index 0844cd45..3f8628d1 100644 --- a/src/frontend/apps/impress/package.json +++ b/src/frontend/apps/impress/package.json @@ -44,6 +44,7 @@ "@sentry/nextjs": "10.34.0", "@tanstack/react-query": "5.90.18", "@tiptap/extensions": "*", + "@y/websocket-server": "^0.1.1", "canvg": "4.0.3", "clsx": "2.1.1", "cmdk": "1.1.1", @@ -54,6 +55,7 @@ "i18next": "25.7.4", "i18next-browser-languagedetector": "8.2.0", "idb": "8.0.3", + "js-base64": "^3.7.8", "lodash": "4.17.23", "luxon": "3.7.2", "next": "15.5.9", @@ -70,6 +72,7 @@ "use-debounce": "10.1.0", "uuid": "13.0.0", "y-protocols": "1.0.7", + "y-websocket": "^3.0.0", "yjs": "*", "zustand": "5.0.10" }, diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index e0be2f28..6c88f99b 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -13,6 +13,7 @@ import { BlockNoteView } from '@blocknote/mantine'; import '@blocknote/mantine/style.css'; import { useCreateBlockNote } from '@blocknote/react'; import { HocuspocusProvider } from '@hocuspocus/provider'; +import { WebsocketProvider } from 'y-websocket'; import { useEffect, useMemo, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { css } from 'styled-components'; @@ -77,7 +78,8 @@ export const blockNoteSchema = (withMultiColumn?.(baseBlockNoteSchema) || interface BlockNoteEditorProps { doc: Doc; - provider: HocuspocusProvider; + // provider: HocuspocusProvider; + provider: WebsocketProvider; } export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { @@ -91,7 +93,8 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { // Determine if comments should be visible in the UI const showComments = canSeeComment; - useSaveDoc(doc.id, provider.document, isConnectedToCollabServer); + // useSaveDoc(doc.id, provider.document, isConnectedToCollabServer); + useSaveDoc(doc.id, provider.doc, isConnectedToCollabServer); const { i18n } = useTranslation(); let lang = i18n.resolvedLanguage; if (!lang || !(lang in locales)) { @@ -120,7 +123,8 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { { collaboration: { provider: provider as { awareness?: Awareness | undefined }, - fragment: provider.document.getXmlFragment('document-store'), + // fragment: provider.document.getXmlFragment('document-store'), + fragment: provider.doc.getXmlFragment('document-store'), user: { name: cursorName, color: randomColor(), diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx index f1ad090e..171a55a4 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx @@ -122,7 +122,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => { }); }, [authenticated, hasTracked, isPublicDoc, trackEvent]); - if (!isProviderReady || provider?.configuration.name !== doc.id) { + if (!isProviderReady || provider?.roomname !== doc.id) { return ; } @@ -134,9 +134,10 @@ export const DocEditor = ({ doc }: DocEditorProps) => { docEditor={ readOnly ? ( ) : ( diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx index a5d1d585..e93f5628 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useSaveDoc.tsx @@ -8,7 +8,7 @@ import { isFirefox } from '@/utils/userAgent'; import { toBase64 } from '../utils'; -const SAVE_INTERVAL = 60000; +const SAVE_INTERVAL = 100 * 60000; export const useSaveDoc = ( docId: string, @@ -35,6 +35,16 @@ export const useSaveDoc = ( _updatedDoc: Y.Doc, transaction: Y.Transaction, ) => { + console.log(333333); + if (transaction.local) { + console.log('LOCAL'); + } else { + console.log('REMOTE'); + } + // console.log(transaction); + + // transaction. + setIsLocalChange(transaction.local); }; @@ -50,6 +60,10 @@ export const useSaveDoc = ( return false; } + console.log('--------'); + console.log(111111); + console.log(yDoc); + updateDoc({ id: docId, content: toBase64(Y.encodeStateAsUpdate(yDoc)), diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx index f910e677..b8ca7f50 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useDuplicateDoc.tsx @@ -72,12 +72,14 @@ export function useDuplicateDoc(options?: DuplicateDocOptions) { const canSave = variables.canSave && provider && - provider.document.guid === variables.docId; + // provider.document.guid === variables.docId; + provider.doc.guid === variables.docId; if (canSave) { await updateDoc({ id: variables.docId, - content: toBase64(Y.encodeStateAsUpdate(provider.document)), + // content: toBase64(Y.encodeStateAsUpdate(provider.document)), + content: toBase64(Y.encodeStateAsUpdate(provider.doc)), }); } diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx index 553c0bbf..e98e8c97 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useCollaboration.tsx @@ -16,6 +16,8 @@ export const useCollaboration = (room?: string, initialContent?: Base64) => { return; } + console.log(222); + const newProvider = createProvider(collaborationUrl, room, initialContent); setBroadcastProvider(newProvider); }, [ diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/stores/IncomingMessage.ts b/src/frontend/apps/impress/src/features/docs/doc-management/stores/IncomingMessage.ts new file mode 100644 index 00000000..89e1b1c8 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-management/stores/IncomingMessage.ts @@ -0,0 +1,63 @@ +import { MessageType } from "@hocuspocus/provider"; +import type { Decoder } from "lib0/decoding"; +import { + createDecoder, + peekVarString, + readVarUint, + readVarUint8Array, + readVarString, +} from "lib0/decoding"; +import type { Encoder } from "lib0/encoding"; +import { + createEncoder, + writeVarUint, + writeVarUint8Array, + writeVarString, + length, +} from "lib0/encoding"; + +export class IncomingMessage { + data: any; + + encoder: Encoder; + + decoder: Decoder; + + constructor(data: any) { + this.data = data; + this.encoder = createEncoder(); + this.decoder = createDecoder(new Uint8Array(this.data)); + } + + peekVarString(): string { + return peekVarString(this.decoder); + } + + readVarUint(): MessageType { + return readVarUint(this.decoder); + } + + readVarString(): string { + return readVarString(this.decoder); + } + + readVarUint8Array() { + return readVarUint8Array(this.decoder); + } + + writeVarUint(type: MessageType) { + return writeVarUint(this.encoder, type); + } + + writeVarString(string: string) { + return writeVarString(this.encoder, string); + } + + writeVarUint8Array(data: Uint8Array) { + return writeVarUint8Array(this.encoder, data); + } + + length() { + return length(this.encoder); + } +} diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx index f1c8d511..7e7bad94 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx @@ -1,18 +1,35 @@ import { CloseEvent } from '@hocuspocus/common'; -import { HocuspocusProvider, WebSocketStatus } from '@hocuspocus/provider'; +import { + ConstructableOutgoingMessage, + HocuspocusProvider, + MessageType, + OutgoingMessageArguments, + WebSocketStatus, +} from '@hocuspocus/provider'; +import { WebsocketProvider } from 'y-websocket'; +// import { MessageSender } from '@hocuspocus/provider/src/MessageSender'; +// import { +// MessageSender +// } from '@hocuspocus/provider/default'; +import { fromUint8Array, toUint8Array } from 'js-base64'; +import * as decoding from 'lib0/decoding'; +import type { Data, MessageEvent } from 'ws'; import * as Y from 'yjs'; import { create } from 'zustand'; import { Base64 } from '@/docs/doc-management'; +import { IncomingMessage } from '@/docs/doc-management/stores/IncomingMessage'; export interface UseCollaborationStore { createProvider: ( providerUrl: string, storeId: string, initialDoc?: Base64, - ) => HocuspocusProvider; + ) => WebsocketProvider; + // ) => HocuspocusProvider; destroyProvider: () => void; - provider: HocuspocusProvider | undefined; + // provider: HocuspocusProvider | undefined; + provider: WebsocketProvider | undefined; isConnected: boolean; isReady: boolean; isSynced: boolean; @@ -30,6 +47,119 @@ const defaultValues = { type ExtendedCloseEvent = CloseEvent & { wasClean: boolean }; +class CustomProvider extends WebsocketProvider {} + +// class CustomProvider extends HocuspocusProvider { +// // eslint-disable-next-line @typescript-eslint/no-explicit-any +// send( +// message: ConstructableOutgoingMessage, +// args: Partial, +// ) { +// // if (!this._isAttached) return; +// // const messageSender = new MessageSender(message, args); +// // this.emit('outgoingMessage', { message: messageSender.message }); +// // messageSender.send(this.configuration.websocketProvider); + +// console.log('-----'); +// if (message.name === 'UpdateMessage') { +// console.log(8888); +// console.log(args.update); +// if (args.update) { +// console.log('.......'); +// console.log(typeof args.update); +// console.log('.......'); + +// // const base64EncodedUpdateAsString = fromUint8Array(args.update); + +// // const encoder = new TextEncoder(); +// // const base64EncodedUpdateAsUint8Array = encoder.encode( +// // base64EncodedUpdateAsString, +// // ); + +// // args.update = base64EncodedUpdateAsUint8Array; + +// const decodedUpdate = Y.decodeUpdate(args.update); + +// for (const struct of decodedUpdate.structs) { +// if (struct instanceof Y.Item) { +// if (struct.content instanceof Y.ContentString) { +// console.log('----'); +// console.log(struct.content.str); +// console.log(struct.content.getContent()); +// console.log(struct.content.getRef()); +// } + +// // TODO: check for other Y.ContentXXXX...? Maybe it could be image binary or something else +// // ... is it enough to encrypt only this and not the whole "update"? So the server can read it if needed + +// // console.log('----'); +// // console.log(struct); +// } +// } + +// // Y.write; + +// // const doc = new Y.Doc(); +// // Y.applyUpdate(doc, args.update); +// // const result = doc.toJSON(); + +// // const decoder = decoding.createDecoder(args.update); +// // const result = decoding.readVarString(decoder); + +// // console.log(fromUint8Array(args.update)); +// // console.log(result); +// } +// } else { +// // console.log(777777); +// // console.log(message.name); +// // console.log(args); +// } + +// // const msg = new message(); +// // msg.get(args); + +// // msg. + +// console.log('-----'); + +// super.send(message, args); +// } + +// // onMessage(event: MessageEvent) { +// // // const message = new IncomingMessage(event.data); +// // // const documentName = message.readVarString(); +// // // message.writeVarString(documentName); +// // // this.emit('message', { event, message: new IncomingMessage(event.data) }); +// // // new MessageReceiver(message).apply(this, true); + +// // const message = new IncomingMessage(event.data); + +// // const type = message.readVarUint(); + +// // if (type === MessageType.Sync) { +// // console.warn('THOMAS'); + +// // const base64EncodedUpdateAsUint8Array = event.data as Uint8Array; + +// // const decoder = new TextDecoder(); +// // const base64EncodedUpdateAsString = decoder.decode( +// // base64EncodedUpdateAsUint8Array, +// // ); + +// // event.data = toUint8Array(base64EncodedUpdateAsString) as Data; +// // } + +// // // this.emit('message', { event, message: new IncomingMessage(event.data) }); +// // // new MessageReceiver(message).apply(this, true); + +// // console.log('-----'); +// // console.log(99999); +// // console.log(event); + +// // super.onMessage(event); +// // } +// } + export const useProviderStore = create((set, get) => ({ ...defaultValues, createProvider: (wsUrl, storeId, initialDoc) => { @@ -41,65 +171,131 @@ export const useProviderStore = create((set, get) => ({ Y.applyUpdate(doc, Buffer.from(initialDoc, 'base64')); } - const provider = new HocuspocusProvider({ - url: wsUrl, - name: storeId, - document: doc, - onDisconnect(data) { - // Attempt to reconnect if the disconnection was clean (initiated by the client or server) - if ((data.event as ExtendedCloseEvent).wasClean) { + // + // TODO: should implement features for authentication (listening on message with custom payload?) + // same for previous "onSynced" + // + + const provider = new CustomProvider(wsUrl, storeId, doc); + + provider.on('connection-close', (event) => { + if (event) { + if (event.wasClean) { + // Attempt to reconnect if the disconnection was clean (initiated by the client or server) void provider.connect(); - } - }, - onAuthenticationFailed() { - set({ isReady: true, isConnected: false }); - }, - onAuthenticated() { - set({ isReady: true, isConnected: true }); - }, - onStatus: ({ status }) => { - set((state) => { - const nextConnected = status === WebSocketStatus.Connected; - + } else if (event.code === 1000) { /** - * status === WebSocketStatus.Connected does not mean we are totally connected - * because authentication can still be in progress and failed - * So we only update isConnected when we loose the connection + * Handle the "Reset Connection" event from the server + * This is triggered when the server wants to reset the connection + * for clients in the room. + * A disconnect is made automatically but it takes time to be triggered, + * so we force the disconnection here. */ - const connected = - status !== WebSocketStatus.Connected - ? { - isConnected: false, - } - : undefined; - - return { - ...connected, - isReady: state.isReady || status === WebSocketStatus.Disconnected, - hasLostConnection: - state.isConnected && !nextConnected - ? true - : state.hasLostConnection, - }; - }); - }, - onSynced: ({ state }) => { - set({ isSynced: state, isReady: true }); - }, - onClose(data) { - /** - * Handle the "Reset Connection" event from the server - * This is triggered when the server wants to reset the connection - * for clients in the room. - * A disconnect is made automatically but it takes time to be triggered, - * so we force the disconnection here. - */ - if (data.event.code === 1000) { provider.disconnect(); } - }, + } }); + provider.on('status', (event) => { + set((state) => { + const nextConnected = event.status === 'connected'; + + /** + * status === 'connected' does not mean we are totally connected + * because authentication can still be in progress and failed + * So we only update isConnected when we loose the connection + */ + const connected = + event.status !== 'connected' + ? { + isConnected: false, + } + : undefined; + + return { + ...connected, + isReady: state.isReady || event.status === 'disconnected', + hasLostConnection: + state.isConnected && !nextConnected + ? true + : state.hasLostConnection, + }; + }); + }); + + provider.on('sync', (state) => { + set({ isSynced: state, isReady: true }); + }); + + // const provider = new CustomProvider({ + // url: wsUrl, + // name: storeId, + // document: doc, + // onDisconnect(data) { + // // Attempt to reconnect if the disconnection was clean (initiated by the client or server) + // if ((data.event as ExtendedCloseEvent).wasClean) { + // void provider.connect(); + // } + // }, + // onMessage(data) { + // console.log('-----'); + // console.log(44444); + // console.log(data); + // }, + // // onOutgoingMessage(data) { + // // console.log('-----'); + // // console.log(555); + // // console.log(data); + // // }, + // onAuthenticationFailed() { + // set({ isReady: true, isConnected: false }); + // }, + // onAuthenticated() { + // set({ isReady: true, isConnected: true }); + // }, + // onStatus: ({ status }) => { + // set((state) => { + // const nextConnected = status === WebSocketStatus.Connected; + + // /** + // * status === WebSocketStatus.Connected does not mean we are totally connected + // * because authentication can still be in progress and failed + // * So we only update isConnected when we loose the connection + // */ + // const connected = + // status !== WebSocketStatus.Connected + // ? { + // isConnected: false, + // } + // : undefined; + + // return { + // ...connected, + // isReady: state.isReady || status === WebSocketStatus.Disconnected, + // hasLostConnection: + // state.isConnected && !nextConnected + // ? true + // : state.hasLostConnection, + // }; + // }); + // }, + // onSynced: ({ state }) => { + // set({ isSynced: state, isReady: true }); + // }, + // onClose(data) { + // /** + // * Handle the "Reset Connection" event from the server + // * This is triggered when the server wants to reset the connection + // * for clients in the room. + // * A disconnect is made automatically but it takes time to be triggered, + // * so we force the disconnection here. + // */ + // if (data.event.code === 1000) { + // provider.disconnect(); + // } + // }, + // }); + set({ provider, }); diff --git a/src/frontend/apps/impress/src/stores/useBroadcastStore.tsx b/src/frontend/apps/impress/src/stores/useBroadcastStore.tsx index 03fb1985..c5b7ef0e 100644 --- a/src/frontend/apps/impress/src/stores/useBroadcastStore.tsx +++ b/src/frontend/apps/impress/src/stores/useBroadcastStore.tsx @@ -1,4 +1,5 @@ import { HocuspocusProvider } from '@hocuspocus/provider'; +import { WebsocketProvider } from 'y-websocket'; import * as Y from 'yjs'; import { create } from 'zustand'; @@ -6,10 +7,13 @@ interface BroadcastState { addTask: (taskLabel: string, action: () => void) => void; broadcast: (taskLabel: string) => void; cleanupBroadcast: () => void; - getBroadcastProvider: () => HocuspocusProvider | undefined; + // getBroadcastProvider: () => HocuspocusProvider | undefined; + getBroadcastProvider: () => WebsocketProvider | undefined; handleProviderSync: () => void; - provider?: HocuspocusProvider; - setBroadcastProvider: (provider: HocuspocusProvider) => void; + // provider?: HocuspocusProvider; + provider?: WebsocketProvider; + // setBroadcastProvider: (provider: HocuspocusProvider) => void; + setBroadcastProvider: (provider: WebsocketProvider) => void; setTask: ( taskLabel: string, task: Y.Array, @@ -34,10 +38,12 @@ export const useBroadcastStore = create((set, get) => ({ // Clean up old provider listeners const oldProvider = get().provider; if (oldProvider) { - oldProvider.off('synced', get().handleProviderSync); + // oldProvider.off('synced', get().handleProviderSync); + oldProvider.off('sync', get().handleProviderSync); } - provider.on('synced', get().handleProviderSync); + // provider.on('synced', get().handleProviderSync); + provider.on('sync', get().handleProviderSync); set({ provider }); }, handleProviderSync: () => { @@ -61,7 +67,8 @@ export const useBroadcastStore = create((set, get) => ({ return; } - const task = provider.document.getArray(taskLabel); + // const task = provider.document.getArray(taskLabel); + const task = provider.doc.getArray(taskLabel); get().setTask(taskLabel, task, action); }, setTask: (taskLabel: string, task: Y.Array, action: () => void) => { @@ -102,7 +109,8 @@ export const useBroadcastStore = create((set, get) => ({ cleanupBroadcast: () => { const provider = get().provider; if (provider) { - provider.off('synced', get().handleProviderSync); + // provider.off('synced', get().handleProviderSync); + provider.off('sync', get().handleProviderSync); } // Unobserve all document-specific tasks diff --git a/src/frontend/servers/y-provider/package.json b/src/frontend/servers/y-provider/package.json index b2d026dc..c9c75935 100644 --- a/src/frontend/servers/y-provider/package.json +++ b/src/frontend/servers/y-provider/package.json @@ -21,12 +21,14 @@ "@sentry/node": "10.34.0", "@sentry/profiling-node": "10.34.0", "@tiptap/extensions": "*", + "@y/websocket-server": "^0.1.1", "axios": "1.13.2", "cors": "2.8.5", "express": "5.2.1", "express-ws": "5.0.2", "uuid": "13.0.0", "y-protocols": "1.0.7", + "y-websocket": "^3.0.0", "yjs": "*" }, "devDependencies": { diff --git a/src/frontend/servers/y-provider/src/handlers/collaborationResetConnectionsHandler.ts b/src/frontend/servers/y-provider/src/handlers/collaborationResetConnectionsHandler.ts index 41dfcee0..65e7c483 100644 --- a/src/frontend/servers/y-provider/src/handlers/collaborationResetConnectionsHandler.ts +++ b/src/frontend/servers/y-provider/src/handlers/collaborationResetConnectionsHandler.ts @@ -2,6 +2,7 @@ import { Request, Response } from 'express'; import { hocuspocusServer } from '@/servers'; import { logger } from '@/utils'; +import { closeConn, getYDoc } from '@/servers/standard/utils'; type ResetConnectionsRequestQuery = { room?: string; @@ -25,23 +26,38 @@ export const collaborationResetConnectionsHandler = ( * If no user ID is provided, close all connections in the room */ if (!userId) { - hocuspocusServer.hocuspocus.closeConnections(room); + // hocuspocusServer.hocuspocus.closeConnections(room); + + const doc = getYDoc(room); + + if (doc) { + doc.conns.forEach((_, conn) => closeConn(doc, conn)); + } } else { /** * Close connections for the user in the room */ - hocuspocusServer.hocuspocus.documents.forEach((doc) => { - if (doc.name !== room) { - return; - } + // hocuspocusServer.hocuspocus.documents.forEach((doc) => { + // if (doc.name !== room) { + // return; + // } + // doc.getConnections().forEach((connection) => { + // // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + // if (connection.context.userId === userId) { + // connection.close(); + // } + // }); + // }); - doc.getConnections().forEach((connection) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (connection.context.userId === userId) { - connection.close(); - } + const doc = getYDoc(room); + + if (doc) { + doc.conns.forEach((clientIds, conn) => { + // TODO: with this current implementation there is no logic about user ID but only also "clientID" + // ... it should be adapted first as for hocuspocus before having this metadata + // closeConn(doc, conn) }); - }); + } } res.status(200).json({ message: 'Connections reset' }); diff --git a/src/frontend/servers/y-provider/src/handlers/collaborationWSHandler.ts b/src/frontend/servers/y-provider/src/handlers/collaborationWSHandler.ts index 8890ad0b..ddcffb92 100644 --- a/src/frontend/servers/y-provider/src/handlers/collaborationWSHandler.ts +++ b/src/frontend/servers/y-provider/src/handlers/collaborationWSHandler.ts @@ -2,10 +2,15 @@ import { Request } from 'express'; import * as ws from 'ws'; import { hocuspocusServer } from '@/servers/hocuspocusServer'; +import { setupWSConnection } from '@/servers/standard/utils' export const collaborationWSHandler = (ws: ws.WebSocket, req: Request) => { try { - hocuspocusServer.hocuspocus.handleConnection(ws, req); + // hocuspocusServer.hocuspocus.handleConnection(ws, req); + + setupWSConnection(ws, req, { + gc: true, + }) } catch (error) { console.error('Failed to handle WebSocket connection:', error); ws.close(); diff --git a/src/frontend/servers/y-provider/src/servers/appServer.ts b/src/frontend/servers/y-provider/src/servers/appServer.ts index c9807bf7..cd9989e7 100644 --- a/src/frontend/servers/y-provider/src/servers/appServer.ts +++ b/src/frontend/servers/y-provider/src/servers/appServer.ts @@ -52,6 +52,9 @@ export const initApp = () => { * Route to convert Markdown or BlockNote blocks and Yjs content */ app.post( + // + // TODO: maybe since could be done on the frontend to avoid data going over the server? + // routes.CONVERT, httpSecurity, express.raw({ @@ -61,6 +64,9 @@ export const initApp = () => { convertHandler, ); + // + // TODO: make sure Sentry is not saving sensitive info for e2ee + // Sentry.setupExpressErrorHandler(app); app.get('/ping', (req, res) => { diff --git a/src/frontend/servers/y-provider/src/servers/hocuspocusServer.ts b/src/frontend/servers/y-provider/src/servers/hocuspocusServer.ts index d6c58658..3972f526 100644 --- a/src/frontend/servers/y-provider/src/servers/hocuspocusServer.ts +++ b/src/frontend/servers/y-provider/src/servers/hocuspocusServer.ts @@ -16,6 +16,9 @@ export const hocuspocusServer = new Server({ context, request, }) { + console.log(222222); + console.log('new CONNECTION'); + const roomParam = requestParameters.get('room'); if (documentName !== roomParam) { diff --git a/src/frontend/servers/y-provider/src/servers/standard/callback.js b/src/frontend/servers/y-provider/src/servers/standard/callback.js new file mode 100644 index 00000000..16d82282 --- /dev/null +++ b/src/frontend/servers/y-provider/src/servers/standard/callback.js @@ -0,0 +1,87 @@ +import http from 'http'; +import * as number from 'lib0/number'; + +const CALLBACK_URL = process.env.CALLBACK_URL + ? new URL(process.env.CALLBACK_URL) + : null; +const CALLBACK_TIMEOUT = number.parseInt( + process.env.CALLBACK_TIMEOUT || '5000', +); +const CALLBACK_OBJECTS = process.env.CALLBACK_OBJECTS + ? JSON.parse(process.env.CALLBACK_OBJECTS) + : {}; + +export const isCallbackSet = !!CALLBACK_URL; + +/** + * @param {import('./utils.js').WSSharedDoc} doc + */ +export const callbackHandler = (doc) => { + const room = doc.name; + const dataToSend = { + room, + data: {}, + }; + const sharedObjectList = Object.keys(CALLBACK_OBJECTS); + sharedObjectList.forEach((sharedObjectName) => { + const sharedObjectType = CALLBACK_OBJECTS[sharedObjectName]; + dataToSend.data[sharedObjectName] = { + type: sharedObjectType, + content: getContent(sharedObjectName, sharedObjectType, doc).toJSON(), + }; + }); + CALLBACK_URL && callbackRequest(CALLBACK_URL, CALLBACK_TIMEOUT, dataToSend); +}; + +/** + * @param {URL} url + * @param {number} timeout + * @param {Object} data + */ +const callbackRequest = (url, timeout, data) => { + data = JSON.stringify(data); + const options = { + hostname: url.hostname, + port: url.port, + path: url.pathname, + timeout, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(data), + }, + }; + const req = http.request(options); + req.on('timeout', () => { + console.warn('Callback request timed out.'); + req.abort(); + }); + req.on('error', (e) => { + console.error('Callback request error.', e); + req.abort(); + }); + req.write(data); + req.end(); +}; + +/** + * @param {string} objName + * @param {string} objType + * @param {import('./utils.js').WSSharedDoc} doc + */ +const getContent = (objName, objType, doc) => { + switch (objType) { + case 'Array': + return doc.getArray(objName); + case 'Map': + return doc.getMap(objName); + case 'Text': + return doc.getText(objName); + case 'XmlFragment': + return doc.getXmlFragment(objName); + case 'XmlElement': + return doc.getXmlElement(objName); + default: + return {}; + } +}; diff --git a/src/frontend/servers/y-provider/src/servers/standard/server.js b/src/frontend/servers/y-provider/src/servers/standard/server.js new file mode 100755 index 00000000..fdc6399c --- /dev/null +++ b/src/frontend/servers/y-provider/src/servers/standard/server.js @@ -0,0 +1,34 @@ +// import WebSocket from 'ws'; +// import http from 'http'; +// import * as number from 'lib0/number'; +// import { setupWSConnection } from './utils.js'; + +// const wss = new WebSocket.Server({ noServer: true }); +// const host = process.env.HOST || 'localhost'; +// const port = number.parseInt(process.env.PORT || '1234'); + +// const server = http.createServer((_request, response) => { +// response.writeHead(200, { 'Content-Type': 'text/plain' }); +// response.end('okay'); +// }); + +// wss.on('connection', setupWSConnection); + +// server.on('upgrade', (request, socket, head) => { +// // You may check auth of request here.. +// // Call `wss.HandleUpgrade` *after* you checked whether the client has access +// // (e.g. by checking cookies, or url parameters). +// // See https://github.com/websockets/ws#client-authentication +// wss.handleUpgrade( +// request, +// socket, +// head, +// /** @param {any} ws */ (ws) => { +// wss.emit('connection', ws, request); +// }, +// ); +// }); + +// server.listen(port, host, () => { +// console.log(`running at '${host}' on port ${port}`); +// }); diff --git a/src/frontend/servers/y-provider/src/servers/standard/utils.js b/src/frontend/servers/y-provider/src/servers/standard/utils.js new file mode 100644 index 00000000..ac4ffd81 --- /dev/null +++ b/src/frontend/servers/y-provider/src/servers/standard/utils.js @@ -0,0 +1,324 @@ +import * as Y from 'yjs'; +import * as syncProtocol from '@y/protocols/sync'; +import * as awarenessProtocol from '@y/protocols/awareness'; + +import * as encoding from 'lib0/encoding'; +import * as decoding from 'lib0/decoding'; +import * as map from 'lib0/map'; + +import * as eventloop from 'lib0/eventloop'; + +import { callbackHandler, isCallbackSet } from './callback.js'; + +const CALLBACK_DEBOUNCE_WAIT = parseInt( + process.env.CALLBACK_DEBOUNCE_WAIT || '2000', +); +const CALLBACK_DEBOUNCE_MAXWAIT = parseInt( + process.env.CALLBACK_DEBOUNCE_MAXWAIT || '10000', +); + +const debouncer = eventloop.createDebouncer( + CALLBACK_DEBOUNCE_WAIT, + CALLBACK_DEBOUNCE_MAXWAIT, +); + +const wsReadyStateConnecting = 0; +const wsReadyStateOpen = 1; +const wsReadyStateClosing = 2; // eslint-disable-line +const wsReadyStateClosed = 3; // eslint-disable-line + +// disable gc when using snapshots! +const gcEnabled = process.env.GC !== 'false' && process.env.GC !== '0'; +// const persistenceDir = process.env.YPERSISTENCE +/** + * @type {{bindState: function(string,WSSharedDoc):void, writeState:function(string,WSSharedDoc):Promise, provider: any}|null} + */ +let persistence = null; + +/** + * @param {{bindState: function(string,WSSharedDoc):void, + * writeState:function(string,WSSharedDoc):Promise,provider:any}|null} persistence_ + */ +export const setPersistence = (persistence_) => { + persistence = persistence_; +}; + +/** + * @return {null|{bindState: function(string,WSSharedDoc):void, + * writeState:function(string,WSSharedDoc):Promise}|null} used persistence layer + */ +export const getPersistence = () => persistence; + +/** + * @type {Map} + */ +export const docs = new Map(); + +const messageSync = 0; +const messageAwareness = 1; +// const messageAuth = 2 + +/** + * @param {Uint8Array} update + * @param {any} _origin + * @param {WSSharedDoc} doc + * @param {any} _tr + */ +const updateHandler = (update, _origin, doc, _tr) => { + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageSync); + syncProtocol.writeUpdate(encoder, update); + const message = encoding.toUint8Array(encoder); + doc.conns.forEach((_, conn) => send(doc, conn, message)); +}; + +/** + * @type {(ydoc: Y.Doc) => Promise} + */ +let contentInitializor = (_ydoc) => Promise.resolve(); + +/** + * This function is called once every time a Yjs document is created. You can + * use it to pull data from an external source or initialize content. + * + * @param {(ydoc: Y.Doc) => Promise} f + */ +export const setContentInitializor = (f) => { + contentInitializor = f; +}; + +export class WSSharedDoc extends Y.Doc { + /** + * @param {string} name + */ + constructor(name) { + super({ gc: gcEnabled }); + this.name = name; + /** + * Maps from conn to set of controlled user ids. Delete all user ids from awareness when this conn is closed + * @type {Map>} + */ + this.conns = new Map(); + /** + * @type {awarenessProtocol.Awareness} + */ + this.awareness = new awarenessProtocol.Awareness(this); + this.awareness.setLocalState(null); + /** + * @param {{ added: Array, updated: Array, removed: Array }} changes + * @param {Object | null} conn Origin is the connection that made the change + */ + const awarenessChangeHandler = ({ added, updated, removed }, conn) => { + const changedClients = added.concat(updated, removed); + if (conn !== null) { + const connControlledIDs = /** @type {Set} */ ( + this.conns.get(conn) + ); + if (connControlledIDs !== undefined) { + added.forEach((clientID) => { + connControlledIDs.add(clientID); + }); + removed.forEach((clientID) => { + connControlledIDs.delete(clientID); + }); + } + } + // broadcast awareness update + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients), + ); + const buff = encoding.toUint8Array(encoder); + this.conns.forEach((_, c) => { + send(this, c, buff); + }); + }; + this.awareness.on('update', awarenessChangeHandler); + this.on('update', /** @type {any} */ (updateHandler)); + if (isCallbackSet) { + this.on('update', (_update, _origin, doc) => { + debouncer(() => callbackHandler(/** @type {WSSharedDoc} */ (doc))); + }); + } + this.whenInitialized = contentInitializor(this); + } +} + +/** + * Gets a Y.Doc by name, whether in memory or on disk + * + * @param {string} docname - the name of the Y.Doc to find or create + * @param {boolean} gc - whether to allow gc on the doc (applies only when created) + * @return {WSSharedDoc} + */ +export const getYDoc = (docname, gc = true) => + map.setIfUndefined(docs, docname, () => { + const doc = new WSSharedDoc(docname); + doc.gc = gc; + if (persistence !== null) { + persistence.bindState(docname, doc); + } + docs.set(docname, doc); + return doc; + }); + +/** + * @param {any} conn + * @param {WSSharedDoc} doc + * @param {Uint8Array} message + */ +const messageListener = (conn, doc, message) => { + try { + const encoder = encoding.createEncoder(); + const decoder = decoding.createDecoder(message); + const messageType = decoding.readVarUint(decoder); + switch (messageType) { + case messageSync: + encoding.writeVarUint(encoder, messageSync); + syncProtocol.readSyncMessage(decoder, encoder, doc, conn); + + // If the `encoder` only contains the type of reply message and no + // message, there is no need to send the message. When `encoder` only + // contains the type of reply, its length is 1. + if (encoding.length(encoder) > 1) { + send(doc, conn, encoding.toUint8Array(encoder)); + } + break; + case messageAwareness: { + awarenessProtocol.applyAwarenessUpdate( + doc.awareness, + decoding.readVarUint8Array(decoder), + conn, + ); + break; + } + } + } catch (err) { + console.error(err); + // @ts-ignore + doc.emit('error', [err]); + } +}; + +/** + * @param {WSSharedDoc} doc + * @param {any} conn + */ +export const closeConn = (doc, conn) => { + if (doc.conns.has(conn)) { + /** + * @type {Set} + */ + // @ts-ignore + const controlledIds = doc.conns.get(conn); + doc.conns.delete(conn); + awarenessProtocol.removeAwarenessStates( + doc.awareness, + Array.from(controlledIds), + null, + ); + if (doc.conns.size === 0 && persistence !== null) { + // if persisted, we store state and destroy ydocument + persistence.writeState(doc.name, doc).then(() => { + doc.destroy(); + }); + docs.delete(doc.name); + } + } + conn.close(); +}; + +/** + * @param {WSSharedDoc} doc + * @param {import('ws').WebSocket} conn + * @param {Uint8Array} m + */ +const send = (doc, conn, m) => { + if ( + conn.readyState !== wsReadyStateConnecting && + conn.readyState !== wsReadyStateOpen + ) { + closeConn(doc, conn); + } + try { + conn.send(m, {}, (err) => { + err != null && closeConn(doc, conn); + }); + } catch (e) { + closeConn(doc, conn); + } +}; + +const pingTimeout = 30000; + +/** + * @param {import('ws').WebSocket} conn + * @param {import('http').IncomingMessage} req + * @param {any} opts + */ +export const setupWSConnection = ( + conn, + req, + { docName = (req.url || '').slice(1).split('?')[0], gc = true } = {}, +) => { + conn.binaryType = 'arraybuffer'; + // get doc, initialize if it does not exist yet + const doc = getYDoc(docName, gc); + doc.conns.set(conn, new Set()); + // listen and reply to events + conn.on( + 'message', + /** @param {ArrayBuffer} message */ (message) => + messageListener(conn, doc, new Uint8Array(message)), + ); + + // Check if connection is still alive + let pongReceived = true; + const pingInterval = setInterval(() => { + if (!pongReceived) { + if (doc.conns.has(conn)) { + closeConn(doc, conn); + } + clearInterval(pingInterval); + } else if (doc.conns.has(conn)) { + pongReceived = false; + try { + conn.ping(); + } catch (e) { + closeConn(doc, conn); + clearInterval(pingInterval); + } + } + }, pingTimeout); + conn.on('close', () => { + closeConn(doc, conn); + clearInterval(pingInterval); + }); + conn.on('pong', () => { + pongReceived = true; + }); + // put the following in a variables in a block so the interval handlers don't keep in in + // scope + { + // send sync step 1 + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageSync); + syncProtocol.writeSyncStep1(encoder, doc); + send(doc, conn, encoding.toUint8Array(encoder)); + const awarenessStates = doc.awareness.getStates(); + if (awarenessStates.size > 0) { + const encoder = encoding.createEncoder(); + encoding.writeVarUint(encoder, messageAwareness); + encoding.writeVarUint8Array( + encoder, + awarenessProtocol.encodeAwarenessUpdate( + doc.awareness, + Array.from(awarenessStates.keys()), + ), + ); + send(doc, conn, encoding.toUint8Array(encoder)); + } + } +}; diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index e4ad314b..caa6a194 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -1802,7 +1802,7 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz#9e585ab6086bef994c6e8a5b3a0481219ada862b" integrity sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ== -"@eslint-community/eslint-utils@^4.7.0", "@eslint-community/eslint-utils@^4.8.0": +"@eslint-community/eslint-utils@^4.7.0": version "4.9.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz#7308df158e064f0dd8b8fdb58aa14fa2a7f913b3" integrity sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g== @@ -1821,67 +1821,6 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.2.tgz#bccdf615bcf7b6e8db830ec0b8d21c9a25de597b" integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew== -"@eslint-community/regexpp@^4.12.1": - version "4.12.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" - integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== - -"@eslint/config-array@^0.21.1": - version "0.21.1" - resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.21.1.tgz#7d1b0060fea407f8301e932492ba8c18aff29713" - integrity sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA== - dependencies: - "@eslint/object-schema" "^2.1.7" - debug "^4.3.1" - minimatch "^3.1.2" - -"@eslint/config-helpers@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.4.2.tgz#1bd006ceeb7e2e55b2b773ab318d300e1a66aeda" - integrity sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw== - dependencies: - "@eslint/core" "^0.17.0" - -"@eslint/core@^0.17.0": - version "0.17.0" - resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.17.0.tgz#77225820413d9617509da9342190a2019e78761c" - integrity sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ== - dependencies: - "@types/json-schema" "^7.0.15" - -"@eslint/eslintrc@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" - integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^10.0.1" - globals "^14.0.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@9.39.2": - version "9.39.2" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.39.2.tgz#2d4b8ec4c3ea13c1b3748e0c97ecd766bdd80599" - integrity sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA== - -"@eslint/object-schema@^2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.7.tgz#6e2126a1347e86a4dedf8706ec67ff8e107ebbad" - integrity sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA== - -"@eslint/plugin-kit@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz#9779e3fd9b7ee33571a57435cf4335a1794a6cb2" - integrity sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA== - dependencies: - "@eslint/core" "^0.17.0" - levn "^0.4.1" - "@exodus/bytes@^1.6.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@exodus/bytes/-/bytes-1.8.0.tgz#8382835f71db8377cf634a4ef5a71806e86ba9c7" @@ -2103,29 +2042,6 @@ lib0 "^0.2.47" ws "^8.5.0" -"@humanfs/core@^0.19.1": - version "0.19.1" - resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" - integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== - -"@humanfs/node@^0.16.6": - version "0.16.7" - resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.7.tgz#822cb7b3a12c5a240a24f621b5a2413e27a45f26" - integrity sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ== - dependencies: - "@humanfs/core" "^0.19.1" - "@humanwhocodes/retry" "^0.4.0" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/retry@^0.4.0", "@humanwhocodes/retry@^0.4.2": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba" - integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ== - "@img/colour@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@img/colour/-/colour-1.0.0.tgz#d2fabb223455a793bf3bf9c70de3d28526aa8311" @@ -6611,7 +6527,7 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-underline/-/extension-underline-3.14.0.tgz#7d9ac55419f353cdd3817b8f5d56a11e909b1251" integrity sha512-zmnWlsi2g/tMlThHby0Je9O+v24j4d+qcXF3nuzLUUaDsGCEtOyC9RzwITft59ViK+Nc2PD2W/J14rsB0j+qoQ== -"@tiptap/extensions@*", "@tiptap/extensions@3.14.0", "@tiptap/extensions@^3.13.0": +"@tiptap/extensions@*", "@tiptap/extensions@^3.13.0": version "3.14.0" resolved "https://registry.yarnpkg.com/@tiptap/extensions/-/extensions-3.14.0.tgz#8367d3d644cf68b85341e059f5685b13b5722b1a" integrity sha512-qQBVKqzU4ZVjRn8W0UbdfE4LaaIgcIWHOMrNnJ+PutrRzQ6ZzhmD/kRONvRWBfG9z3DU7pSKGwVYSR2hztsGuQ== @@ -6785,7 +6701,7 @@ "@types/estree" "*" "@types/json-schema" "*" -"@types/estree@*", "@types/estree@1.0.8", "@types/estree@^1.0.0", "@types/estree@^1.0.6", "@types/estree@^1.0.8": +"@types/estree@*", "@types/estree@1.0.8", "@types/estree@^1.0.0", "@types/estree@^1.0.8": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== @@ -6943,13 +6859,20 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@22.10.7", "@types/node@24.10.9", "@types/node@>=13.7.0", "@types/node@^24.0.1": +"@types/node@*", "@types/node@>=13.7.0", "@types/node@^24.0.1": version "24.10.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-24.10.9.tgz#1aeb5142e4a92957489cac12b07f9c7fe26057d0" integrity sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw== dependencies: undici-types "~7.16.0" +"@types/node@22.10.7": + version "22.10.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.7.tgz#14a1ca33fd0ebdd9d63593ed8d3fbc882a6d28d7" + integrity sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg== + dependencies: + undici-types "~6.20.0" + "@types/parse-json@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" @@ -7002,7 +6925,7 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@*", "@types/react-dom@19.2.3": +"@types/react-dom@*": version "19.2.3" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.2.3.tgz#c1e305d15a52a3e508d54dca770d202cb63abf2c" integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== @@ -7019,7 +6942,7 @@ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== -"@types/react@*", "@types/react@19.2.8": +"@types/react@*": version "19.2.8" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.8.tgz#307011c9f5973a6abab8e17d0293f48843627994" integrity sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg== @@ -7776,6 +7699,17 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +"@y/websocket-server@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@y/websocket-server/-/websocket-server-0.1.1.tgz#861cbbbf85aa4e32b36514f8cd22619d3e3b8c5a" + integrity sha512-pPtXm5Ceqs4orhXXHwm2I+u1mKNBDNzlrwNiI7OMwM7PlVS4WCMpiIuSB8WsYeSuISbvpXPNvaj6H1MoQBbE+g== + dependencies: + lib0 "^0.2.102" + y-protocols "^1.0.5" + optionalDependencies: + ws "^6.2.1" + y-leveldb "^0.1.0" + "@zip.js/zip.js@^2.8.8": version "2.8.10" resolved "https://registry.yarnpkg.com/@zip.js/zip.js/-/zip.js-2.8.10.tgz#98a0cc7fdef9d6e227236271af412db02b18a5b2" @@ -7786,6 +7720,28 @@ abs-svg-path@^0.1.1: resolved "https://registry.yarnpkg.com/abs-svg-path/-/abs-svg-path-0.1.1.tgz#df601c8e8d2ba10d4a76d625e236a9a39c2723bf" integrity sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA== +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~6.2.1, abstract-leveldown@~6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + accepts@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" @@ -7804,11 +7760,6 @@ acorn-import-phases@^1.0.3: resolved "https://registry.yarnpkg.com/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz#16eb850ba99a056cb7cbfe872ffb8972e18c8bd7" integrity sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ== -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - acorn-walk@^8.1.1: version "8.3.4" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.4.tgz#794dd169c3977edf4ba4ea47583587c5866236b7" @@ -7847,16 +7798,6 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.0, ajv@^8.9.0: version "8.17.1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" @@ -7896,7 +7837,7 @@ ansi-styles@^5.0.0, ansi-styles@^5.2.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.2.1: +ansi-styles@^6.1.0, ansi-styles@^6.2.1: version "6.2.3" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== @@ -8066,6 +8007,11 @@ async-function@^1.0.0: resolved "https://registry.yarnpkg.com/async-function/-/async-function-1.0.0.tgz#509c9fca60eaf85034c6829838188e4e4c8ffb2b" integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA== +async-limiter@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" + integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== + async-lock@^1.3.1: version "1.4.1" resolved "https://registry.yarnpkg.com/async-lock/-/async-lock-1.4.1.tgz#56b8718915a9b68b10fce2f2a9a3dddf765ef53f" @@ -8417,6 +8363,14 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@^5.5.0, buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -8518,7 +8472,7 @@ chai@^6.2.1: resolved "https://registry.yarnpkg.com/chai/-/chai-6.2.1.tgz#d1e64bc42433fbee6175ad5346799682060b5b6a" integrity sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg== -chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.2: +chalk@4.1.2, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -9084,7 +9038,7 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: +debug@4, debug@^4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -9122,16 +9076,19 @@ dedent@^1.6.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.0.tgz#c1f9445335f0175a96587be245a282ff451446ca" integrity sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ== -deep-is@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - deepmerge@4.3.1, deepmerge@^4.2.2, deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" @@ -9400,6 +9357,16 @@ encodeurl@^2.0.0: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + encoding-sniffer@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz#396ec97ac22ce5a037ba44af1992ac9d46a7b819" @@ -9441,6 +9408,13 @@ eol@^0.9.1: resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd" integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg== +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + error-causes@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/error-causes/-/error-causes-3.0.2.tgz#0d657671293d806f0b2008a4cf85518b762865c2" @@ -9840,14 +9814,6 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.4.0.tgz#88e646a207fad61436ffa39eb505147200655c82" - integrity sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" @@ -9858,67 +9824,11 @@ eslint-visitor-keys@^4.2.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz#4cfea60fe7dd0ad8e816e1ed026c1d5251b512c1" integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ== -eslint@9.39.2: - version "9.39.2" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.39.2.tgz#cb60e6d16ab234c0f8369a3fe7cc87967faf4b6c" - integrity sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw== - dependencies: - "@eslint-community/eslint-utils" "^4.8.0" - "@eslint-community/regexpp" "^4.12.1" - "@eslint/config-array" "^0.21.1" - "@eslint/config-helpers" "^0.4.2" - "@eslint/core" "^0.17.0" - "@eslint/eslintrc" "^3.3.1" - "@eslint/js" "9.39.2" - "@eslint/plugin-kit" "^0.4.1" - "@humanfs/node" "^0.16.6" - "@humanwhocodes/module-importer" "^1.0.1" - "@humanwhocodes/retry" "^0.4.2" - "@types/estree" "^1.0.6" - ajv "^6.12.4" - chalk "^4.0.0" - cross-spawn "^7.0.6" - debug "^4.3.2" - escape-string-regexp "^4.0.0" - eslint-scope "^8.4.0" - eslint-visitor-keys "^4.2.1" - espree "^10.4.0" - esquery "^1.5.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^8.0.0" - find-up "^5.0.0" - glob-parent "^6.0.2" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - json-stable-stringify-without-jsonify "^1.0.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.3" - -espree@^10.0.1, espree@^10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.4.0.tgz#d54f4949d4629005a1fa168d937c3ff1f7e2a837" - integrity sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ== - dependencies: - acorn "^8.15.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.2.1" - esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" - integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== - dependencies: - estraverse "^5.1.0" - esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -9931,7 +9841,7 @@ estraverse@^4.1.1: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== -estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: +estraverse@^5.2.0, estraverse@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== @@ -10063,7 +9973,7 @@ extend@^3.0.0: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: +fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -10105,16 +10015,11 @@ fast-glob@^3.2.9, fast-glob@^3.3.2, fast-glob@^3.3.3: merge2 "^1.3.0" micromatch "^4.0.8" -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - fast-safe-stringify@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" @@ -10182,13 +10087,6 @@ file-entry-cache@^11.1.1: dependencies: flat-cache "^6.1.19" -file-entry-cache@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" - integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== - dependencies: - flat-cache "^4.0.0" - file-selector@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-2.1.2.tgz#fe7c7ee9e550952dfbc863d73b14dc740d7de8b4" @@ -10243,14 +10141,6 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -flat-cache@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" - integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== - dependencies: - flatted "^3.2.9" - keyv "^4.5.4" - flat-cache@^6.1.19: version "6.1.19" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-6.1.19.tgz#20e5b201c9b181a7b773b3b150108932077d2bbf" @@ -10260,7 +10150,7 @@ flat-cache@^6.1.19: flatted "^3.3.3" hookified "^1.13.0" -flatted@^3.2.9, flatted@^3.3.3: +flatted@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== @@ -10606,11 +10496,6 @@ global-prefix@^3.0.0: kind-of "^6.0.2" which "^1.3.1" -globals@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" - integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== - globals@^16.4.0: version "16.5.0" resolved "https://registry.yarnpkg.com/globals/-/globals-16.5.0.tgz#ccf1594a437b97653b2be13ed4d8f5c9f850cac1" @@ -11128,7 +11013,7 @@ idb@^7.0.1: resolved "https://registry.yarnpkg.com/idb/-/idb-7.1.1.tgz#d910ded866d32c7ced9befc5bfdf36f572ced72b" integrity sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ== -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -11153,6 +11038,11 @@ image-meta@^0.2.2: resolved "https://registry.yarnpkg.com/image-meta/-/image-meta-0.2.2.tgz#a88dbdf1983d7c23a80c3e71d3b8acdb5379f5e0" integrity sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA== +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + immediate@~3.0.5: version "3.0.6" resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" @@ -11367,7 +11257,7 @@ is-generator-function@^1.0.10: has-tostringtag "^1.0.2" safe-regex-test "^1.1.0" -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -11994,6 +11884,11 @@ jest@30.2.0: import-local "^3.2.0" jest-cli "30.2.0" +js-base64@^3.7.8: + version "3.7.8" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.8.tgz#af44496bc09fa178ed9c4adf67eb2b46f5c6d2a4" + integrity sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -12072,21 +11967,11 @@ jsesc@^3.0.2, jsesc@~3.1.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -json-buffer@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" - integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== - json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - json-schema-traverse@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" @@ -12097,11 +11982,6 @@ json-schema@^0.4.0: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - json5@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" @@ -12155,13 +12035,6 @@ jszip@^3.10.1: readable-stream "~2.3.6" setimmediate "^1.0.5" -keyv@^4.5.4: - version "4.5.4" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" - integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== - dependencies: - json-buffer "3.0.1" - keyv@^5.5.4: version "5.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-5.5.4.tgz#0f26a32183a5058f93fc6e02ced6318f66e8a9ea" @@ -12201,18 +12074,99 @@ lead@^4.0.0: resolved "https://registry.yarnpkg.com/lead/-/lead-4.0.0.tgz#5317a49effb0e7ec3a0c8fb9c1b24fb716aab939" integrity sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg== +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-js@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/level-js/-/level-js-5.0.2.tgz#5e280b8f93abd9ef3a305b13faf0b5397c969b55" + integrity sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg== + dependencies: + abstract-leveldown "~6.2.3" + buffer "^5.5.0" + inherits "^2.0.3" + ltgt "^2.1.2" + +level-packager@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/level/-/level-6.0.1.tgz#dc34c5edb81846a6de5079eac15706334b0d7cd6" + integrity sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw== + dependencies: + level-js "^5.0.0" + level-packager "^5.1.0" + leveldown "^5.4.0" + +leveldown@^5.4.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/leveldown/-/leveldown-5.6.0.tgz#16ba937bb2991c6094e13ac5a6898ee66d3eee98" + integrity sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ== + dependencies: + abstract-leveldown "~6.2.1" + napi-macros "~2.0.0" + node-gyp-build "~4.1.0" + +levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== +lib0@^0.2.102, lib0@^0.2.31, lib0@^0.2.99: + version "0.2.117" + resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.117.tgz#6c3f926475d28904af05b590703cbbbc29475716" + integrity sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw== dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" + isomorphic.js "^0.2.4" lib0@^0.2.109, lib0@^0.2.47, lib0@^0.2.85, lib0@^0.2.87: version "0.2.114" @@ -12221,13 +12175,6 @@ lib0@^0.2.109, lib0@^0.2.47, lib0@^0.2.85, lib0@^0.2.87: dependencies: isomorphic.js "^0.2.4" -lib0@^0.2.99: - version "0.2.117" - resolved "https://registry.yarnpkg.com/lib0/-/lib0-0.2.117.tgz#6c3f926475d28904af05b590703cbbbc29475716" - integrity sha512-DeXj9X5xDCjgKLU/7RR+/HQEVzuuEUiwldwOGsHK/sfAfELGWEyTcf0x+uOvCvK3O2zPmZePXWL85vtia6GyZw== - dependencies: - isomorphic.js "^0.2.4" - lie@~3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" @@ -12365,6 +12312,11 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +ltgt@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA== + luxon@3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.7.2.tgz#d697e48f478553cca187a0f8436aff468e3ba0ba" @@ -13051,6 +13003,11 @@ nanoid@^5.1.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.1.6.tgz#30363f664797e7d40429f6c16946d6bd7a3f26c9" integrity sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg== +napi-macros@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/napi-macros/-/napi-macros-2.0.0.tgz#2b6bae421e7b96eb687aa6c77a7858640670001b" + integrity sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg== + napi-postinstall@^0.3.0: version "0.3.4" resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.3.4.tgz#7af256d6588b5f8e952b9190965d6b019653bbb9" @@ -13114,6 +13071,11 @@ node-fetch@2.7.0, node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-gyp-build@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.1.1.tgz#d7270b5d86717068d114cc57fff352f96d745feb" + integrity sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -13289,18 +13251,6 @@ oniguruma-to-es@^4.3.4: regex "^6.0.1" regex-recursion "^6.0.2" -optionator@^0.9.3: - version "0.9.4" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" - integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.5" - orderedmap@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-2.1.1.tgz#61481269c44031c449915497bf5a4ad273c512d2" @@ -13662,11 +13612,6 @@ preact@^10.28.0: resolved "https://registry.yarnpkg.com/preact/-/preact-10.28.2.tgz#4b668383afa4b4a2546bbe4bd1747e02e2360138" integrity sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA== -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - prettier-linter-helpers@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" @@ -13899,7 +13844,7 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transfor dependencies: prosemirror-model "^1.21.0" -prosemirror-view@1.41.4, prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.38.1, prosemirror-view@^1.39.1, prosemirror-view@^1.41.4: +prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.27.0, prosemirror-view@^1.31.0, prosemirror-view@^1.38.1, prosemirror-view@^1.39.1, prosemirror-view@^1.41.4: version "1.41.4" resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.41.4.tgz#4e1b3e90accc0eebe3bddb497a40ce54e4de722d" integrity sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA== @@ -13939,6 +13884,11 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + pstree.remy@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" @@ -14280,7 +14230,7 @@ react-dnd@^14.0.3: fast-deep-equal "^3.1.3" hoist-non-react-statics "^3.3.2" -react-dom@*, react-dom@19.2.3: +react-dom@*: version "19.2.3" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.3.tgz#f0b61d7e5c4a86773889fcc1853af3ed5f215b17" integrity sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg== @@ -14535,7 +14485,7 @@ react-window@^1.8.11: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -react@*, react@19.2.3: +react@*: version "19.2.3" resolved "https://registry.yarnpkg.com/react/-/react-19.2.3.tgz#d83e5e8e7a258cf6b4fe28640515f99b87cd19b8" integrity sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA== @@ -15413,7 +15363,7 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.1.2: +string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== @@ -16100,13 +16050,6 @@ tslib@2.8.1, tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0, tslib@^2.6. resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -16186,7 +16129,7 @@ typed-array-length@^1.0.7: possible-typed-array-names "^1.0.0" reflect.getprototypeof "^1.0.6" -typescript@*, typescript@5.9.3, typescript@^5.0.4: +typescript@*, typescript@^5.0.4: version "5.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== @@ -16224,6 +16167,11 @@ underscore.string@~3.3.4: sprintf-js "^1.1.1" util-deprecate "^1.0.2" +undici-types@~6.20.0: + version "6.20.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + undici-types@~7.16.0: version "7.16.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" @@ -16412,13 +16360,6 @@ update-browserslist-db@^1.2.0: escalade "^3.2.0" picocolors "^1.1.1" -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - use-callback-ref@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz#98d9fab067075841c5b2c6852090d5d0feabe2bf" @@ -16916,11 +16857,6 @@ why-is-node-running@^2.3.0: siginfo "^2.0.0" stackback "0.0.2" -word-wrap@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -17104,7 +17040,25 @@ workbox-window@7.1.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@9.0.2, wrap-ansi@^7.0.0, wrap-ansi@^8.1.0, wrap-ansi@^9.0.0: +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrap-ansi@^9.0.0: version "9.0.2" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-9.0.2.tgz#956832dea9494306e6d209eb871643bb873d7c98" integrity sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww== @@ -17131,6 +17085,13 @@ ws@8.19.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.19.0.tgz#ddc2bdfa5b9ad860204f5a72a4863a8895fd8c8b" integrity sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg== +ws@^6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.3.tgz#ccc96e4add5fd6fedbc491903075c85c5a11d9ee" + integrity sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA== + dependencies: + async-limiter "~1.0.0" + ws@^7.4.6: version "7.5.10" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" @@ -17163,11 +17124,19 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xtend@^4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +y-leveldb@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/y-leveldb/-/y-leveldb-0.1.2.tgz#43f6c5004b6891b57926d8a1e0eb0c883003e34b" + integrity sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg== + dependencies: + level "^6.0.1" + lib0 "^0.2.31" + y-prosemirror@^1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/y-prosemirror/-/y-prosemirror-1.3.7.tgz#f88e553da4ea33278b114cf0b6a0ea978b154e84" @@ -17175,7 +17144,7 @@ y-prosemirror@^1.3.7: dependencies: lib0 "^0.2.109" -y-protocols@1.0.7: +y-protocols@1.0.7, y-protocols@^1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/y-protocols/-/y-protocols-1.0.7.tgz#6631c492e75b78b3a61353a60067e6f8a4c38d5f" integrity sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw== @@ -17189,6 +17158,14 @@ y-protocols@^1.0.6: dependencies: lib0 "^0.2.85" +y-websocket@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/y-websocket/-/y-websocket-3.0.0.tgz#e86bdb29cc0a53cb8d6e33ec8d24614a723832af" + integrity sha512-mUHy7AzkOZ834T/7piqtlA8Yk6AchqKqcrCXjKW8J1w2lPtRDjz8W5/CvXz9higKAHgKRKqpI3T33YkRFLkPtg== + dependencies: + lib0 "^0.2.102" + y-protocols "^1.0.5" + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -17239,7 +17216,7 @@ yargs@^17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" -yjs@*, yjs@13.6.29, yjs@^13.6.27: +yjs@*, yjs@^13.6.27: version "13.6.29" resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.6.29.tgz#bdc3e8379ff36603bcd6d3a1889752e4dd00f637" integrity sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==