y-mxgraph 0.8.6 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","sources":["../../../iframe-bridge/src/origin.ts","../../../iframe-bridge/src/server.ts"],"sourcesContent":["/**\n * iframe bridge 内部变更的 origin 标识。\n * 当 provider 端产生 ydoc-update 时,使用此 origin 标记,\n * 以便 server 端的 UndoManager 能正确追踪来自 iframe 的变更。\n */\nexport const IFRAME_ORIGIN: object = {};\n\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let forceFullSync = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingUpdatesToIframe: Array<{ update: Uint8Array; isBaseline: boolean }> = [];\n let serverSeq = 0;\n const unackedServerUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean }>();\n\n function tryAddIframeOriginTracking() {\n if (!undoManager) return;\n try {\n if (typeof (undoManager as any).addTrackedOrigin === \"function\") {\n (undoManager as any).addTrackedOrigin(IFRAME_ORIGIN);\n iframeOriginTracked = true;\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n function tryRemoveIframeOriginTracking() {\n if (!undoManager || !iframeOriginTracked) return;\n try {\n if (typeof (undoManager as any).removeTrackedOrigin === \"function\") {\n (undoManager as any).removeTrackedOrigin(IFRAME_ORIGIN);\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n tryAddIframeOriginTracking();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n const wasForceFullSync = forceFullSync;\n forceFullSync = false;\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n }\n if (!wasForceFullSync) {\n for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq }, \"*\");\n }\n }\n }\n connectListeners.forEach((fn) => fn());\n } else {\n unackedServerUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n const message = { type, payload: payload ? Array.from(payload) : [] };\n logMessage(\"send\", type, payload);\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n function postObjectToIframe(message: Record<string, unknown>) {\n const type = message.type as string;\n logMessage(\"send\", type, message);\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n logMessage(\"send\", \"ydoc-update\", update);\n if (!connected) {\n if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingUpdatesToIframe.length = 0;\n console.warn(\"[iframe-bridge server] queue full, forcing full sync on reconnect\");\n }\n pendingUpdatesToIframe.push({ update, isBaseline });\n return;\n }\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n };\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n logMessage(\"send\", \"undo-state\", state);\n cw.postMessage(state, \"*\");\n }\n }\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n logMessage(\"send\", \"awareness-update\", update);\n postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"ydoc-pending-updates\") {\n logMessage(\"recv\", \"ydoc-pending-updates\", payload);\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const arr = item.update ?? item;\n const isBaseline = item.isBaseline ?? false;\n const update = new Uint8Array(arr);\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n }\n }\n } else if (msgType === \"init\") {\n logMessage(\"recv\", \"init\", payload);\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n logMessage(\"send\", \"ydoc-sync\", { bytes: docState.length });\n postObjectToIframe({ type: \"ydoc-sync\", payload: Array.from(docState), protocolVersion: 2 });\n unackedServerUpdates.clear();\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID, protocolVersion: 2 };\n logMessage(\"send\", \"pong\", message);\n cw.postMessage(message, \"*\");\n }\n } else if (msgType === \"ydoc-update\") {\n logMessage(\"recv\", \"ydoc-update\", payload);\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n const inSeq = event.data.seq;\n if (inSeq != null) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update-ack\", seq: inSeq }, \"*\");\n }\n }\n } else if (msgType === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedServerUpdates.delete(ackSeq);\n }\n } else if (msgType === \"awareness-local-state\") {\n // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n destroy: () => {\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n }\n setConnected(false);\n postToIframe(\"disconnect\");\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.off(\"stack-item-popped\", onUndoPopped);\n undoManager.off(\"stack-cleared\", onStackCleared);\n undoManager.off(\"stack-item-added\", onStackItemAdded);\n tryRemoveIframeOriginTracking();\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
1
+ {"version":3,"file":"server.cjs","sources":["../../../iframe-bridge/src/server.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n /** pending update 超时回调(ms),不传则不检测 */\n pendingTimeoutMs?: number;\n /** pending update 超时触发的回调 */\n onPendingTimeout?: (info: { pendingCount: number; oldestMs: number }) => void;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n forceSyncToClient: () => void;\n /** 暂停消息处理(模拟丢失) */\n pause: () => void;\n /** 恢复消息处理 */\n resume: () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false, pendingTimeoutMs, onPendingTimeout } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let forceFullSync = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingUpdatesToIframe: Array<{ update: Uint8Array; isBaseline: boolean }> = [];\n let serverSeq = 0;\n const unackedServerUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean; sentAt: number }>();\n let pendingCheckTimer: ReturnType<typeof setTimeout> | null = null;\n\n function schedulePendingCheck() {\n if (pendingCheckTimer || !pendingTimeoutMs || !onPendingTimeout) return;\n pendingCheckTimer = setTimeout(() => {\n pendingCheckTimer = null;\n const now = Date.now();\n const unackedCount = unackedServerUpdates.size;\n const pendingCount = pendingUpdatesToIframe.length;\n if (unackedCount > 0 || pendingCount > 0) {\n const oldest = unackedCount > 0\n ? Math.min(...Array.from(unackedServerUpdates.values()).map((v) => v.sentAt))\n : now;\n onPendingTimeout({\n pendingCount: unackedCount + pendingCount,\n oldestMs: now - oldest,\n });\n }\n }, pendingTimeoutMs);\n }\n\n // capabilities learned from the iframe (if any)\n let iframeCapabilities: Record<string, unknown> | null = null;\n\n function tryAddIframeOriginTracking() {\n if (!undoManager) return;\n try {\n if (typeof (undoManager as any).addTrackedOrigin === \"function\") {\n (undoManager as any).addTrackedOrigin(IFRAME_ORIGIN);\n iframeOriginTracked = true;\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n function tryRemoveIframeOriginTracking() {\n if (!undoManager || !iframeOriginTracked) return;\n try {\n if (typeof (undoManager as any).removeTrackedOrigin === \"function\") {\n (undoManager as any).removeTrackedOrigin(IFRAME_ORIGIN);\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n tryAddIframeOriginTracking();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n const wasForceFullSync = forceFullSync;\n forceFullSync = false;\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline, sentAt: Date.now() });\n }\n if (!wasForceFullSync) {\n for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq }, \"*\");\n }\n }\n }\n\n // send a capabilities request to the iframe so both sides can negotiate\n // older providers will ignore unknown messages, so this is backwards compatible\n postObjectToIframe({ type: \"capabilities-request\" });\n\n connectListeners.forEach((fn) => fn());\n } else {\n unackedServerUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n const message = { type, payload: payload ? Array.from(payload) : [] };\n logMessage(\"send\", type, payload);\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n function postObjectToIframe(message: Record<string, unknown>) {\n const type = message.type as string;\n logMessage(\"send\", type, message);\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n logMessage(\"send\", \"ydoc-update\", update);\n if (!connected) {\n if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingUpdatesToIframe.length = 0;\n console.warn(\"[iframe-bridge server] queue full, forcing full sync on reconnect\");\n }\n pendingUpdatesToIframe.push({ update, isBaseline });\n schedulePendingCheck();\n return;\n }\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline, sentAt: Date.now() });\n schedulePendingCheck();\n };\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n logMessage(\"send\", \"undo-state\", state);\n cw.postMessage(state, \"*\");\n }\n }\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n logMessage(\"send\", \"awareness-update\", update);\n postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"ydoc-pending-updates\") {\n logMessage(\"recv\", \"ydoc-pending-updates\", payload);\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const arr = item.update ?? item;\n const isBaseline = item.isBaseline ?? false;\n const update = new Uint8Array(arr);\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n }\n }\n } else if (msgType === \"init\") {\n logMessage(\"recv\", \"init\", payload);\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n logMessage(\"send\", \"ydoc-sync\", { bytes: docState.length });\n postObjectToIframe({ type: \"ydoc-sync\", payload: Array.from(docState), protocolVersion: 2 });\n unackedServerUpdates.clear();\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID, protocolVersion: 2 };\n logMessage(\"send\", \"pong\", message);\n cw.postMessage(message, \"*\");\n }\n } else if (msgType === \"ydoc-update\") {\n logMessage(\"recv\", \"ydoc-update\", payload);\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n const inSeq = event.data.seq;\n if (inSeq != null) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update-ack\", seq: inSeq }, \"*\");\n }\n }\n } else if (msgType === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedServerUpdates.delete(ackSeq);\n }\n } else if (msgType === \"awareness-local-state\") {\n // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"consistency-check\") {\n // provider 请求一致性检查:比较 state vector\n logMessage(\"recv\", \"consistency-check\", payload);\n const providerSV = new Uint8Array(event.data.stateVector || []);\n const serverSV = Y.encodeStateVector(ydoc);\n // 简单比较:如果 state vector 不同,发送 force-sync\n const svMatch = providerSV.length === serverSV.length &&\n providerSV.every((v: number, i: number) => v === serverSV[i]);\n if (!svMatch) {\n log(\"consistency mismatch detected, sending force-sync\");\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"force-sync\" }, \"*\");\n }\n }\n } else if (msgType === \"request-full-sync\") {\n // provider 请求完整同步\n logMessage(\"recv\", \"request-full-sync\", payload);\n const docState = Y.encodeStateAsUpdate(ydoc);\n postObjectToIframe({ type: \"ydoc-sync\", payload: Array.from(docState), protocolVersion: 2 });\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n } else if (msgType === \"capabilities-request\") {\n // provider 想协商能力集,回复 capabilities-reply\n logMessage(\"recv\", \"capabilities-request\", payload);\n postObjectToIframe({ type: \"capabilities-reply\", capabilities: { consistency: true } });\n } else if (msgType === \"capabilities-reply\") {\n // provider 回复能力集,保存以便后续使用(可扩展)\n logMessage(\"recv\", \"capabilities-reply\", payload);\n iframeCapabilities = event.data.capabilities ?? null;\n log(\"[DEBUG] received iframe capabilities\", iframeCapabilities);\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n let paused = false;\n const wrappedOnMessage = (event: MessageEvent) => {\n if (paused) return;\n onMessage(event);\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", wrappedOnMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n forceSyncToClient() {\n if (!connected) return;\n const cw = iframe.contentWindow;\n if (!cw) return;\n const docState = Y.encodeStateAsUpdate(ydoc);\n cw.postMessage({ type: \"ydoc-sync\", payload: Array.from(docState) }, \"*\");\n unackedServerUpdates.clear();\n if (pendingCheckTimer) {\n clearTimeout(pendingCheckTimer);\n pendingCheckTimer = null;\n }\n },\n pause() {\n paused = true;\n },\n resume() {\n paused = false;\n },\n destroy: () => {\n if (pendingCheckTimer) {\n clearTimeout(pendingCheckTimer);\n pendingCheckTimer = null;\n }\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n }\n setConnected(false);\n postToIframe(\"disconnect\");\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", wrappedOnMessage);\n if (undoManager) {\n undoManager.off(\"stack-item-popped\", onUndoPopped);\n undoManager.off(\"stack-cleared\", onStackCleared);\n undoManager.off(\"stack-item-added\", onStackItemAdded);\n tryRemoveIframeOriginTracking();\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":["awareness","IFRAME_ORIGIN","origin","encodeAwarenessUpdate","BASELINE_ORIGIN","Y","applyAwarenessUpdate"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import * as Y from "yjs";
2
2
  import { encodeAwarenessUpdate, applyAwarenessUpdate } from "y-protocols/awareness";
3
- const IFRAME_ORIGIN = {};
4
- const BASELINE_ORIGIN = {};
3
+ import { I as IFRAME_ORIGIN, B as BASELINE_ORIGIN } from "../origin-Doo2uLkM.js";
5
4
  var __defProp = Object.defineProperty;
6
5
  var __defProps = Object.defineProperties;
7
6
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
@@ -22,7 +21,7 @@ var __spreadValues = (a, b) => {
22
21
  };
23
22
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
23
  function createIframeBridgeServer(iframe, ydoc, awareness, options) {
25
- const { undoManager, debug = false } = options != null ? options : {};
24
+ const { undoManager, debug = false, pendingTimeoutMs, onPendingTimeout } = options != null ? options : {};
26
25
  const log = debug ? (...args) => console.log("[iframe-bridge server]", ...args) : () => void 0;
27
26
  function formatPayload(payload) {
28
27
  if (payload instanceof Uint8Array) {
@@ -47,6 +46,24 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
47
46
  const pendingUpdatesToIframe = [];
48
47
  let serverSeq = 0;
49
48
  const unackedServerUpdates = /* @__PURE__ */ new Map();
49
+ let pendingCheckTimer = null;
50
+ function schedulePendingCheck() {
51
+ if (pendingCheckTimer || !pendingTimeoutMs || !onPendingTimeout) return;
52
+ pendingCheckTimer = setTimeout(() => {
53
+ pendingCheckTimer = null;
54
+ const now = Date.now();
55
+ const unackedCount = unackedServerUpdates.size;
56
+ const pendingCount = pendingUpdatesToIframe.length;
57
+ if (unackedCount > 0 || pendingCount > 0) {
58
+ const oldest = unackedCount > 0 ? Math.min(...Array.from(unackedServerUpdates.values()).map((v) => v.sentAt)) : now;
59
+ onPendingTimeout({
60
+ pendingCount: unackedCount + pendingCount,
61
+ oldestMs: now - oldest
62
+ });
63
+ }
64
+ }, pendingTimeoutMs);
65
+ }
66
+ let iframeCapabilities = null;
50
67
  function tryAddIframeOriginTracking() {
51
68
  if (!undoManager) return;
52
69
  try {
@@ -89,7 +106,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
89
106
  const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
90
107
  cw.postMessage(message, "*");
91
108
  }
92
- unackedServerUpdates.set(seqNum, { update, isBaseline });
109
+ unackedServerUpdates.set(seqNum, { update, isBaseline, sentAt: Date.now() });
93
110
  }
94
111
  if (!wasForceFullSync) {
95
112
  for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {
@@ -99,6 +116,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
99
116
  }
100
117
  }
101
118
  }
119
+ postObjectToIframe({ type: "capabilities-request" });
102
120
  connectListeners.forEach((fn) => fn());
103
121
  } else {
104
122
  unackedServerUpdates.clear();
@@ -132,6 +150,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
132
150
  console.warn("[iframe-bridge server] queue full, forcing full sync on reconnect");
133
151
  }
134
152
  pendingUpdatesToIframe.push({ update, isBaseline });
153
+ schedulePendingCheck();
135
154
  return;
136
155
  }
137
156
  const seqNum = ++serverSeq;
@@ -140,7 +159,8 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
140
159
  const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
141
160
  cw.postMessage(message, "*");
142
161
  }
143
- unackedServerUpdates.set(seqNum, { update, isBaseline });
162
+ unackedServerUpdates.set(seqNum, { update, isBaseline, sentAt: Date.now() });
163
+ schedulePendingCheck();
144
164
  };
145
165
  function postUndoStateToIframe() {
146
166
  var _a, _b;
@@ -177,7 +197,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
177
197
  });
178
198
  };
179
199
  const onMessage = (event) => {
180
- var _a, _b;
200
+ var _a, _b, _c;
181
201
  if (event.source !== iframe.contentWindow) return;
182
202
  const { type: msgType, payload } = event.data;
183
203
  if (msgType === "ydoc-pending-updates") {
@@ -286,12 +306,35 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
286
306
  }
287
307
  } else if (msgType === "request-undo-state") {
288
308
  postUndoStateToIframe();
309
+ } else if (msgType === "consistency-check") {
310
+ logMessage("recv", "consistency-check", payload);
311
+ const providerSV = new Uint8Array(event.data.stateVector || []);
312
+ const serverSV = Y.encodeStateVector(ydoc);
313
+ const svMatch = providerSV.length === serverSV.length && providerSV.every((v, i) => v === serverSV[i]);
314
+ if (!svMatch) {
315
+ log("consistency mismatch detected, sending force-sync");
316
+ const cw = iframe.contentWindow;
317
+ if (cw) {
318
+ cw.postMessage({ type: "force-sync" }, "*");
319
+ }
320
+ }
321
+ } else if (msgType === "request-full-sync") {
322
+ logMessage("recv", "request-full-sync", payload);
323
+ const docState = Y.encodeStateAsUpdate(ydoc);
324
+ postObjectToIframe({ type: "ydoc-sync", payload: Array.from(docState), protocolVersion: 2 });
289
325
  } else if (msgType === "undo" && undoManager) {
290
326
  undoManager.undo();
291
327
  postUndoStateToIframe();
292
328
  } else if (msgType === "redo" && undoManager) {
293
329
  undoManager.redo();
294
330
  postUndoStateToIframe();
331
+ } else if (msgType === "capabilities-request") {
332
+ logMessage("recv", "capabilities-request", payload);
333
+ postObjectToIframe({ type: "capabilities-reply", capabilities: { consistency: true } });
334
+ } else if (msgType === "capabilities-reply") {
335
+ logMessage("recv", "capabilities-reply", payload);
336
+ iframeCapabilities = (_c = event.data.capabilities) != null ? _c : null;
337
+ log("[DEBUG] received iframe capabilities", iframeCapabilities);
295
338
  }
296
339
  };
297
340
  const onUndoPopped = () => {
@@ -303,9 +346,14 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
303
346
  const onStackItemAdded = () => {
304
347
  postUndoStateToIframe();
305
348
  };
349
+ let paused = false;
350
+ const wrappedOnMessage = (event) => {
351
+ if (paused) return;
352
+ onMessage(event);
353
+ };
306
354
  ydoc.on("update", onYdocUpdate);
307
355
  awareness.on("update", onAwarenessUpdate);
308
- window.addEventListener("message", onMessage);
356
+ window.addEventListener("message", wrappedOnMessage);
309
357
  if (undoManager) {
310
358
  undoManager.on("stack-item-popped", onUndoPopped);
311
359
  undoManager.on("stack-cleared", onStackCleared);
@@ -332,7 +380,29 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
332
380
  return () => disconnectListeners.delete(fn);
333
381
  }
334
382
  },
383
+ forceSyncToClient() {
384
+ if (!connected) return;
385
+ const cw = iframe.contentWindow;
386
+ if (!cw) return;
387
+ const docState = Y.encodeStateAsUpdate(ydoc);
388
+ cw.postMessage({ type: "ydoc-sync", payload: Array.from(docState) }, "*");
389
+ unackedServerUpdates.clear();
390
+ if (pendingCheckTimer) {
391
+ clearTimeout(pendingCheckTimer);
392
+ pendingCheckTimer = null;
393
+ }
394
+ },
395
+ pause() {
396
+ paused = true;
397
+ },
398
+ resume() {
399
+ paused = false;
400
+ },
335
401
  destroy: () => {
402
+ if (pendingCheckTimer) {
403
+ clearTimeout(pendingCheckTimer);
404
+ pendingCheckTimer = null;
405
+ }
336
406
  while (pendingUpdatesToIframe.length > 0) {
337
407
  const { update, isBaseline } = pendingUpdatesToIframe.shift();
338
408
  const cw = iframe.contentWindow;
@@ -344,7 +414,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
344
414
  postToIframe("disconnect");
345
415
  ydoc.off("update", onYdocUpdate);
346
416
  awareness.off("update", onAwarenessUpdate);
347
- window.removeEventListener("message", onMessage);
417
+ window.removeEventListener("message", wrappedOnMessage);
348
418
  if (undoManager) {
349
419
  undoManager.off("stack-item-popped", onUndoPopped);
350
420
  undoManager.off("stack-cleared", onStackCleared);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../../iframe-bridge/src/origin.ts","../../../iframe-bridge/src/server.ts"],"sourcesContent":["/**\n * iframe bridge 内部变更的 origin 标识。\n * 当 provider 端产生 ydoc-update 时,使用此 origin 标记,\n * 以便 server 端的 UndoManager 能正确追踪来自 iframe 的变更。\n */\nexport const IFRAME_ORIGIN: object = {};\n\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let forceFullSync = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingUpdatesToIframe: Array<{ update: Uint8Array; isBaseline: boolean }> = [];\n let serverSeq = 0;\n const unackedServerUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean }>();\n\n function tryAddIframeOriginTracking() {\n if (!undoManager) return;\n try {\n if (typeof (undoManager as any).addTrackedOrigin === \"function\") {\n (undoManager as any).addTrackedOrigin(IFRAME_ORIGIN);\n iframeOriginTracked = true;\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n function tryRemoveIframeOriginTracking() {\n if (!undoManager || !iframeOriginTracked) return;\n try {\n if (typeof (undoManager as any).removeTrackedOrigin === \"function\") {\n (undoManager as any).removeTrackedOrigin(IFRAME_ORIGIN);\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n tryAddIframeOriginTracking();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n const wasForceFullSync = forceFullSync;\n forceFullSync = false;\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n }\n if (!wasForceFullSync) {\n for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq }, \"*\");\n }\n }\n }\n connectListeners.forEach((fn) => fn());\n } else {\n unackedServerUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n const message = { type, payload: payload ? Array.from(payload) : [] };\n logMessage(\"send\", type, payload);\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n function postObjectToIframe(message: Record<string, unknown>) {\n const type = message.type as string;\n logMessage(\"send\", type, message);\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n logMessage(\"send\", \"ydoc-update\", update);\n if (!connected) {\n if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingUpdatesToIframe.length = 0;\n console.warn(\"[iframe-bridge server] queue full, forcing full sync on reconnect\");\n }\n pendingUpdatesToIframe.push({ update, isBaseline });\n return;\n }\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n };\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n logMessage(\"send\", \"undo-state\", state);\n cw.postMessage(state, \"*\");\n }\n }\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n logMessage(\"send\", \"awareness-update\", update);\n postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"ydoc-pending-updates\") {\n logMessage(\"recv\", \"ydoc-pending-updates\", payload);\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const arr = item.update ?? item;\n const isBaseline = item.isBaseline ?? false;\n const update = new Uint8Array(arr);\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n }\n }\n } else if (msgType === \"init\") {\n logMessage(\"recv\", \"init\", payload);\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n logMessage(\"send\", \"ydoc-sync\", { bytes: docState.length });\n postObjectToIframe({ type: \"ydoc-sync\", payload: Array.from(docState), protocolVersion: 2 });\n unackedServerUpdates.clear();\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID, protocolVersion: 2 };\n logMessage(\"send\", \"pong\", message);\n cw.postMessage(message, \"*\");\n }\n } else if (msgType === \"ydoc-update\") {\n logMessage(\"recv\", \"ydoc-update\", payload);\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n const inSeq = event.data.seq;\n if (inSeq != null) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update-ack\", seq: inSeq }, \"*\");\n }\n }\n } else if (msgType === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedServerUpdates.delete(ackSeq);\n }\n } else if (msgType === \"awareness-local-state\") {\n // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n destroy: () => {\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n }\n setConnected(false);\n postToIframe(\"disconnect\");\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.off(\"stack-item-popped\", onUndoPopped);\n undoManager.off(\"stack-cleared\", onStackCleared);\n undoManager.off(\"stack-item-added\", onStackItemAdded);\n tryRemoveIframeOriginTracking();\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sources":["../../../iframe-bridge/src/server.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n /** pending update 超时回调(ms),不传则不检测 */\n pendingTimeoutMs?: number;\n /** pending update 超时触发的回调 */\n onPendingTimeout?: (info: { pendingCount: number; oldestMs: number }) => void;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n forceSyncToClient: () => void;\n /** 暂停消息处理(模拟丢失) */\n pause: () => void;\n /** 恢复消息处理 */\n resume: () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false, pendingTimeoutMs, onPendingTimeout } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let forceFullSync = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingUpdatesToIframe: Array<{ update: Uint8Array; isBaseline: boolean }> = [];\n let serverSeq = 0;\n const unackedServerUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean; sentAt: number }>();\n let pendingCheckTimer: ReturnType<typeof setTimeout> | null = null;\n\n function schedulePendingCheck() {\n if (pendingCheckTimer || !pendingTimeoutMs || !onPendingTimeout) return;\n pendingCheckTimer = setTimeout(() => {\n pendingCheckTimer = null;\n const now = Date.now();\n const unackedCount = unackedServerUpdates.size;\n const pendingCount = pendingUpdatesToIframe.length;\n if (unackedCount > 0 || pendingCount > 0) {\n const oldest = unackedCount > 0\n ? Math.min(...Array.from(unackedServerUpdates.values()).map((v) => v.sentAt))\n : now;\n onPendingTimeout({\n pendingCount: unackedCount + pendingCount,\n oldestMs: now - oldest,\n });\n }\n }, pendingTimeoutMs);\n }\n\n // capabilities learned from the iframe (if any)\n let iframeCapabilities: Record<string, unknown> | null = null;\n\n function tryAddIframeOriginTracking() {\n if (!undoManager) return;\n try {\n if (typeof (undoManager as any).addTrackedOrigin === \"function\") {\n (undoManager as any).addTrackedOrigin(IFRAME_ORIGIN);\n iframeOriginTracked = true;\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n function tryRemoveIframeOriginTracking() {\n if (!undoManager || !iframeOriginTracked) return;\n try {\n if (typeof (undoManager as any).removeTrackedOrigin === \"function\") {\n (undoManager as any).removeTrackedOrigin(IFRAME_ORIGIN);\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n tryAddIframeOriginTracking();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n const wasForceFullSync = forceFullSync;\n forceFullSync = false;\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline, sentAt: Date.now() });\n }\n if (!wasForceFullSync) {\n for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq }, \"*\");\n }\n }\n }\n\n // send a capabilities request to the iframe so both sides can negotiate\n // older providers will ignore unknown messages, so this is backwards compatible\n postObjectToIframe({ type: \"capabilities-request\" });\n\n connectListeners.forEach((fn) => fn());\n } else {\n unackedServerUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n const message = { type, payload: payload ? Array.from(payload) : [] };\n logMessage(\"send\", type, payload);\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n function postObjectToIframe(message: Record<string, unknown>) {\n const type = message.type as string;\n logMessage(\"send\", type, message);\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n logMessage(\"send\", \"ydoc-update\", update);\n if (!connected) {\n if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingUpdatesToIframe.length = 0;\n console.warn(\"[iframe-bridge server] queue full, forcing full sync on reconnect\");\n }\n pendingUpdatesToIframe.push({ update, isBaseline });\n schedulePendingCheck();\n return;\n }\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline, sentAt: Date.now() });\n schedulePendingCheck();\n };\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n logMessage(\"send\", \"undo-state\", state);\n cw.postMessage(state, \"*\");\n }\n }\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n logMessage(\"send\", \"awareness-update\", update);\n postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"ydoc-pending-updates\") {\n logMessage(\"recv\", \"ydoc-pending-updates\", payload);\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const arr = item.update ?? item;\n const isBaseline = item.isBaseline ?? false;\n const update = new Uint8Array(arr);\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n }\n }\n } else if (msgType === \"init\") {\n logMessage(\"recv\", \"init\", payload);\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n logMessage(\"send\", \"ydoc-sync\", { bytes: docState.length });\n postObjectToIframe({ type: \"ydoc-sync\", payload: Array.from(docState), protocolVersion: 2 });\n unackedServerUpdates.clear();\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID, protocolVersion: 2 };\n logMessage(\"send\", \"pong\", message);\n cw.postMessage(message, \"*\");\n }\n } else if (msgType === \"ydoc-update\") {\n logMessage(\"recv\", \"ydoc-update\", payload);\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n const inSeq = event.data.seq;\n if (inSeq != null) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update-ack\", seq: inSeq }, \"*\");\n }\n }\n } else if (msgType === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedServerUpdates.delete(ackSeq);\n }\n } else if (msgType === \"awareness-local-state\") {\n // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"consistency-check\") {\n // provider 请求一致性检查:比较 state vector\n logMessage(\"recv\", \"consistency-check\", payload);\n const providerSV = new Uint8Array(event.data.stateVector || []);\n const serverSV = Y.encodeStateVector(ydoc);\n // 简单比较:如果 state vector 不同,发送 force-sync\n const svMatch = providerSV.length === serverSV.length &&\n providerSV.every((v: number, i: number) => v === serverSV[i]);\n if (!svMatch) {\n log(\"consistency mismatch detected, sending force-sync\");\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"force-sync\" }, \"*\");\n }\n }\n } else if (msgType === \"request-full-sync\") {\n // provider 请求完整同步\n logMessage(\"recv\", \"request-full-sync\", payload);\n const docState = Y.encodeStateAsUpdate(ydoc);\n postObjectToIframe({ type: \"ydoc-sync\", payload: Array.from(docState), protocolVersion: 2 });\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n } else if (msgType === \"capabilities-request\") {\n // provider 想协商能力集,回复 capabilities-reply\n logMessage(\"recv\", \"capabilities-request\", payload);\n postObjectToIframe({ type: \"capabilities-reply\", capabilities: { consistency: true } });\n } else if (msgType === \"capabilities-reply\") {\n // provider 回复能力集,保存以便后续使用(可扩展)\n logMessage(\"recv\", \"capabilities-reply\", payload);\n iframeCapabilities = event.data.capabilities ?? null;\n log(\"[DEBUG] received iframe capabilities\", iframeCapabilities);\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n let paused = false;\n const wrappedOnMessage = (event: MessageEvent) => {\n if (paused) return;\n onMessage(event);\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", wrappedOnMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n forceSyncToClient() {\n if (!connected) return;\n const cw = iframe.contentWindow;\n if (!cw) return;\n const docState = Y.encodeStateAsUpdate(ydoc);\n cw.postMessage({ type: \"ydoc-sync\", payload: Array.from(docState) }, \"*\");\n unackedServerUpdates.clear();\n if (pendingCheckTimer) {\n clearTimeout(pendingCheckTimer);\n pendingCheckTimer = null;\n }\n },\n pause() {\n paused = true;\n },\n resume() {\n paused = false;\n },\n destroy: () => {\n if (pendingCheckTimer) {\n clearTimeout(pendingCheckTimer);\n pendingCheckTimer = null;\n }\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n }\n setConnected(false);\n postToIframe(\"disconnect\");\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", wrappedOnMessage);\n if (undoManager) {\n undoManager.off(\"stack-item-popped\", onUndoPopped);\n undoManager.off(\"stack-cleared\", onStackCleared);\n undoManager.off(\"stack-item-added\", onStackItemAdded);\n tryRemoveIframeOriginTracking();\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -150,13 +150,35 @@ function parse$2(object, doc) {
150
150
  function serialize$1(map) {
151
151
  const cells = getMap(map, key$3);
152
152
  const cellsOrder = getArray(map, mxCellOrderKey);
153
+ if (!cells || !cellsOrder) {
154
+ return {
155
+ _attributes: {},
156
+ root: { [key$3]: [] }
157
+ };
158
+ }
153
159
  const bg = map.get(backgroundKey);
154
160
  const _attributes = {};
155
161
  if (bg) _attributes.background = bg;
162
+ const orderIds = cellsOrder.toArray();
163
+ const missingIds = [];
164
+ const filtered = [];
165
+ for (const id of orderIds) {
166
+ const cell = cells.get(id);
167
+ if (cell) {
168
+ filtered.push(cell);
169
+ } else {
170
+ missingIds.push(id);
171
+ }
172
+ }
173
+ if (missingIds.length) {
174
+ console.warn(
175
+ `[y-mxgraph] serialize: cellsOrder contains ids not present in mxCell map: ${missingIds.join(",")}`
176
+ );
177
+ }
156
178
  return {
157
179
  _attributes,
158
180
  root: {
159
- [key$3]: cellsOrder.toArray().map((id) => serialize$2(cells.get(id)))
181
+ [key$3]: filtered.map((cell) => serialize$2(cell))
160
182
  }
161
183
  };
162
184
  }
@@ -263,12 +285,12 @@ export {
263
285
  key$3 as e,
264
286
  parse$1 as f,
265
287
  getMap as g,
266
- serializer$1 as h,
288
+ serialize as h,
267
289
  key$2 as k,
268
290
  mxCellOrderKey as m,
269
291
  parse$4 as p,
270
- serialize as s,
292
+ serializer$1 as s,
271
293
  xml2ydoc as x,
272
294
  ydoc2xml as y
273
295
  };
274
- //# sourceMappingURL=index-BhW4J2Zt.js.map
296
+ //# sourceMappingURL=index-CN7tFgEG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-CN7tFgEG.js","sources":["../src/helper/xml.ts","../src/helper/yjs.ts","../src/models/mxCell.ts","../src/models/mxGraphModel.ts","../src/models/diagram.ts","../src/models/mxfile.ts","../src/transform/index.ts"],"sourcesContent":["import { xml2js, js2xml, type ElementCompact } from \"xml-js\";\n\nfunction deepProcess(node: unknown): void {\n if (node == null) return;\n\n if (Array.isArray(node)) {\n for (const item of node) {\n deepProcess(item);\n }\n return;\n }\n\n if (typeof node !== \"object\") return;\n\n const obj = node as Record<string, unknown>;\n const keys = Object.keys(obj);\n for (const key of keys) {\n if (key === \"_attributes\") continue;\n\n let value = obj[key];\n const keyLower = key.toLowerCase();\n\n if (\n (keyLower === \"diagram\" || keyLower === \"mxcell\") &&\n value !== undefined &&\n !Array.isArray(value)\n ) {\n obj[key] = [value];\n value = obj[key];\n }\n\n if (Array.isArray(value)) {\n for (const v of value) deepProcess(v);\n } else if (value && typeof value === \"object\") {\n deepProcess(value);\n }\n }\n}\n\nexport function parse(xml: string) {\n const result = xml2js(xml, { compact: true }) as Record<string, unknown>;\n deepProcess(result);\n return result;\n}\n\nexport function serializer(obj: ElementCompact, spaces = 2) {\n return js2xml(obj, {\n compact: true,\n spaces,\n });\n}\n","import * as Y from \"yjs\";\n\n/**\n * 从 Y.Map 中安全获取子 Map(格式固定,转型安全)\n */\nexport function getMap<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Map<T> | undefined {\n return parent.get(key) as Y.Map<T> | undefined;\n}\n\n/**\n * 从 Y.Map 中安全获取子 Array(格式固定,转型安全)\n */\nexport function getArray<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Array<T> | undefined {\n return parent.get(key) as Y.Array<T> | undefined;\n}\n","import * as Y from \"yjs\";\nimport { xml2js, js2xml } from \"xml-js\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxCell\";\n\nconst mxGeometryKey = \"mxGeometry\";\nconst mxGeometryAttributeKey = \"geometry\";\n\nexport interface MxCellModel extends ElementCompact {\n [mxGeometryKey]?: ElementCompact;\n}\n\nexport function parse(object: MxCellModel): Y.XmlElement {\n const xmlElement = new Y.XmlElement(\"mxCell\");\n\n for (const attribute of Object.keys(object._attributes || {})) {\n xmlElement.setAttribute(\n attribute,\n `${object._attributes?.[attribute] || \"\"}`,\n );\n }\n\n if (object[mxGeometryKey]) {\n const geometry = object[mxGeometryKey];\n const geometryString = js2xml(\n { [mxGeometryKey]: geometry },\n {\n compact: true,\n },\n );\n xmlElement.setAttribute(mxGeometryAttributeKey, geometryString);\n delete object[mxGeometryKey];\n }\n\n return xmlElement;\n}\n\nexport function serialize(xmlElement: Y.XmlElement) {\n const rawAttributes = {\n ...xmlElement.getAttributes(),\n };\n\n // 提取 mxGeometry(不需要转义,它本身就是 XML 字符串)\n let mxGeometry: ElementCompact | null = null;\n let mxGeometryString: string | undefined;\n\n if (mxGeometryAttributeKey in rawAttributes) {\n mxGeometryString = rawAttributes[mxGeometryAttributeKey];\n delete rawAttributes[mxGeometryAttributeKey];\n }\n\n // 转义其他属性值中的特殊字符\n const attributes: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawAttributes)) {\n if (typeof value === \"string\") {\n attributes[key] = value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n } else if (value != null) {\n attributes[key] = String(value);\n }\n }\n\n // 解析 mxGeometry\n if (mxGeometryString) {\n try {\n const parsed = xml2js(mxGeometryString, { compact: true }) as Record<\n string,\n ElementCompact\n >;\n mxGeometry = parsed[mxGeometryKey] ?? null;\n if (mxGeometry && mxGeometry._attributes) {\n mxGeometry._attributes[\"as\"] = \"geometry\";\n }\n } catch (e) {\n console.warn(\"[y-mxgraph] Failed to parse mxGeometry:\", e);\n }\n }\n\n const obj: Record<string, unknown> = {\n _attributes: attributes,\n };\n\n if (mxGeometry) {\n obj[mxGeometryKey] = mxGeometry;\n }\n\n return obj;\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\n\nimport {\n key as mxCellKey,\n parse as parseMxCell,\n serialize as serializeMxCell,\n type MxCellModel,\n} from \"./mxCell\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxGraphModel\";\nexport const mxCellOrderKey = mxCellKey + \"Order\";\n/** 协同同步的 mxGraphModel 属性:仅 background */\nexport const backgroundKey = \"background\";\n\nexport interface MxGraphModel extends ElementCompact {\n _attributes?: Record<string, string>;\n root: {\n mxCell: MxCellModel[];\n };\n}\n\nexport type YMxGraphModel = Y.Map<unknown>;\n\nexport function parse(object: MxGraphModel, doc?: Y.Doc) {\n const mxCells = (object.root[mxCellKey] || []).map((cell: MxCellModel) => {\n return {\n value: parseMxCell(cell),\n id: (cell._attributes?.id || \"\") as string,\n };\n });\n\n const mxGraphElement = doc?.getMap(key) || new Y.Map();\n\n const cells = new Y.Map<Y.XmlElement>();\n const cellsOrder = new Y.Array<string>();\n\n mxCells.forEach((cell) => {\n cells.set(cell.id, cell.value);\n });\n\n cellsOrder.push(mxCells.map((cell) => cell.id));\n\n mxGraphElement.set(mxCellKey, cells);\n mxGraphElement.set(mxCellOrderKey, cellsOrder);\n\n const bg = object._attributes?.background;\n if (bg != null && bg !== \"\") {\n mxGraphElement.set(backgroundKey, String(bg));\n }\n\n return mxGraphElement as YMxGraphModel;\n}\n\nexport function serialize(map: YMxGraphModel) {\n const cells = getMap<Y.XmlElement>(map, mxCellKey);\n const cellsOrder = getArray<string>(map, mxCellOrderKey);\n\n if (!cells || !cellsOrder) {\n return {\n _attributes: {},\n root: { [mxCellKey]: [] },\n };\n }\n\n const bg = map.get(backgroundKey) as string | undefined;\n const _attributes: Record<string, string> = {};\n if (bg) _attributes.background = bg;\n const orderIds = cellsOrder.toArray();\n const missingIds: string[] = [];\n const filtered: Y.XmlElement[] = [];\n for (const id of orderIds) {\n const cell = cells.get(id);\n if (cell) {\n filtered.push(cell);\n } else {\n missingIds.push(id);\n }\n }\n if (missingIds.length) {\n console.warn(\n `[y-mxgraph] serialize: cellsOrder contains ids not present in mxCell map: ${missingIds.join(\",\")}`,\n );\n }\n return {\n _attributes,\n root: {\n [mxCellKey]: filtered.map((cell) => serializeMxCell(cell)),\n },\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap } from \"../helper/yjs\";\nimport {\n parse as parseMxGraphModel,\n serialize as serializeMxGraphModel,\n key as mxGraphModelKey,\n type MxGraphModel,\n type YMxGraphModel,\n} from \"./mxGraphModel\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"diagram\";\n\nexport interface Diagram extends ElementCompact {\n mxGraphModel: MxGraphModel;\n}\n\nexport type YDiagram = Y.Map<unknown>;\n\nexport function parse(object: Diagram): YDiagram {\n const yDiagramElement = new Y.Map();\n yDiagramElement.set(\"name\", `${object._attributes?.name || \"\"}`);\n yDiagramElement.set(\"id\", `${object._attributes?.id || \"\"}`);\n\n const mxGraphModel = parseMxGraphModel(object[mxGraphModelKey]);\n\n yDiagramElement.set(mxGraphModelKey, mxGraphModel);\n return yDiagramElement as YDiagram;\n}\n\nexport function serialize(yDiagram: YDiagram) {\n const mxGraphModel = getMap(yDiagram, mxGraphModelKey);\n\n return {\n _attributes: {\n name: yDiagram.get(\"name\") as string,\n id: yDiagram.get(\"id\") as string,\n },\n [mxGraphModelKey]: mxGraphModel\n ? serializeMxGraphModel(mxGraphModel)\n : undefined,\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\nimport {\n parse as parseDiagram,\n key as diagramKey,\n serialize as serializeDiagram,\n} from \"./diagram\";\nimport type { Diagram, YDiagram } from \"./diagram\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxfile\";\nexport const diagramOrderKey = diagramKey + \"Order\";\n\nexport type YMxFile = Y.Map<unknown>;\n\nexport interface MxFile extends ElementCompact {\n diagram: Diagram[];\n}\n\nexport function parse(object: MxFile, doc: Y.Doc) {\n const mxfile = doc.getMap(key);\n mxfile.set(\"pages\", (object._attributes?.pages || \"1\") + \"\");\n\n const diagramList = object.diagram.map((diagram) => ({\n value: parseDiagram(diagram),\n id: (diagram._attributes?.id || \"\") as string,\n }));\n const diagramMap = new Y.Map<YDiagram>();\n const diagramOrder = new Y.Array<string>();\n diagramList.forEach((diagram) => {\n diagramMap.set(diagram.id, diagram.value);\n });\n diagramOrder.push(diagramList.map((diagram) => diagram.id));\n\n mxfile.set(diagramKey, diagramMap);\n mxfile.set(diagramOrderKey, diagramOrder);\n return mxfile;\n}\n\nexport function serializer(yMxFile: YMxFile): ElementCompact {\n const diagrams = getMap<YDiagram>(yMxFile, diagramKey);\n const diagramOrder = getArray<string>(yMxFile, diagramOrderKey);\n\n const orderIds = diagramOrder ? diagramOrder.toArray() : [];\n // 如果 diagramOrder 为空但 diagram map 不为空,使用 diagram map 中的所有 ID\n const ids =\n orderIds.length > 0\n ? orderIds\n : diagrams\n ? Array.from(diagrams.keys())\n : [];\n\n const obj: Record<string, unknown> = {\n _attributes: {\n pages: (yMxFile.get(\"pages\") as string) || \"1\",\n },\n [diagramKey]: ids\n .map((id) => diagrams!.get(id) as YDiagram)\n .filter((d): d is YDiagram => !!d)\n .map((diagramElement) => serializeDiagram(diagramElement)),\n };\n\n return obj as ElementCompact;\n}\n","import * as Y from \"yjs\";\nimport { parse, serializer } from \"../helper/xml\";\nimport {\n parse as parseMxFile,\n key as mxfileKey,\n serializer as serializerMxFile,\n type YMxFile,\n} from \"../models/mxfile\";\nimport {\n parse as parseMxGraphModel,\n key as mxGraphModelKey,\n serialize as serializerMxGraphModel,\n type YMxGraphModel,\n} from \"../models/mxGraphModel\";\n\nexport function xml2ydoc(xml: string, doc: Y.Doc): Y.Doc {\n const object = parse(xml);\n\n const mxfile = (object as Record<string, unknown>).mxfile;\n const mxGraphModel = (object as Record<string, unknown>).mxGraphModel;\n if (mxfile) {\n doc.transact(() => {\n parseMxFile(mxfile as import(\"../models/mxfile\").MxFile, doc);\n });\n } else if (mxGraphModel) {\n doc.transact(() => {\n parseMxGraphModel(\n mxGraphModel as import(\"../models/mxGraphModel\").MxGraphModel,\n doc,\n );\n });\n } else {\n throw new Error(\"不支持的文件格式\");\n }\n\n return doc;\n}\n\nexport function ydoc2xml(doc: Y.Doc, spaces = 0): string {\n if (doc.share.has(mxfileKey)) {\n return serializer(\n {\n [mxfileKey]: serializerMxFile(doc.getMap(mxfileKey)),\n },\n spaces,\n );\n }\n if (doc.share.has(mxGraphModelKey)) {\n return serializer(\n {\n [mxGraphModelKey]: serializerMxGraphModel(doc.getMap(mxGraphModelKey)),\n },\n spaces,\n );\n }\n\n return \"\";\n}\n"],"names":["key","parse","serializer","serialize","mxCellKey","_a","parseMxCell","serializeMxCell","parseMxGraphModel","mxGraphModelKey","serializeMxGraphModel","diagramKey","parseDiagram","serializeDiagram","parseMxFile","mxfileKey","serializerMxFile","serializerMxGraphModel"],"mappings":";;AAEA,SAAS,YAAY,MAAqB;AACxC,MAAI,QAAQ,KAAM;AAElB,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,eAAW,QAAQ,MAAM;AACvB,kBAAY,IAAI;AAAA,IAClB;AACA;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,SAAU;AAE9B,QAAM,MAAM;AACZ,QAAM,OAAO,OAAO,KAAK,GAAG;AAC5B,aAAWA,QAAO,MAAM;AACtB,QAAIA,SAAQ,cAAe;AAE3B,QAAI,QAAQ,IAAIA,IAAG;AACnB,UAAM,WAAWA,KAAI,YAAA;AAErB,SACG,aAAa,aAAa,aAAa,aACxC,UAAU,UACV,CAAC,MAAM,QAAQ,KAAK,GACpB;AACA,UAAIA,IAAG,IAAI,CAAC,KAAK;AACjB,cAAQ,IAAIA,IAAG;AAAA,IACjB;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,iBAAW,KAAK,MAAO,aAAY,CAAC;AAAA,IACtC,WAAW,SAAS,OAAO,UAAU,UAAU;AAC7C,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF;AACF;AAEO,SAASC,QAAM,KAAa;AACjC,QAAM,SAAS,OAAO,KAAK,EAAE,SAAS,MAAM;AAC5C,cAAY,MAAM;AAClB,SAAO;AACT;AAEO,SAASC,aAAW,KAAqB,SAAS,GAAG;AAC1D,SAAO,OAAO,KAAK;AAAA,IACjB,SAAS;AAAA,IACT;AAAA,EAAA,CACD;AACH;AC7CO,SAAS,OACd,QACAF,MACsB;AACtB,SAAO,OAAO,IAAIA,IAAG;AACvB;AAKO,SAAS,SACd,QACAA,MACwB;AACxB,SAAO,OAAO,IAAIA,IAAG;AACvB;;;;;;;;;;;;;;;;;AChBO,MAAMA,QAAM;AAEnB,MAAM,gBAAgB;AACtB,MAAM,yBAAyB;AAMxB,SAASC,QAAM,QAAmC;AAbzD,MAAA;AAcE,QAAM,aAAa,IAAI,EAAE,WAAW,QAAQ;AAE5C,aAAW,aAAa,OAAO,KAAK,OAAO,eAAe,CAAA,CAAE,GAAG;AAC7D,eAAW;AAAA,MACT;AAAA,MACA,KAAG,KAAA,OAAO,gBAAP,OAAA,SAAA,GAAqB,eAAc,EAAE;AAAA,IAAA;AAAA,EAE5C;AAEA,MAAI,OAAO,aAAa,GAAG;AACzB,UAAM,WAAW,OAAO,aAAa;AACrC,UAAM,iBAAiB;AAAA,MACrB,EAAE,CAAC,aAAa,GAAG,SAAA;AAAA,MACnB;AAAA,QACE,SAAS;AAAA,MAAA;AAAA,IACX;AAEF,eAAW,aAAa,wBAAwB,cAAc;AAC9D,WAAO,OAAO,aAAa;AAAA,EAC7B;AAEA,SAAO;AACT;AAEO,SAASE,YAAU,YAA0B;AAtCpD,MAAA;AAuCE,QAAM,gBAAgB,eAAA,CAAA,GACjB,WAAW,cAAA,CAAc;AAI9B,MAAI,aAAoC;AACxC,MAAI;AAEJ,MAAI,0BAA0B,eAAe;AAC3C,uBAAmB,cAAc,sBAAsB;AACvD,WAAO,cAAc,sBAAsB;AAAA,EAC7C;AAGA,QAAM,aAAqC,CAAA;AAC3C,aAAW,CAACH,MAAK,KAAK,KAAK,OAAO,QAAQ,aAAa,GAAG;AACxD,QAAI,OAAO,UAAU,UAAU;AAC7B,iBAAWA,IAAG,IAAI,MACf,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAAA,IAC3B,WAAW,SAAS,MAAM;AACxB,iBAAWA,IAAG,IAAI,OAAO,KAAK;AAAA,IAChC;AAAA,EACF;AAGA,MAAI,kBAAkB;AACpB,QAAI;AACF,YAAM,SAAS,OAAO,kBAAkB,EAAE,SAAS,MAAM;AAIzD,oBAAa,KAAA,OAAO,aAAa,MAApB,OAAA,KAAyB;AACtC,UAAI,cAAc,WAAW,aAAa;AACxC,mBAAW,YAAY,IAAI,IAAI;AAAA,MACjC;AAAA,IACF,SAAS,GAAG;AACV,cAAQ,KAAK,2CAA2C,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,QAAM,MAA+B;AAAA,IACnC,aAAa;AAAA,EAAA;AAGf,MAAI,YAAY;AACd,QAAI,aAAa,IAAI;AAAA,EACvB;AAEA,SAAO;AACT;ACjFO,MAAMA,QAAM;AACZ,MAAM,iBAAiBI,QAAY;AAEnC,MAAM,gBAAgB;AAWtB,SAASH,QAAM,QAAsB,KAAa;AAzBzD,MAAA;AA0BE,QAAM,WAAW,OAAO,KAAKG,KAAS,KAAK,CAAA,GAAI,IAAI,CAAC,SAAsB;AA1B5E,QAAAC;AA2BI,WAAO;AAAA,MACL,OAAOC,QAAY,IAAI;AAAA,MACvB,MAAKD,MAAA,KAAK,gBAAL,OAAA,SAAAA,IAAkB,OAAM;AAAA,IAAA;AAAA,EAEjC,CAAC;AAED,QAAM,kBAAiB,OAAA,OAAA,SAAA,IAAK,OAAOL,KAAA,MAAQ,IAAI,EAAE,IAAA;AAEjD,QAAM,QAAQ,IAAI,EAAE,IAAA;AACpB,QAAM,aAAa,IAAI,EAAE,MAAA;AAEzB,UAAQ,QAAQ,CAAC,SAAS;AACxB,UAAM,IAAI,KAAK,IAAI,KAAK,KAAK;AAAA,EAC/B,CAAC;AAED,aAAW,KAAK,QAAQ,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC;AAE9C,iBAAe,IAAII,OAAW,KAAK;AACnC,iBAAe,IAAI,gBAAgB,UAAU;AAE7C,QAAM,MAAK,KAAA,OAAO,gBAAP,OAAA,SAAA,GAAoB;AAC/B,MAAI,MAAM,QAAQ,OAAO,IAAI;AAC3B,mBAAe,IAAI,eAAe,OAAO,EAAE,CAAC;AAAA,EAC9C;AAEA,SAAO;AACT;AAEO,SAASD,YAAU,KAAoB;AAC5C,QAAM,QAAQ,OAAqB,KAAKC,KAAS;AACjD,QAAM,aAAa,SAAiB,KAAK,cAAc;AAEvD,MAAI,CAAC,SAAS,CAAC,YAAY;AACzB,WAAO;AAAA,MACL,aAAa,CAAA;AAAA,MACb,MAAM,EAAE,CAACA,KAAS,GAAG,CAAA,EAAC;AAAA,IAAE;AAAA,EAE5B;AAEA,QAAM,KAAK,IAAI,IAAI,aAAa;AAChC,QAAM,cAAsC,CAAA;AAC5C,MAAI,gBAAgB,aAAa;AACjC,QAAM,WAAW,WAAW,QAAA;AAC5B,QAAM,aAAuB,CAAA;AAC7B,QAAM,WAA2B,CAAA;AACjC,aAAW,MAAM,UAAU;AACzB,UAAM,OAAO,MAAM,IAAI,EAAE;AACzB,QAAI,MAAM;AACR,eAAS,KAAK,IAAI;AAAA,IACpB,OAAO;AACL,iBAAW,KAAK,EAAE;AAAA,IACpB;AAAA,EACF;AACA,MAAI,WAAW,QAAQ;AACrB,YAAQ;AAAA,MACN,6EAA6E,WAAW,KAAK,GAAG,CAAC;AAAA,IAAA;AAAA,EAErG;AACA,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,MACJ,CAACA,KAAS,GAAG,SAAS,IAAI,CAAC,SAASG,YAAgB,IAAI,CAAC;AAAA,IAAA;AAAA,EAC3D;AAEJ;AChFO,MAAMP,QAAM;AAQZ,SAASC,QAAM,QAA2B;AAnBjD,MAAA,IAAA;AAoBE,QAAM,kBAAkB,IAAI,EAAE,IAAA;AAC9B,kBAAgB,IAAI,QAAQ,KAAG,KAAA,OAAO,gBAAP,OAAA,SAAA,GAAoB,SAAQ,EAAE,EAAE;AAC/D,kBAAgB,IAAI,MAAM,KAAG,KAAA,OAAO,gBAAP,OAAA,SAAA,GAAoB,OAAM,EAAE,EAAE;AAE3D,QAAM,eAAeO,QAAkB,OAAOC,KAAe,CAAC;AAE9D,kBAAgB,IAAIA,OAAiB,YAAY;AACjD,SAAO;AACT;AAEO,SAAS,UAAU,UAAoB;AAC5C,QAAM,eAAe,OAAO,UAAUA,KAAe;AAErD,SAAO;AAAA,IACL,aAAa;AAAA,MACX,MAAM,SAAS,IAAI,MAAM;AAAA,MACzB,IAAI,SAAS,IAAI,IAAI;AAAA,IAAA;AAAA,IAEvB,CAACA,KAAe,GAAG,eACfC,YAAsB,YAAY,IAClC;AAAA,EAAA;AAER;AChCO,MAAM,MAAM;AACZ,MAAM,kBAAkBC,QAAa;AAQrC,SAAS,MAAM,QAAgB,KAAY;AAnBlD,MAAA;AAoBE,QAAM,SAAS,IAAI,OAAO,GAAG;AAC7B,SAAO,IAAI,YAAU,KAAA,OAAO,gBAAP,OAAA,SAAA,GAAoB,UAAS,OAAO,EAAE;AAE3D,QAAM,cAAc,OAAO,QAAQ,IAAI,CAAC,YAAS;AAvBnD,QAAAN;AAuBuD,WAAA;AAAA,MACnD,OAAOO,QAAa,OAAO;AAAA,MAC3B,MAAKP,MAAA,QAAQ,gBAAR,OAAA,SAAAA,IAAqB,OAAM;AAAA,IAAA;AAAA,EAClC,CAAE;AACF,QAAM,aAAa,IAAI,EAAE,IAAA;AACzB,QAAM,eAAe,IAAI,EAAE,MAAA;AAC3B,cAAY,QAAQ,CAAC,YAAY;AAC/B,eAAW,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAAA,EAC1C,CAAC;AACD,eAAa,KAAK,YAAY,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AAE1D,SAAO,IAAIM,OAAY,UAAU;AACjC,SAAO,IAAI,iBAAiB,YAAY;AACxC,SAAO;AACT;AAEO,SAAS,WAAW,SAAkC;AAC3D,QAAM,WAAW,OAAiB,SAASA,KAAU;AACrD,QAAM,eAAe,SAAiB,SAAS,eAAe;AAE9D,QAAM,WAAW,eAAe,aAAa,QAAA,IAAY,CAAA;AAEzD,QAAM,MACJ,SAAS,SAAS,IACd,WACA,WACE,MAAM,KAAK,SAAS,KAAA,CAAM,IAC1B,CAAA;AAER,QAAM,MAA+B;AAAA,IACnC,aAAa;AAAA,MACX,OAAQ,QAAQ,IAAI,OAAO,KAAgB;AAAA,IAAA;AAAA,IAE7C,CAACA,KAAU,GAAG,IACX,IAAI,CAAC,OAAO,SAAU,IAAI,EAAE,CAAa,EACzC,OAAO,CAAC,MAAqB,CAAC,CAAC,CAAC,EAChC,IAAI,CAAC,mBAAmBE,UAAiB,cAAc,CAAC;AAAA,EAAA;AAG7D,SAAO;AACT;AChDO,SAAS,SAAS,KAAa,KAAmB;AACvD,QAAM,SAASZ,QAAM,GAAG;AAExB,QAAM,SAAU,OAAmC;AACnD,QAAM,eAAgB,OAAmC;AACzD,MAAI,QAAQ;AACV,QAAI,SAAS,MAAM;AACjBa,YAAY,QAA6C,GAAG;AAAA,IAC9D,CAAC;AAAA,EACH,WAAW,cAAc;AACvB,QAAI,SAAS,MAAM;AACjBN;AAAAA,QACE;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ,CAAC;AAAA,EACH,OAAO;AACL,UAAM,IAAI,MAAM,UAAU;AAAA,EAC5B;AAEA,SAAO;AACT;AAEO,SAAS,SAAS,KAAY,SAAS,GAAW;AACvD,MAAI,IAAI,MAAM,IAAIO,GAAS,GAAG;AAC5B,WAAOb;AAAAA,MACL;AAAA,QACE,CAACa,GAAS,GAAGC,WAAiB,IAAI,OAAOD,GAAS,CAAC;AAAA,MAAA;AAAA,MAErD;AAAA,IAAA;AAAA,EAEJ;AACA,MAAI,IAAI,MAAM,IAAIN,KAAe,GAAG;AAClC,WAAOP;AAAAA,MACL;AAAA,QACE,CAACO,KAAe,GAAGQ,YAAuB,IAAI,OAAOR,KAAe,CAAC;AAAA,MAAA;AAAA,MAEvE;AAAA,IAAA;AAAA,EAEJ;AAEA,SAAO;AACT;"}
@@ -168,13 +168,35 @@ function parse$2(object, doc) {
168
168
  function serialize$1(map) {
169
169
  const cells = getMap(map, key$3);
170
170
  const cellsOrder = getArray(map, mxCellOrderKey);
171
+ if (!cells || !cellsOrder) {
172
+ return {
173
+ _attributes: {},
174
+ root: { [key$3]: [] }
175
+ };
176
+ }
171
177
  const bg = map.get(backgroundKey);
172
178
  const _attributes = {};
173
179
  if (bg) _attributes.background = bg;
180
+ const orderIds = cellsOrder.toArray();
181
+ const missingIds = [];
182
+ const filtered = [];
183
+ for (const id of orderIds) {
184
+ const cell = cells.get(id);
185
+ if (cell) {
186
+ filtered.push(cell);
187
+ } else {
188
+ missingIds.push(id);
189
+ }
190
+ }
191
+ if (missingIds.length) {
192
+ console.warn(
193
+ `[y-mxgraph] serialize: cellsOrder contains ids not present in mxCell map: ${missingIds.join(",")}`
194
+ );
195
+ }
174
196
  return {
175
197
  _attributes,
176
198
  root: {
177
- [key$3]: cellsOrder.toArray().map((id) => serialize$2(cells.get(id)))
199
+ [key$3]: filtered.map((cell) => serialize$2(cell))
178
200
  }
179
201
  };
180
202
  }
@@ -287,4 +309,4 @@ exports.serialize = serialize;
287
309
  exports.serializer = serializer$1;
288
310
  exports.xml2ydoc = xml2ydoc;
289
311
  exports.ydoc2xml = ydoc2xml;
290
- //# sourceMappingURL=index-D3Hk2QcW.cjs.map
312
+ //# sourceMappingURL=index-CqD1uLD3.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-CqD1uLD3.cjs","sources":["../src/helper/xml.ts","../src/helper/yjs.ts","../src/models/mxCell.ts","../src/models/mxGraphModel.ts","../src/models/diagram.ts","../src/models/mxfile.ts","../src/transform/index.ts"],"sourcesContent":["import { xml2js, js2xml, type ElementCompact } from \"xml-js\";\n\nfunction deepProcess(node: unknown): void {\n if (node == null) return;\n\n if (Array.isArray(node)) {\n for (const item of node) {\n deepProcess(item);\n }\n return;\n }\n\n if (typeof node !== \"object\") return;\n\n const obj = node as Record<string, unknown>;\n const keys = Object.keys(obj);\n for (const key of keys) {\n if (key === \"_attributes\") continue;\n\n let value = obj[key];\n const keyLower = key.toLowerCase();\n\n if (\n (keyLower === \"diagram\" || keyLower === \"mxcell\") &&\n value !== undefined &&\n !Array.isArray(value)\n ) {\n obj[key] = [value];\n value = obj[key];\n }\n\n if (Array.isArray(value)) {\n for (const v of value) deepProcess(v);\n } else if (value && typeof value === \"object\") {\n deepProcess(value);\n }\n }\n}\n\nexport function parse(xml: string) {\n const result = xml2js(xml, { compact: true }) as Record<string, unknown>;\n deepProcess(result);\n return result;\n}\n\nexport function serializer(obj: ElementCompact, spaces = 2) {\n return js2xml(obj, {\n compact: true,\n spaces,\n });\n}\n","import * as Y from \"yjs\";\n\n/**\n * 从 Y.Map 中安全获取子 Map(格式固定,转型安全)\n */\nexport function getMap<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Map<T> | undefined {\n return parent.get(key) as Y.Map<T> | undefined;\n}\n\n/**\n * 从 Y.Map 中安全获取子 Array(格式固定,转型安全)\n */\nexport function getArray<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Array<T> | undefined {\n return parent.get(key) as Y.Array<T> | undefined;\n}\n","import * as Y from \"yjs\";\nimport { xml2js, js2xml } from \"xml-js\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxCell\";\n\nconst mxGeometryKey = \"mxGeometry\";\nconst mxGeometryAttributeKey = \"geometry\";\n\nexport interface MxCellModel extends ElementCompact {\n [mxGeometryKey]?: ElementCompact;\n}\n\nexport function parse(object: MxCellModel): Y.XmlElement {\n const xmlElement = new Y.XmlElement(\"mxCell\");\n\n for (const attribute of Object.keys(object._attributes || {})) {\n xmlElement.setAttribute(\n attribute,\n `${object._attributes?.[attribute] || \"\"}`,\n );\n }\n\n if (object[mxGeometryKey]) {\n const geometry = object[mxGeometryKey];\n const geometryString = js2xml(\n { [mxGeometryKey]: geometry },\n {\n compact: true,\n },\n );\n xmlElement.setAttribute(mxGeometryAttributeKey, geometryString);\n delete object[mxGeometryKey];\n }\n\n return xmlElement;\n}\n\nexport function serialize(xmlElement: Y.XmlElement) {\n const rawAttributes = {\n ...xmlElement.getAttributes(),\n };\n\n // 提取 mxGeometry(不需要转义,它本身就是 XML 字符串)\n let mxGeometry: ElementCompact | null = null;\n let mxGeometryString: string | undefined;\n\n if (mxGeometryAttributeKey in rawAttributes) {\n mxGeometryString = rawAttributes[mxGeometryAttributeKey];\n delete rawAttributes[mxGeometryAttributeKey];\n }\n\n // 转义其他属性值中的特殊字符\n const attributes: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawAttributes)) {\n if (typeof value === \"string\") {\n attributes[key] = value\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n } else if (value != null) {\n attributes[key] = String(value);\n }\n }\n\n // 解析 mxGeometry\n if (mxGeometryString) {\n try {\n const parsed = xml2js(mxGeometryString, { compact: true }) as Record<\n string,\n ElementCompact\n >;\n mxGeometry = parsed[mxGeometryKey] ?? null;\n if (mxGeometry && mxGeometry._attributes) {\n mxGeometry._attributes[\"as\"] = \"geometry\";\n }\n } catch (e) {\n console.warn(\"[y-mxgraph] Failed to parse mxGeometry:\", e);\n }\n }\n\n const obj: Record<string, unknown> = {\n _attributes: attributes,\n };\n\n if (mxGeometry) {\n obj[mxGeometryKey] = mxGeometry;\n }\n\n return obj;\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\n\nimport {\n key as mxCellKey,\n parse as parseMxCell,\n serialize as serializeMxCell,\n type MxCellModel,\n} from \"./mxCell\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxGraphModel\";\nexport const mxCellOrderKey = mxCellKey + \"Order\";\n/** 协同同步的 mxGraphModel 属性:仅 background */\nexport const backgroundKey = \"background\";\n\nexport interface MxGraphModel extends ElementCompact {\n _attributes?: Record<string, string>;\n root: {\n mxCell: MxCellModel[];\n };\n}\n\nexport type YMxGraphModel = Y.Map<unknown>;\n\nexport function parse(object: MxGraphModel, doc?: Y.Doc) {\n const mxCells = (object.root[mxCellKey] || []).map((cell: MxCellModel) => {\n return {\n value: parseMxCell(cell),\n id: (cell._attributes?.id || \"\") as string,\n };\n });\n\n const mxGraphElement = doc?.getMap(key) || new Y.Map();\n\n const cells = new Y.Map<Y.XmlElement>();\n const cellsOrder = new Y.Array<string>();\n\n mxCells.forEach((cell) => {\n cells.set(cell.id, cell.value);\n });\n\n cellsOrder.push(mxCells.map((cell) => cell.id));\n\n mxGraphElement.set(mxCellKey, cells);\n mxGraphElement.set(mxCellOrderKey, cellsOrder);\n\n const bg = object._attributes?.background;\n if (bg != null && bg !== \"\") {\n mxGraphElement.set(backgroundKey, String(bg));\n }\n\n return mxGraphElement as YMxGraphModel;\n}\n\nexport function serialize(map: YMxGraphModel) {\n const cells = getMap<Y.XmlElement>(map, mxCellKey);\n const cellsOrder = getArray<string>(map, mxCellOrderKey);\n\n if (!cells || !cellsOrder) {\n return {\n _attributes: {},\n root: { [mxCellKey]: [] },\n };\n }\n\n const bg = map.get(backgroundKey) as string | undefined;\n const _attributes: Record<string, string> = {};\n if (bg) _attributes.background = bg;\n const orderIds = cellsOrder.toArray();\n const missingIds: string[] = [];\n const filtered: Y.XmlElement[] = [];\n for (const id of orderIds) {\n const cell = cells.get(id);\n if (cell) {\n filtered.push(cell);\n } else {\n missingIds.push(id);\n }\n }\n if (missingIds.length) {\n console.warn(\n `[y-mxgraph] serialize: cellsOrder contains ids not present in mxCell map: ${missingIds.join(\",\")}`,\n );\n }\n return {\n _attributes,\n root: {\n [mxCellKey]: filtered.map((cell) => serializeMxCell(cell)),\n },\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap } from \"../helper/yjs\";\nimport {\n parse as parseMxGraphModel,\n serialize as serializeMxGraphModel,\n key as mxGraphModelKey,\n type MxGraphModel,\n type YMxGraphModel,\n} from \"./mxGraphModel\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"diagram\";\n\nexport interface Diagram extends ElementCompact {\n mxGraphModel: MxGraphModel;\n}\n\nexport type YDiagram = Y.Map<unknown>;\n\nexport function parse(object: Diagram): YDiagram {\n const yDiagramElement = new Y.Map();\n yDiagramElement.set(\"name\", `${object._attributes?.name || \"\"}`);\n yDiagramElement.set(\"id\", `${object._attributes?.id || \"\"}`);\n\n const mxGraphModel = parseMxGraphModel(object[mxGraphModelKey]);\n\n yDiagramElement.set(mxGraphModelKey, mxGraphModel);\n return yDiagramElement as YDiagram;\n}\n\nexport function serialize(yDiagram: YDiagram) {\n const mxGraphModel = getMap(yDiagram, mxGraphModelKey);\n\n return {\n _attributes: {\n name: yDiagram.get(\"name\") as string,\n id: yDiagram.get(\"id\") as string,\n },\n [mxGraphModelKey]: mxGraphModel\n ? serializeMxGraphModel(mxGraphModel)\n : undefined,\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\nimport {\n parse as parseDiagram,\n key as diagramKey,\n serialize as serializeDiagram,\n} from \"./diagram\";\nimport type { Diagram, YDiagram } from \"./diagram\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxfile\";\nexport const diagramOrderKey = diagramKey + \"Order\";\n\nexport type YMxFile = Y.Map<unknown>;\n\nexport interface MxFile extends ElementCompact {\n diagram: Diagram[];\n}\n\nexport function parse(object: MxFile, doc: Y.Doc) {\n const mxfile = doc.getMap(key);\n mxfile.set(\"pages\", (object._attributes?.pages || \"1\") + \"\");\n\n const diagramList = object.diagram.map((diagram) => ({\n value: parseDiagram(diagram),\n id: (diagram._attributes?.id || \"\") as string,\n }));\n const diagramMap = new Y.Map<YDiagram>();\n const diagramOrder = new Y.Array<string>();\n diagramList.forEach((diagram) => {\n diagramMap.set(diagram.id, diagram.value);\n });\n diagramOrder.push(diagramList.map((diagram) => diagram.id));\n\n mxfile.set(diagramKey, diagramMap);\n mxfile.set(diagramOrderKey, diagramOrder);\n return mxfile;\n}\n\nexport function serializer(yMxFile: YMxFile): ElementCompact {\n const diagrams = getMap<YDiagram>(yMxFile, diagramKey);\n const diagramOrder = getArray<string>(yMxFile, diagramOrderKey);\n\n const orderIds = diagramOrder ? diagramOrder.toArray() : [];\n // 如果 diagramOrder 为空但 diagram map 不为空,使用 diagram map 中的所有 ID\n const ids =\n orderIds.length > 0\n ? orderIds\n : diagrams\n ? Array.from(diagrams.keys())\n : [];\n\n const obj: Record<string, unknown> = {\n _attributes: {\n pages: (yMxFile.get(\"pages\") as string) || \"1\",\n },\n [diagramKey]: ids\n .map((id) => diagrams!.get(id) as YDiagram)\n .filter((d): d is YDiagram => !!d)\n .map((diagramElement) => serializeDiagram(diagramElement)),\n };\n\n return obj as ElementCompact;\n}\n","import * as Y from \"yjs\";\nimport { parse, serializer } from \"../helper/xml\";\nimport {\n parse as parseMxFile,\n key as mxfileKey,\n serializer as serializerMxFile,\n type YMxFile,\n} from \"../models/mxfile\";\nimport {\n parse as parseMxGraphModel,\n key as mxGraphModelKey,\n serialize as serializerMxGraphModel,\n type YMxGraphModel,\n} from \"../models/mxGraphModel\";\n\nexport function xml2ydoc(xml: string, doc: Y.Doc): Y.Doc {\n const object = parse(xml);\n\n const mxfile = (object as Record<string, unknown>).mxfile;\n const mxGraphModel = (object as Record<string, unknown>).mxGraphModel;\n if (mxfile) {\n doc.transact(() => {\n parseMxFile(mxfile as import(\"../models/mxfile\").MxFile, doc);\n });\n } else if (mxGraphModel) {\n doc.transact(() => {\n parseMxGraphModel(\n mxGraphModel as import(\"../models/mxGraphModel\").MxGraphModel,\n doc,\n );\n });\n } else {\n throw new Error(\"不支持的文件格式\");\n }\n\n return doc;\n}\n\nexport function ydoc2xml(doc: Y.Doc, spaces = 0): string {\n if (doc.share.has(mxfileKey)) {\n return serializer(\n {\n [mxfileKey]: serializerMxFile(doc.getMap(mxfileKey)),\n },\n spaces,\n );\n }\n if (doc.share.has(mxGraphModelKey)) {\n return serializer(\n {\n [mxGraphModelKey]: serializerMxGraphModel(doc.getMap(mxGraphModelKey)),\n },\n spaces,\n );\n }\n\n return \"\";\n}\n"],"names":["key","parse","xml2js","serializer","js2xml","Y","serialize","mxCellKey","_a","parseMxCell","serializeMxCell","parseMxGraphModel","mxGraphModelKey","serializeMxGraphModel","diagramKey","parseDiagram","serializeDiagram","parseMxFile","mxfileKey","serializerMxFile","serializerMxGraphModel"],"mappings":"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"}
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { Binding } from "./binding";
2
- export type { BindDrawioFileOptions, InitialContentStrategy } from "./binding";
2
+ export type { BindDrawioFileOptions, InitialContentStrategy, DriftEvent, DriftHandler } from "./binding";
3
3
  export type { FilePatch } from "./binding/patch";
4
4
  export { xml2ydoc, ydoc2xml } from "./transform";
5
5
  export { LOCAL_ORIGIN } from "./helper/origin";