y-mxgraph 0.6.9 → 0.6.11

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.
package/README.md CHANGED
@@ -116,6 +116,8 @@ pnpm --filter @y-mxgraph/ws-demo dev # Start client on port 5174
116
116
  └─────────────┘
117
117
  ```
118
118
 
119
+ By default, the iframe provider keeps its local awareness state in sync with the parent bridge using `awareness.setLocalState()`. The parent page is treated as authoritative for user info, and the bridge forwards awareness updates between the iframe and the network provider.
120
+
119
121
  ```ts
120
122
  // Server (parent page)
121
123
  import * as Y from 'yjs';
package/README.zh-CN.md CHANGED
@@ -78,6 +78,8 @@ App.main((app) => {
78
78
  └─────────────┘
79
79
  ```
80
80
 
81
+ 默认情况下,iframe provider 会通过 `awareness.setLocalState()` 将本地 awareness state 同步到父容器桥接层。父页面的 awareness 信息被视为权威来源,桥接器会在 iframe 和网络 provider 之间转发 awareness 更新。
82
+
81
83
  ```ts
82
84
  // Server(父页面)
83
85
  import * as Y from 'yjs';
@@ -96,7 +96,7 @@ function remapClientIdInUpdate(update, fromId, toId) {
96
96
  return new Uint8Array(result);
97
97
  }
98
98
  function createIframeBridgeProvider(ydoc, awareness$1, options) {
99
- const { awarenessSyncMode = "binary" } = options != null ? options : {};
99
+ const { debug = false } = options != null ? options : {};
100
100
  let applyingParentUpdate = false;
101
101
  let serverClientId = null;
102
102
  let currentCleanup = null;
@@ -105,6 +105,25 @@ function createIframeBridgeProvider(ydoc, awareness$1, options) {
105
105
  let initRetryTimer = null;
106
106
  const connectListeners = /* @__PURE__ */ new Set();
107
107
  const disconnectListeners = /* @__PURE__ */ new Set();
108
+ const log = debug ? (...args) => console.debug("[iframe-bridge provider]", ...args) : () => void 0;
109
+ function formatPayload(payload) {
110
+ if (payload instanceof Uint8Array) {
111
+ return { bytes: payload.byteLength };
112
+ }
113
+ if (Array.isArray(payload) && payload.every((item) => typeof item === "number")) {
114
+ return { bytes: payload.length };
115
+ }
116
+ return payload;
117
+ }
118
+ function logMessage(direction, type, payload) {
119
+ if (!debug) return;
120
+ log(direction, type, formatPayload(payload));
121
+ }
122
+ function parentPostMessage(message) {
123
+ var _a;
124
+ logMessage("send", (_a = message.type) != null ? _a : "postMessage", message);
125
+ window.parent.postMessage(message, "*");
126
+ }
108
127
  function setConnected(value) {
109
128
  if (connected === value) return;
110
129
  connected = value;
@@ -118,20 +137,19 @@ function createIframeBridgeProvider(ydoc, awareness$1, options) {
118
137
  if (initRetryTimer) {
119
138
  clearInterval(initRetryTimer);
120
139
  }
121
- window.parent.postMessage({ type: "init" }, "*");
140
+ parentPostMessage({ type: "init" });
122
141
  initRetryTimer = setInterval(() => {
123
142
  if (!connected) {
124
- window.parent.postMessage({ type: "init" }, "*");
143
+ parentPostMessage({ type: "init" });
125
144
  }
126
145
  }, 1e3);
127
146
  }
128
147
  const onYdocUpdate = (update, origin) => {
129
148
  if (applyingParentUpdate) return;
130
149
  const isBaseline = origin === null || origin === void 0;
131
- window.parent.postMessage(
132
- { type: "ydoc-update", payload: Array.from(update), isBaseline },
133
- "*"
134
- );
150
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline };
151
+ logMessage("send", "ydoc-update", message);
152
+ window.parent.postMessage(message, "*");
135
153
  };
136
154
  const onAwarenessUpdate = ({
137
155
  added,
@@ -144,24 +162,16 @@ function createIframeBridgeProvider(ydoc, awareness$1, options) {
144
162
  const localClientId = awareness$1.clientID;
145
163
  const localChanged = changes.includes(localClientId);
146
164
  if (!localChanged) return;
147
- if (awarenessSyncMode === "local-state") {
148
- window.parent.postMessage(
149
- { type: "awareness-local-state", state: awareness$1.getLocalState() },
150
- "*"
151
- );
152
- } else {
153
- const update = awareness.encodeAwarenessUpdate(awareness$1, [localClientId]);
154
- const remapped = serverClientId != null ? remapClientIdInUpdate(update, localClientId, serverClientId) : update;
155
- window.parent.postMessage(
156
- { type: "awareness-update", payload: Array.from(remapped) },
157
- "*"
158
- );
159
- }
165
+ const state = awareness$1.getLocalState();
166
+ const message = { type: "awareness-local-state", state };
167
+ logMessage("send", "awareness-local-state", state);
168
+ window.parent.postMessage(message, "*");
160
169
  };
161
170
  const onMessage = (event) => {
162
171
  var _a, _b, _c;
163
172
  if (event.source !== window.parent) return;
164
173
  const { type, payload, serverClientId: receivedServerId } = event.data;
174
+ logMessage("recv", type, payload);
165
175
  if (type === "pong" && receivedServerId != null) {
166
176
  serverClientId = receivedServerId;
167
177
  return;
@@ -178,6 +188,7 @@ function createIframeBridgeProvider(ydoc, awareness$1, options) {
178
188
  }
179
189
  }
180
190
  } else if (type === "awareness-sync" || type === "awareness-update") {
191
+ logMessage("recv", type, payload);
181
192
  if (receivedServerId != null) {
182
193
  serverClientId = receivedServerId;
183
194
  }
@@ -221,10 +232,9 @@ function createIframeBridgeProvider(ydoc, awareness$1, options) {
221
232
  awareness$1.clientID,
222
233
  serverClientId
223
234
  );
224
- window.parent.postMessage(
225
- { type: "awareness-update", payload: Array.from(remapped) },
226
- "*"
227
- );
235
+ const message = { type: "awareness-update", payload: Array.from(remapped) };
236
+ logMessage("send", "awareness-update", message);
237
+ window.parent.postMessage(message, "*");
228
238
  }
229
239
  } else if (type === "undo-state" && currentMxLike) {
230
240
  const { undoStackSize, redoStackSize } = event.data;
@@ -1 +1 @@
1
- {"version":3,"file":"provider.cjs","sources":["../../../iframe-bridge/src/provider.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\n\ntype ListenerFn = (sender: unknown, evt?: unknown) => void;\n\nfunction createMxEventObject(name: string, props?: Record<string, unknown>) {\n const _props = props || {};\n return {\n name,\n getName: () => name,\n getProperty: (k: string) => _props[k],\n };\n}\n\ntype MxLike = Record<string, unknown> & {\n eventListeners: Array<string | ListenerFn>;\n history: unknown[];\n indexOfNextAdd: number;\n addListener(name: string, fn: ListenerFn): void;\n fireEvent(evt: unknown): void;\n canUndo(): boolean;\n canRedo(): boolean;\n undo(): void;\n redo(): void;\n undoableEditHappened(_edit: unknown): void;\n};\n\nexport interface DrawioEditor {\n undoManager?: {\n eventListeners?: unknown[];\n [key: string]: unknown;\n };\n undoListener?: (...args: unknown[]) => void;\n}\n\nexport interface DrawioFile {\n getUi(): { editor: DrawioEditor };\n}\n\nexport interface IframeBridgeProviderOptions {\n awarenessSyncMode?: \"binary\" | \"local-state\";\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n takeoverUndoManager: (file: DrawioFile) => () => void;\n destroy: () => void;\n}\n\nfunction readVarUint(data: Uint8Array, pos: number): [number, number] {\n let result = 0;\n let shift = 0;\n let byte: number;\n do {\n byte = data[pos++];\n result |= (byte & 0x7f) << shift;\n shift += 7;\n } while (byte >= 0x80);\n return [result >>> 0, pos];\n}\n\nfunction writeVarUint(value: number): number[] {\n const bytes: number[] = [];\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80);\n value >>>= 7;\n }\n bytes.push(value);\n return bytes;\n}\n\nfunction readVarString(data: Uint8Array, pos: number): [string, number] {\n const [len, pos2] = readVarUint(data, pos);\n const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));\n return [str, pos2 + len];\n}\n\nfunction writeVarString(str: string): number[] {\n const encoded = new TextEncoder().encode(str);\n return [...writeVarUint(encoded.length), ...encoded];\n}\n\nfunction remapClientIdInUpdate(\n update: Uint8Array,\n fromId: number,\n toId: number,\n): Uint8Array {\n const result: number[] = [];\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\n result.push(...writeVarUint(count));\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(update, pos);\n pos = pos3;\n const [clock, pos4] = readVarUint(update, pos);\n pos = pos4;\n const [state, pos5] = readVarString(update, pos);\n pos = pos5;\n\n const mappedId = clientID === fromId ? toId : clientID;\n result.push(...writeVarUint(mappedId));\n result.push(...writeVarUint(clock));\n result.push(...writeVarString(state));\n }\n\n return new Uint8Array(result);\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { awarenessSyncMode = \"binary\" } = options ?? {};\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n let connected = false;\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n window.parent.postMessage({ type: \"init\" }, \"*\");\n initRetryTimer = setInterval(() => {\n if (!connected) {\n window.parent.postMessage({ type: \"init\" }, \"*\");\n }\n }, 1000);\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update), isBaseline },\n \"*\",\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 (applyingParentUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 只发送本地 clientID 的更新给父页面\n // 其他 peers 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n if (awarenessSyncMode === \"local-state\") {\n window.parent.postMessage(\n { type: \"awareness-local-state\", state: awareness.getLocalState() },\n \"*\",\n );\n } else {\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, serverClientId)\n : update;\n\n window.parent.postMessage(\n { type: \"awareness-update\", payload: Array.from(remapped) },\n \"*\",\n );\n }\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n\n // 从 server 的 awareness state 中提取 user 信息并同步到本地\n // 避免 binding 生成随机用户信息后通过 remap 覆盖父页面的真实用户\n let localUserSynced = false;\n if (serverClientId != null) {\n const serverState = awareness.getStates().get(serverClientId);\n if (serverState) {\n const serverUserAccount = (\n serverState as { user?: { account?: unknown } }\n ).user?.account;\n const serverUserName = (serverState as { user?: { name?: unknown } })\n .user?.name;\n const serverUserColor = (\n serverState as { user?: { color?: unknown } }\n ).user?.color;\n if (serverUserAccount || serverUserName || serverUserColor) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = ((currentLocal as Record<string, unknown>)\n .user || {}) as Record<string, unknown>;\n const nextUser: Record<string, unknown> = { ...currentUser };\n if (serverUserAccount) {\n nextUser.account = serverUserAccount;\n }\n if (serverUserName) {\n nextUser.name = serverUserName;\n }\n if (serverUserColor) {\n nextUser.color = serverUserColor;\n }\n const userChanged =\n currentUser.account !== nextUser.account ||\n currentUser.name !== nextUser.name ||\n currentUser.color !== nextUser.color;\n if (userChanged) {\n awareness.setLocalState({\n ...currentLocal,\n user: nextUser,\n });\n localUserSynced = true;\n }\n }\n }\n }\n\n applyingParentUpdate = false;\n\n // 如果同步了本地 user info,需要发送一次更新给父页面\n // 恢复可能被随机值覆盖的 server user 信息\n if (localUserSynced && serverClientId != null) {\n const update = encodeAwarenessUpdate(awareness, [awareness.clientID]);\n const remapped = remapClientIdInUpdate(\n update,\n awareness.clientID,\n serverClientId,\n );\n window.parent.postMessage(\n { type: \"awareness-update\", payload: Array.from(remapped) },\n \"*\",\n );\n }\n } else if (type === \"undo-state\" && currentMxLike) {\n // 从 Server 同步真实的 undo/redo 状态\n const { undoStackSize, redoStackSize } = event.data;\n\n const oldIndex = currentMxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n\n // 直接根据 server 状态重建本地状态\n applyingParentUpdate = true;\n\n // 重建 history 数组匹配 server 的总大小\n currentMxLike.history = new Array(newTotal).fill({});\n currentMxLike.indexOfNextAdd = newIndex;\n\n // 触发对应事件通知 UI 更新\n if (newTotal === 0) {\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n currentMxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n\n applyingParentUpdate = false;\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n startInitRetry();\n\n // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\n\n return {\n get serverClientId() {\n return serverClientId;\n },\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 takeoverUndoManager(file: DrawioFile) {\n if (currentCleanup) {\n currentCleanup();\n }\n\n const editor = file.getUi().editor;\n const originUndoManager = editor.undoManager;\n\n const pairs: Array<[string, ListenerFn]> = [];\n const raw = Array.isArray(originUndoManager?.eventListeners)\n ? (originUndoManager.eventListeners as unknown[])\n : [];\n for (let i = 0; i + 1 < raw.length; i += 2) {\n const key = String(raw[i]);\n const fn = raw[i + 1] as ListenerFn;\n pairs.push([key, fn]);\n }\n\n const mxLike: MxLike = {\n eventListeners: [] as Array<string | ListenerFn>,\n history: [] as unknown[],\n indexOfNextAdd: 0,\n\n addListener(name: string, fn: ListenerFn) {\n this.eventListeners.push(name, fn);\n },\n\n fireEvent(evt: unknown) {\n const eventName: string =\n (evt as { name?: string } | undefined)?.name ||\n ((evt as { getName?: () => string } | undefined)?.getName?.() ??\n \"\");\n for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {\n const key = this.eventListeners[i];\n const listener = this.eventListeners[i + 1] as ListenerFn;\n if (key === eventName) {\n try {\n listener(this, evt);\n } catch (e) {\n console.warn(\n \"[iframe-bridge] undoManager event listener error:\",\n e,\n );\n }\n }\n }\n },\n\n canUndo(): boolean {\n return this.indexOfNextAdd > 0;\n },\n\n canRedo(): boolean {\n return this.indexOfNextAdd < this.history.length;\n },\n\n undo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n }\n },\n\n redo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n }\n },\n\n undoableEditHappened() {\n // no-op\n },\n };\n\n pairs.forEach(([key, fn]) => {\n const k = key.toLowerCase();\n if (k === \"add\" || k === \"clear\" || k === \"undo\" || k === \"redo\") {\n mxLike.addListener(k, fn);\n }\n });\n\n currentMxLike = mxLike;\n editor.undoManager = mxLike as any;\n editor.undoListener = function () {};\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n connectListeners.clear();\n disconnectListeners.clear();\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
1
+ {"version":3,"file":"provider.cjs","sources":["../../../iframe-bridge/src/provider.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\n\ntype ListenerFn = (sender: unknown, evt?: unknown) => void;\n\nfunction createMxEventObject(name: string, props?: Record<string, unknown>) {\n const _props = props || {};\n return {\n name,\n getName: () => name,\n getProperty: (k: string) => _props[k],\n };\n}\n\ntype MxLike = Record<string, unknown> & {\n eventListeners: Array<string | ListenerFn>;\n history: unknown[];\n indexOfNextAdd: number;\n addListener(name: string, fn: ListenerFn): void;\n fireEvent(evt: unknown): void;\n canUndo(): boolean;\n canRedo(): boolean;\n undo(): void;\n redo(): void;\n undoableEditHappened(_edit: unknown): void;\n};\n\nexport interface DrawioEditor {\n undoManager?: {\n eventListeners?: unknown[];\n [key: string]: unknown;\n };\n undoListener?: (...args: unknown[]) => void;\n}\n\nexport interface DrawioFile {\n getUi(): { editor: DrawioEditor };\n}\n\nexport interface IframeBridgeProviderOptions {\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n takeoverUndoManager: (file: DrawioFile) => () => void;\n destroy: () => void;\n}\n\nfunction readVarUint(data: Uint8Array, pos: number): [number, number] {\n let result = 0;\n let shift = 0;\n let byte: number;\n do {\n byte = data[pos++];\n result |= (byte & 0x7f) << shift;\n shift += 7;\n } while (byte >= 0x80);\n return [result >>> 0, pos];\n}\n\nfunction writeVarUint(value: number): number[] {\n const bytes: number[] = [];\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80);\n value >>>= 7;\n }\n bytes.push(value);\n return bytes;\n}\n\nfunction readVarString(data: Uint8Array, pos: number): [string, number] {\n const [len, pos2] = readVarUint(data, pos);\n const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));\n return [str, pos2 + len];\n}\n\nfunction writeVarString(str: string): number[] {\n const encoded = new TextEncoder().encode(str);\n return [...writeVarUint(encoded.length), ...encoded];\n}\n\nfunction remapClientIdInUpdate(\n update: Uint8Array,\n fromId: number,\n toId: number,\n): Uint8Array {\n const result: number[] = [];\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\n result.push(...writeVarUint(count));\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(update, pos);\n pos = pos3;\n const [clock, pos4] = readVarUint(update, pos);\n pos = pos4;\n const [state, pos5] = readVarString(update, pos);\n pos = pos5;\n\n const mappedId = clientID === fromId ? toId : clientID;\n result.push(...writeVarUint(mappedId));\n result.push(...writeVarUint(clock));\n result.push(...writeVarString(state));\n }\n\n return new Uint8Array(result);\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { debug = false } = options ?? {};\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n let connected = false;\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n\n const log = debug\n ? (...args: unknown[]) => console.debug(\"[iframe-bridge provider]\", ...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 function parentPostMessage(message: unknown) {\n logMessage(\"send\", (message as { type?: string }).type ?? \"postMessage\", message);\n window.parent.postMessage(message, \"*\");\n }\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n parentPostMessage({ type: \"init\" });\n initRetryTimer = setInterval(() => {\n if (!connected) {\n parentPostMessage({ type: \"init\" });\n }\n }, 1000);\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline };\n logMessage(\"send\", \"ydoc-update\", message);\n window.parent.postMessage(message, \"*\");\n };\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingParentUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 只同步本地 clientID 的状态给父页面\n // 其他 peers 的更新由父页面的 WebRTC/WebSocket provider 负责广播\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const state = awareness.getLocalState();\n const message = { type: \"awareness-local-state\", state };\n logMessage(\"send\", \"awareness-local-state\", state);\n window.parent.postMessage(message, \"*\");\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n logMessage(\"recv\", type, payload);\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n logMessage(\"recv\", type, payload);\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n\n // 从 server 的 awareness state 中提取 user 信息并同步到本地\n // 避免 binding 生成随机用户信息后通过 remap 覆盖父页面的真实用户\n let localUserSynced = false;\n if (serverClientId != null) {\n const serverState = awareness.getStates().get(serverClientId);\n if (serverState) {\n const serverUserAccount = (\n serverState as { user?: { account?: unknown } }\n ).user?.account;\n const serverUserName = (serverState as { user?: { name?: unknown } })\n .user?.name;\n const serverUserColor = (\n serverState as { user?: { color?: unknown } }\n ).user?.color;\n if (serverUserAccount || serverUserName || serverUserColor) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = ((currentLocal as Record<string, unknown>)\n .user || {}) as Record<string, unknown>;\n const nextUser: Record<string, unknown> = { ...currentUser };\n if (serverUserAccount) {\n nextUser.account = serverUserAccount;\n }\n if (serverUserName) {\n nextUser.name = serverUserName;\n }\n if (serverUserColor) {\n nextUser.color = serverUserColor;\n }\n const userChanged =\n currentUser.account !== nextUser.account ||\n currentUser.name !== nextUser.name ||\n currentUser.color !== nextUser.color;\n if (userChanged) {\n awareness.setLocalState({\n ...currentLocal,\n user: nextUser,\n });\n localUserSynced = true;\n }\n }\n }\n }\n\n applyingParentUpdate = false;\n\n // 如果同步了本地 user info,需要发送一次更新给父页面\n // 恢复可能被随机值覆盖的 server user 信息\n if (localUserSynced && serverClientId != null) {\n const update = encodeAwarenessUpdate(awareness, [awareness.clientID]);\n const remapped = remapClientIdInUpdate(\n update,\n awareness.clientID,\n serverClientId,\n );\n const message = { type: \"awareness-update\", payload: Array.from(remapped) };\n logMessage(\"send\", \"awareness-update\", message);\n window.parent.postMessage(message, \"*\");\n }\n } else if (type === \"undo-state\" && currentMxLike) {\n // 从 Server 同步真实的 undo/redo 状态\n const { undoStackSize, redoStackSize } = event.data;\n\n const oldIndex = currentMxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n\n // 直接根据 server 状态重建本地状态\n applyingParentUpdate = true;\n\n // 重建 history 数组匹配 server 的总大小\n currentMxLike.history = new Array(newTotal).fill({});\n currentMxLike.indexOfNextAdd = newIndex;\n\n // 触发对应事件通知 UI 更新\n if (newTotal === 0) {\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n currentMxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n\n applyingParentUpdate = false;\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n startInitRetry();\n\n // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\n\n return {\n get serverClientId() {\n return serverClientId;\n },\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 takeoverUndoManager(file: DrawioFile) {\n if (currentCleanup) {\n currentCleanup();\n }\n\n const editor = file.getUi().editor;\n const originUndoManager = editor.undoManager;\n\n const pairs: Array<[string, ListenerFn]> = [];\n const raw = Array.isArray(originUndoManager?.eventListeners)\n ? (originUndoManager.eventListeners as unknown[])\n : [];\n for (let i = 0; i + 1 < raw.length; i += 2) {\n const key = String(raw[i]);\n const fn = raw[i + 1] as ListenerFn;\n pairs.push([key, fn]);\n }\n\n const mxLike: MxLike = {\n eventListeners: [] as Array<string | ListenerFn>,\n history: [] as unknown[],\n indexOfNextAdd: 0,\n\n addListener(name: string, fn: ListenerFn) {\n this.eventListeners.push(name, fn);\n },\n\n fireEvent(evt: unknown) {\n const eventName: string =\n (evt as { name?: string } | undefined)?.name ||\n ((evt as { getName?: () => string } | undefined)?.getName?.() ??\n \"\");\n for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {\n const key = this.eventListeners[i];\n const listener = this.eventListeners[i + 1] as ListenerFn;\n if (key === eventName) {\n try {\n listener(this, evt);\n } catch (e) {\n console.warn(\n \"[iframe-bridge] undoManager event listener error:\",\n e,\n );\n }\n }\n }\n },\n\n canUndo(): boolean {\n return this.indexOfNextAdd > 0;\n },\n\n canRedo(): boolean {\n return this.indexOfNextAdd < this.history.length;\n },\n\n undo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n }\n },\n\n redo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n }\n },\n\n undoableEditHappened() {\n // no-op\n },\n };\n\n pairs.forEach(([key, fn]) => {\n const k = key.toLowerCase();\n if (k === \"add\" || k === \"clear\" || k === \"undo\" || k === \"redo\") {\n mxLike.addListener(k, fn);\n }\n });\n\n currentMxLike = mxLike;\n editor.undoManager = mxLike as any;\n editor.undoListener = function () {};\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n connectListeners.clear();\n disconnectListeners.clear();\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":["awareness","Y","applyAwarenessUpdate","encodeAwarenessUpdate"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import * as Y from "yjs";
2
- import { encodeAwarenessUpdate, applyAwarenessUpdate } from "y-protocols/awareness";
2
+ import { applyAwarenessUpdate, encodeAwarenessUpdate } from "y-protocols/awareness";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __defProps = Object.defineProperties;
5
5
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
@@ -77,7 +77,7 @@ function remapClientIdInUpdate(update, fromId, toId) {
77
77
  return new Uint8Array(result);
78
78
  }
79
79
  function createIframeBridgeProvider(ydoc, awareness, options) {
80
- const { awarenessSyncMode = "binary" } = options != null ? options : {};
80
+ const { debug = false } = options != null ? options : {};
81
81
  let applyingParentUpdate = false;
82
82
  let serverClientId = null;
83
83
  let currentCleanup = null;
@@ -86,6 +86,25 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
86
86
  let initRetryTimer = null;
87
87
  const connectListeners = /* @__PURE__ */ new Set();
88
88
  const disconnectListeners = /* @__PURE__ */ new Set();
89
+ const log = debug ? (...args) => console.debug("[iframe-bridge provider]", ...args) : () => void 0;
90
+ function formatPayload(payload) {
91
+ if (payload instanceof Uint8Array) {
92
+ return { bytes: payload.byteLength };
93
+ }
94
+ if (Array.isArray(payload) && payload.every((item) => typeof item === "number")) {
95
+ return { bytes: payload.length };
96
+ }
97
+ return payload;
98
+ }
99
+ function logMessage(direction, type, payload) {
100
+ if (!debug) return;
101
+ log(direction, type, formatPayload(payload));
102
+ }
103
+ function parentPostMessage(message) {
104
+ var _a;
105
+ logMessage("send", (_a = message.type) != null ? _a : "postMessage", message);
106
+ window.parent.postMessage(message, "*");
107
+ }
89
108
  function setConnected(value) {
90
109
  if (connected === value) return;
91
110
  connected = value;
@@ -99,20 +118,19 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
99
118
  if (initRetryTimer) {
100
119
  clearInterval(initRetryTimer);
101
120
  }
102
- window.parent.postMessage({ type: "init" }, "*");
121
+ parentPostMessage({ type: "init" });
103
122
  initRetryTimer = setInterval(() => {
104
123
  if (!connected) {
105
- window.parent.postMessage({ type: "init" }, "*");
124
+ parentPostMessage({ type: "init" });
106
125
  }
107
126
  }, 1e3);
108
127
  }
109
128
  const onYdocUpdate = (update, origin) => {
110
129
  if (applyingParentUpdate) return;
111
130
  const isBaseline = origin === null || origin === void 0;
112
- window.parent.postMessage(
113
- { type: "ydoc-update", payload: Array.from(update), isBaseline },
114
- "*"
115
- );
131
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline };
132
+ logMessage("send", "ydoc-update", message);
133
+ window.parent.postMessage(message, "*");
116
134
  };
117
135
  const onAwarenessUpdate = ({
118
136
  added,
@@ -125,24 +143,16 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
125
143
  const localClientId = awareness.clientID;
126
144
  const localChanged = changes.includes(localClientId);
127
145
  if (!localChanged) return;
128
- if (awarenessSyncMode === "local-state") {
129
- window.parent.postMessage(
130
- { type: "awareness-local-state", state: awareness.getLocalState() },
131
- "*"
132
- );
133
- } else {
134
- const update = encodeAwarenessUpdate(awareness, [localClientId]);
135
- const remapped = serverClientId != null ? remapClientIdInUpdate(update, localClientId, serverClientId) : update;
136
- window.parent.postMessage(
137
- { type: "awareness-update", payload: Array.from(remapped) },
138
- "*"
139
- );
140
- }
146
+ const state = awareness.getLocalState();
147
+ const message = { type: "awareness-local-state", state };
148
+ logMessage("send", "awareness-local-state", state);
149
+ window.parent.postMessage(message, "*");
141
150
  };
142
151
  const onMessage = (event) => {
143
152
  var _a, _b, _c;
144
153
  if (event.source !== window.parent) return;
145
154
  const { type, payload, serverClientId: receivedServerId } = event.data;
155
+ logMessage("recv", type, payload);
146
156
  if (type === "pong" && receivedServerId != null) {
147
157
  serverClientId = receivedServerId;
148
158
  return;
@@ -159,6 +169,7 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
159
169
  }
160
170
  }
161
171
  } else if (type === "awareness-sync" || type === "awareness-update") {
172
+ logMessage("recv", type, payload);
162
173
  if (receivedServerId != null) {
163
174
  serverClientId = receivedServerId;
164
175
  }
@@ -202,10 +213,9 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
202
213
  awareness.clientID,
203
214
  serverClientId
204
215
  );
205
- window.parent.postMessage(
206
- { type: "awareness-update", payload: Array.from(remapped) },
207
- "*"
208
- );
216
+ const message = { type: "awareness-update", payload: Array.from(remapped) };
217
+ logMessage("send", "awareness-update", message);
218
+ window.parent.postMessage(message, "*");
209
219
  }
210
220
  } else if (type === "undo-state" && currentMxLike) {
211
221
  const { undoStackSize, redoStackSize } = event.data;
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sources":["../../../iframe-bridge/src/provider.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\n\ntype ListenerFn = (sender: unknown, evt?: unknown) => void;\n\nfunction createMxEventObject(name: string, props?: Record<string, unknown>) {\n const _props = props || {};\n return {\n name,\n getName: () => name,\n getProperty: (k: string) => _props[k],\n };\n}\n\ntype MxLike = Record<string, unknown> & {\n eventListeners: Array<string | ListenerFn>;\n history: unknown[];\n indexOfNextAdd: number;\n addListener(name: string, fn: ListenerFn): void;\n fireEvent(evt: unknown): void;\n canUndo(): boolean;\n canRedo(): boolean;\n undo(): void;\n redo(): void;\n undoableEditHappened(_edit: unknown): void;\n};\n\nexport interface DrawioEditor {\n undoManager?: {\n eventListeners?: unknown[];\n [key: string]: unknown;\n };\n undoListener?: (...args: unknown[]) => void;\n}\n\nexport interface DrawioFile {\n getUi(): { editor: DrawioEditor };\n}\n\nexport interface IframeBridgeProviderOptions {\n awarenessSyncMode?: \"binary\" | \"local-state\";\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n takeoverUndoManager: (file: DrawioFile) => () => void;\n destroy: () => void;\n}\n\nfunction readVarUint(data: Uint8Array, pos: number): [number, number] {\n let result = 0;\n let shift = 0;\n let byte: number;\n do {\n byte = data[pos++];\n result |= (byte & 0x7f) << shift;\n shift += 7;\n } while (byte >= 0x80);\n return [result >>> 0, pos];\n}\n\nfunction writeVarUint(value: number): number[] {\n const bytes: number[] = [];\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80);\n value >>>= 7;\n }\n bytes.push(value);\n return bytes;\n}\n\nfunction readVarString(data: Uint8Array, pos: number): [string, number] {\n const [len, pos2] = readVarUint(data, pos);\n const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));\n return [str, pos2 + len];\n}\n\nfunction writeVarString(str: string): number[] {\n const encoded = new TextEncoder().encode(str);\n return [...writeVarUint(encoded.length), ...encoded];\n}\n\nfunction remapClientIdInUpdate(\n update: Uint8Array,\n fromId: number,\n toId: number,\n): Uint8Array {\n const result: number[] = [];\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\n result.push(...writeVarUint(count));\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(update, pos);\n pos = pos3;\n const [clock, pos4] = readVarUint(update, pos);\n pos = pos4;\n const [state, pos5] = readVarString(update, pos);\n pos = pos5;\n\n const mappedId = clientID === fromId ? toId : clientID;\n result.push(...writeVarUint(mappedId));\n result.push(...writeVarUint(clock));\n result.push(...writeVarString(state));\n }\n\n return new Uint8Array(result);\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { awarenessSyncMode = \"binary\" } = options ?? {};\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n let connected = false;\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n window.parent.postMessage({ type: \"init\" }, \"*\");\n initRetryTimer = setInterval(() => {\n if (!connected) {\n window.parent.postMessage({ type: \"init\" }, \"*\");\n }\n }, 1000);\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update), isBaseline },\n \"*\",\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 (applyingParentUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 只发送本地 clientID 的更新给父页面\n // 其他 peers 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n if (awarenessSyncMode === \"local-state\") {\n window.parent.postMessage(\n { type: \"awareness-local-state\", state: awareness.getLocalState() },\n \"*\",\n );\n } else {\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, serverClientId)\n : update;\n\n window.parent.postMessage(\n { type: \"awareness-update\", payload: Array.from(remapped) },\n \"*\",\n );\n }\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n\n // 从 server 的 awareness state 中提取 user 信息并同步到本地\n // 避免 binding 生成随机用户信息后通过 remap 覆盖父页面的真实用户\n let localUserSynced = false;\n if (serverClientId != null) {\n const serverState = awareness.getStates().get(serverClientId);\n if (serverState) {\n const serverUserAccount = (\n serverState as { user?: { account?: unknown } }\n ).user?.account;\n const serverUserName = (serverState as { user?: { name?: unknown } })\n .user?.name;\n const serverUserColor = (\n serverState as { user?: { color?: unknown } }\n ).user?.color;\n if (serverUserAccount || serverUserName || serverUserColor) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = ((currentLocal as Record<string, unknown>)\n .user || {}) as Record<string, unknown>;\n const nextUser: Record<string, unknown> = { ...currentUser };\n if (serverUserAccount) {\n nextUser.account = serverUserAccount;\n }\n if (serverUserName) {\n nextUser.name = serverUserName;\n }\n if (serverUserColor) {\n nextUser.color = serverUserColor;\n }\n const userChanged =\n currentUser.account !== nextUser.account ||\n currentUser.name !== nextUser.name ||\n currentUser.color !== nextUser.color;\n if (userChanged) {\n awareness.setLocalState({\n ...currentLocal,\n user: nextUser,\n });\n localUserSynced = true;\n }\n }\n }\n }\n\n applyingParentUpdate = false;\n\n // 如果同步了本地 user info,需要发送一次更新给父页面\n // 恢复可能被随机值覆盖的 server user 信息\n if (localUserSynced && serverClientId != null) {\n const update = encodeAwarenessUpdate(awareness, [awareness.clientID]);\n const remapped = remapClientIdInUpdate(\n update,\n awareness.clientID,\n serverClientId,\n );\n window.parent.postMessage(\n { type: \"awareness-update\", payload: Array.from(remapped) },\n \"*\",\n );\n }\n } else if (type === \"undo-state\" && currentMxLike) {\n // 从 Server 同步真实的 undo/redo 状态\n const { undoStackSize, redoStackSize } = event.data;\n\n const oldIndex = currentMxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n\n // 直接根据 server 状态重建本地状态\n applyingParentUpdate = true;\n\n // 重建 history 数组匹配 server 的总大小\n currentMxLike.history = new Array(newTotal).fill({});\n currentMxLike.indexOfNextAdd = newIndex;\n\n // 触发对应事件通知 UI 更新\n if (newTotal === 0) {\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n currentMxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n\n applyingParentUpdate = false;\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n startInitRetry();\n\n // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\n\n return {\n get serverClientId() {\n return serverClientId;\n },\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 takeoverUndoManager(file: DrawioFile) {\n if (currentCleanup) {\n currentCleanup();\n }\n\n const editor = file.getUi().editor;\n const originUndoManager = editor.undoManager;\n\n const pairs: Array<[string, ListenerFn]> = [];\n const raw = Array.isArray(originUndoManager?.eventListeners)\n ? (originUndoManager.eventListeners as unknown[])\n : [];\n for (let i = 0; i + 1 < raw.length; i += 2) {\n const key = String(raw[i]);\n const fn = raw[i + 1] as ListenerFn;\n pairs.push([key, fn]);\n }\n\n const mxLike: MxLike = {\n eventListeners: [] as Array<string | ListenerFn>,\n history: [] as unknown[],\n indexOfNextAdd: 0,\n\n addListener(name: string, fn: ListenerFn) {\n this.eventListeners.push(name, fn);\n },\n\n fireEvent(evt: unknown) {\n const eventName: string =\n (evt as { name?: string } | undefined)?.name ||\n ((evt as { getName?: () => string } | undefined)?.getName?.() ??\n \"\");\n for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {\n const key = this.eventListeners[i];\n const listener = this.eventListeners[i + 1] as ListenerFn;\n if (key === eventName) {\n try {\n listener(this, evt);\n } catch (e) {\n console.warn(\n \"[iframe-bridge] undoManager event listener error:\",\n e,\n );\n }\n }\n }\n },\n\n canUndo(): boolean {\n return this.indexOfNextAdd > 0;\n },\n\n canRedo(): boolean {\n return this.indexOfNextAdd < this.history.length;\n },\n\n undo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n }\n },\n\n redo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n }\n },\n\n undoableEditHappened() {\n // no-op\n },\n };\n\n pairs.forEach(([key, fn]) => {\n const k = key.toLowerCase();\n if (k === \"add\" || k === \"clear\" || k === \"undo\" || k === \"redo\") {\n mxLike.addListener(k, fn);\n }\n });\n\n currentMxLike = mxLike;\n editor.undoManager = mxLike as any;\n editor.undoListener = function () {};\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n connectListeners.clear();\n disconnectListeners.clear();\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.js","sources":["../../../iframe-bridge/src/provider.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\n\ntype ListenerFn = (sender: unknown, evt?: unknown) => void;\n\nfunction createMxEventObject(name: string, props?: Record<string, unknown>) {\n const _props = props || {};\n return {\n name,\n getName: () => name,\n getProperty: (k: string) => _props[k],\n };\n}\n\ntype MxLike = Record<string, unknown> & {\n eventListeners: Array<string | ListenerFn>;\n history: unknown[];\n indexOfNextAdd: number;\n addListener(name: string, fn: ListenerFn): void;\n fireEvent(evt: unknown): void;\n canUndo(): boolean;\n canRedo(): boolean;\n undo(): void;\n redo(): void;\n undoableEditHappened(_edit: unknown): void;\n};\n\nexport interface DrawioEditor {\n undoManager?: {\n eventListeners?: unknown[];\n [key: string]: unknown;\n };\n undoListener?: (...args: unknown[]) => void;\n}\n\nexport interface DrawioFile {\n getUi(): { editor: DrawioEditor };\n}\n\nexport interface IframeBridgeProviderOptions {\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n takeoverUndoManager: (file: DrawioFile) => () => void;\n destroy: () => void;\n}\n\nfunction readVarUint(data: Uint8Array, pos: number): [number, number] {\n let result = 0;\n let shift = 0;\n let byte: number;\n do {\n byte = data[pos++];\n result |= (byte & 0x7f) << shift;\n shift += 7;\n } while (byte >= 0x80);\n return [result >>> 0, pos];\n}\n\nfunction writeVarUint(value: number): number[] {\n const bytes: number[] = [];\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80);\n value >>>= 7;\n }\n bytes.push(value);\n return bytes;\n}\n\nfunction readVarString(data: Uint8Array, pos: number): [string, number] {\n const [len, pos2] = readVarUint(data, pos);\n const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));\n return [str, pos2 + len];\n}\n\nfunction writeVarString(str: string): number[] {\n const encoded = new TextEncoder().encode(str);\n return [...writeVarUint(encoded.length), ...encoded];\n}\n\nfunction remapClientIdInUpdate(\n update: Uint8Array,\n fromId: number,\n toId: number,\n): Uint8Array {\n const result: number[] = [];\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\n result.push(...writeVarUint(count));\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(update, pos);\n pos = pos3;\n const [clock, pos4] = readVarUint(update, pos);\n pos = pos4;\n const [state, pos5] = readVarString(update, pos);\n pos = pos5;\n\n const mappedId = clientID === fromId ? toId : clientID;\n result.push(...writeVarUint(mappedId));\n result.push(...writeVarUint(clock));\n result.push(...writeVarString(state));\n }\n\n return new Uint8Array(result);\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { debug = false } = options ?? {};\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n let connected = false;\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n\n const log = debug\n ? (...args: unknown[]) => console.debug(\"[iframe-bridge provider]\", ...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 function parentPostMessage(message: unknown) {\n logMessage(\"send\", (message as { type?: string }).type ?? \"postMessage\", message);\n window.parent.postMessage(message, \"*\");\n }\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n parentPostMessage({ type: \"init\" });\n initRetryTimer = setInterval(() => {\n if (!connected) {\n parentPostMessage({ type: \"init\" });\n }\n }, 1000);\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline };\n logMessage(\"send\", \"ydoc-update\", message);\n window.parent.postMessage(message, \"*\");\n };\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingParentUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 只同步本地 clientID 的状态给父页面\n // 其他 peers 的更新由父页面的 WebRTC/WebSocket provider 负责广播\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const state = awareness.getLocalState();\n const message = { type: \"awareness-local-state\", state };\n logMessage(\"send\", \"awareness-local-state\", state);\n window.parent.postMessage(message, \"*\");\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n logMessage(\"recv\", type, payload);\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n logMessage(\"recv\", type, payload);\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n\n // 从 server 的 awareness state 中提取 user 信息并同步到本地\n // 避免 binding 生成随机用户信息后通过 remap 覆盖父页面的真实用户\n let localUserSynced = false;\n if (serverClientId != null) {\n const serverState = awareness.getStates().get(serverClientId);\n if (serverState) {\n const serverUserAccount = (\n serverState as { user?: { account?: unknown } }\n ).user?.account;\n const serverUserName = (serverState as { user?: { name?: unknown } })\n .user?.name;\n const serverUserColor = (\n serverState as { user?: { color?: unknown } }\n ).user?.color;\n if (serverUserAccount || serverUserName || serverUserColor) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = ((currentLocal as Record<string, unknown>)\n .user || {}) as Record<string, unknown>;\n const nextUser: Record<string, unknown> = { ...currentUser };\n if (serverUserAccount) {\n nextUser.account = serverUserAccount;\n }\n if (serverUserName) {\n nextUser.name = serverUserName;\n }\n if (serverUserColor) {\n nextUser.color = serverUserColor;\n }\n const userChanged =\n currentUser.account !== nextUser.account ||\n currentUser.name !== nextUser.name ||\n currentUser.color !== nextUser.color;\n if (userChanged) {\n awareness.setLocalState({\n ...currentLocal,\n user: nextUser,\n });\n localUserSynced = true;\n }\n }\n }\n }\n\n applyingParentUpdate = false;\n\n // 如果同步了本地 user info,需要发送一次更新给父页面\n // 恢复可能被随机值覆盖的 server user 信息\n if (localUserSynced && serverClientId != null) {\n const update = encodeAwarenessUpdate(awareness, [awareness.clientID]);\n const remapped = remapClientIdInUpdate(\n update,\n awareness.clientID,\n serverClientId,\n );\n const message = { type: \"awareness-update\", payload: Array.from(remapped) };\n logMessage(\"send\", \"awareness-update\", message);\n window.parent.postMessage(message, \"*\");\n }\n } else if (type === \"undo-state\" && currentMxLike) {\n // 从 Server 同步真实的 undo/redo 状态\n const { undoStackSize, redoStackSize } = event.data;\n\n const oldIndex = currentMxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n\n // 直接根据 server 状态重建本地状态\n applyingParentUpdate = true;\n\n // 重建 history 数组匹配 server 的总大小\n currentMxLike.history = new Array(newTotal).fill({});\n currentMxLike.indexOfNextAdd = newIndex;\n\n // 触发对应事件通知 UI 更新\n if (newTotal === 0) {\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n currentMxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n\n applyingParentUpdate = false;\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n startInitRetry();\n\n // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\n\n return {\n get serverClientId() {\n return serverClientId;\n },\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 takeoverUndoManager(file: DrawioFile) {\n if (currentCleanup) {\n currentCleanup();\n }\n\n const editor = file.getUi().editor;\n const originUndoManager = editor.undoManager;\n\n const pairs: Array<[string, ListenerFn]> = [];\n const raw = Array.isArray(originUndoManager?.eventListeners)\n ? (originUndoManager.eventListeners as unknown[])\n : [];\n for (let i = 0; i + 1 < raw.length; i += 2) {\n const key = String(raw[i]);\n const fn = raw[i + 1] as ListenerFn;\n pairs.push([key, fn]);\n }\n\n const mxLike: MxLike = {\n eventListeners: [] as Array<string | ListenerFn>,\n history: [] as unknown[],\n indexOfNextAdd: 0,\n\n addListener(name: string, fn: ListenerFn) {\n this.eventListeners.push(name, fn);\n },\n\n fireEvent(evt: unknown) {\n const eventName: string =\n (evt as { name?: string } | undefined)?.name ||\n ((evt as { getName?: () => string } | undefined)?.getName?.() ??\n \"\");\n for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {\n const key = this.eventListeners[i];\n const listener = this.eventListeners[i + 1] as ListenerFn;\n if (key === eventName) {\n try {\n listener(this, evt);\n } catch (e) {\n console.warn(\n \"[iframe-bridge] undoManager event listener error:\",\n e,\n );\n }\n }\n }\n },\n\n canUndo(): boolean {\n return this.indexOfNextAdd > 0;\n },\n\n canRedo(): boolean {\n return this.indexOfNextAdd < this.history.length;\n },\n\n undo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n }\n },\n\n redo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n }\n },\n\n undoableEditHappened() {\n // no-op\n },\n };\n\n pairs.forEach(([key, fn]) => {\n const k = key.toLowerCase();\n if (k === \"add\" || k === \"clear\" || k === \"undo\" || k === \"redo\") {\n mxLike.addListener(k, fn);\n }\n });\n\n currentMxLike = mxLike;\n editor.undoManager = mxLike as any;\n editor.undoListener = function () {};\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n connectListeners.clear();\n disconnectListeners.clear();\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -22,7 +22,21 @@ const Y__namespace = /* @__PURE__ */ _interopNamespaceDefault(Y);
22
22
  const IFRAME_ORIGIN = {};
23
23
  const BASELINE_ORIGIN = {};
24
24
  function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
25
- const { undoManager } = options != null ? options : {};
25
+ const { undoManager, debug = false } = options != null ? options : {};
26
+ const log = debug ? (...args) => console.debug("[iframe-bridge server]", ...args) : () => void 0;
27
+ function formatPayload(payload) {
28
+ if (payload instanceof Uint8Array) {
29
+ return { bytes: payload.byteLength };
30
+ }
31
+ if (Array.isArray(payload) && payload.every((item) => typeof item === "number")) {
32
+ return { bytes: payload.length };
33
+ }
34
+ return payload;
35
+ }
36
+ function logMessage(direction, type, payload) {
37
+ if (!debug) return;
38
+ log(direction, type, formatPayload(payload));
39
+ }
26
40
  let connected = false;
27
41
  let applyingIframeUpdate = false;
28
42
  const connectListeners = /* @__PURE__ */ new Set();
@@ -67,13 +81,17 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
67
81
  }
68
82
  function postToIframe(type, payload) {
69
83
  const cw = iframe.contentWindow;
84
+ const message = { type, payload: payload ? Array.from(payload) : [] };
85
+ logMessage("send", type, payload);
70
86
  if (cw) {
71
- cw.postMessage(
72
- { type, payload: payload ? Array.from(payload) : [] },
73
- "*"
74
- );
87
+ cw.postMessage(message, "*");
75
88
  }
76
89
  }
90
+ const onYdocUpdate = (update, origin) => {
91
+ if (origin === IFRAME_ORIGIN) return;
92
+ logMessage("send", "ydoc-update", update);
93
+ postToIframe("ydoc-update", update);
94
+ };
77
95
  function postUndoStateToIframe() {
78
96
  var _a, _b;
79
97
  if (!undoManager) return;
@@ -88,13 +106,10 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
88
106
  undoStackSize: (_a = undoStack == null ? void 0 : undoStack.length) != null ? _a : 0,
89
107
  redoStackSize: (_b = redoStack == null ? void 0 : redoStack.length) != null ? _b : 0
90
108
  };
109
+ logMessage("send", "undo-state", state);
91
110
  cw.postMessage(state, "*");
92
111
  }
93
112
  }
94
- const onYdocUpdate = (update, origin) => {
95
- if (origin === IFRAME_ORIGIN) return;
96
- postToIframe("ydoc-update", update);
97
- };
98
113
  const onAwarenessUpdate = ({
99
114
  added,
100
115
  updated,
@@ -104,57 +119,55 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
104
119
  const changes = [...added, ...updated, ...removed];
105
120
  if (changes.length === 0) return;
106
121
  const update = awareness.encodeAwarenessUpdate(awareness$1, changes);
122
+ logMessage("send", "awareness-update", update);
107
123
  postToIframe("awareness-update", update);
108
124
  };
109
125
  const onMessage = (event) => {
110
126
  if (event.source !== iframe.contentWindow) return;
111
127
  const { type: msgType, payload } = event.data;
112
128
  if (msgType === "init") {
113
- console.log(
114
- `[iframe-bridge server] received init — connected=${connected}`
115
- );
129
+ logMessage("recv", "init", payload);
116
130
  if (!connected) {
117
131
  setConnected(true);
118
132
  }
119
133
  const docState = Y__namespace.encodeStateAsUpdate(ydoc);
120
- console.log(
121
- `[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`
122
- );
123
- const awarenessState = awareness.encodeAwarenessUpdate(
124
- awareness$1,
125
- Array.from(awareness$1.getStates().keys())
126
- );
134
+ logMessage("send", "ydoc-sync", { bytes: docState.length });
127
135
  postToIframe("ydoc-sync", new Uint8Array(Array.from(docState)));
128
136
  const cw = iframe.contentWindow;
129
137
  if (cw) {
130
- cw.postMessage(
131
- {
132
- type: "awareness-sync",
133
- payload: Array.from(awarenessState),
134
- serverClientId: awareness$1.clientID
135
- },
136
- "*"
137
- );
138
+ const message = {
139
+ type: "awareness-sync",
140
+ payload: Array.from(awareness.encodeAwarenessUpdate(
141
+ awareness$1,
142
+ Array.from(awareness$1.getStates().keys())
143
+ )),
144
+ serverClientId: awareness$1.clientID
145
+ };
146
+ logMessage("send", "awareness-sync", message);
147
+ cw.postMessage(message, "*");
138
148
  }
139
149
  postUndoStateToIframe();
140
150
  } else if (msgType === "ping") {
151
+ logMessage("recv", "ping", payload);
141
152
  const cw = iframe.contentWindow;
142
153
  if (cw) {
143
- cw.postMessage(
144
- { type: "pong", serverClientId: awareness$1.clientID },
145
- "*"
146
- );
154
+ const message = { type: "pong", serverClientId: awareness$1.clientID };
155
+ logMessage("send", "pong", message);
156
+ cw.postMessage(message, "*");
147
157
  }
148
158
  } else if (msgType === "ydoc-update") {
159
+ logMessage("recv", "ydoc-update", payload);
149
160
  const update = new Uint8Array(payload);
150
161
  const isBaseline = event.data.isBaseline;
151
162
  const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
152
163
  Y__namespace.applyUpdate(ydoc, update, applyOrigin);
153
164
  } else if (msgType === "awareness-local-state") {
165
+ logMessage("recv", "awareness-local-state", payload);
154
166
  applyingIframeUpdate = true;
155
167
  awareness$1.setLocalState(event.data.state);
156
168
  applyingIframeUpdate = false;
157
169
  } else if (msgType === "awareness-update") {
170
+ logMessage("recv", "awareness-update", payload);
158
171
  applyingIframeUpdate = true;
159
172
  awareness.applyAwarenessUpdate(awareness$1, new Uint8Array(payload), IFRAME_ORIGIN);
160
173
  applyingIframeUpdate = false;
@@ -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}\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 } = options ?? {};\n let connected = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\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 connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type, payload: payload ? Array.from(payload) : [] },\n \"*\",\n );\n }\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 cw.postMessage(state, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n postToIframe(\"ydoc-update\", update);\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 postToIframe(\"awareness-update\", update);\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 === \"init\") {\n console.log(\n `[iframe-bridge server] received init — connected=${connected}`,\n );\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n console.log(\n `[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`,\n );\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n {\n type: \"awareness-sync\",\n payload: Array.from(awarenessState),\n serverClientId: awareness.clientID,\n },\n \"*\",\n );\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n }\n } else if (msgType === \"ydoc-update\") {\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 } else if (msgType === \"awareness-local-state\") {\n applyingIframeUpdate = true;\n awareness.setLocalState(event.data.state);\n applyingIframeUpdate = false;\n } else if (msgType === \"awareness-update\") {\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\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 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/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.debug(\"[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 applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\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 connectListeners.forEach((fn) => fn());\n } else {\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 logMessage(\"send\", \"ydoc-update\", update);\n postToIframe(\"ydoc-update\", update);\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 postToIframe(\"awareness-update\", update);\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 === \"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 postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n )),\n serverClientId: awareness.clientID,\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 };\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 } else if (msgType === \"awareness-local-state\") {\n logMessage(\"recv\", \"awareness-local-state\", payload);\n applyingIframeUpdate = true;\n awareness.setLocalState(event.data.state);\n applyingIframeUpdate = false;\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 === \"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 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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"}
@@ -3,7 +3,21 @@ import { encodeAwarenessUpdate, applyAwarenessUpdate } from "y-protocols/awarene
3
3
  const IFRAME_ORIGIN = {};
4
4
  const BASELINE_ORIGIN = {};
5
5
  function createIframeBridgeServer(iframe, ydoc, awareness, options) {
6
- const { undoManager } = options != null ? options : {};
6
+ const { undoManager, debug = false } = options != null ? options : {};
7
+ const log = debug ? (...args) => console.debug("[iframe-bridge server]", ...args) : () => void 0;
8
+ function formatPayload(payload) {
9
+ if (payload instanceof Uint8Array) {
10
+ return { bytes: payload.byteLength };
11
+ }
12
+ if (Array.isArray(payload) && payload.every((item) => typeof item === "number")) {
13
+ return { bytes: payload.length };
14
+ }
15
+ return payload;
16
+ }
17
+ function logMessage(direction, type, payload) {
18
+ if (!debug) return;
19
+ log(direction, type, formatPayload(payload));
20
+ }
7
21
  let connected = false;
8
22
  let applyingIframeUpdate = false;
9
23
  const connectListeners = /* @__PURE__ */ new Set();
@@ -48,13 +62,17 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
48
62
  }
49
63
  function postToIframe(type, payload) {
50
64
  const cw = iframe.contentWindow;
65
+ const message = { type, payload: payload ? Array.from(payload) : [] };
66
+ logMessage("send", type, payload);
51
67
  if (cw) {
52
- cw.postMessage(
53
- { type, payload: payload ? Array.from(payload) : [] },
54
- "*"
55
- );
68
+ cw.postMessage(message, "*");
56
69
  }
57
70
  }
71
+ const onYdocUpdate = (update, origin) => {
72
+ if (origin === IFRAME_ORIGIN) return;
73
+ logMessage("send", "ydoc-update", update);
74
+ postToIframe("ydoc-update", update);
75
+ };
58
76
  function postUndoStateToIframe() {
59
77
  var _a, _b;
60
78
  if (!undoManager) return;
@@ -69,13 +87,10 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
69
87
  undoStackSize: (_a = undoStack == null ? void 0 : undoStack.length) != null ? _a : 0,
70
88
  redoStackSize: (_b = redoStack == null ? void 0 : redoStack.length) != null ? _b : 0
71
89
  };
90
+ logMessage("send", "undo-state", state);
72
91
  cw.postMessage(state, "*");
73
92
  }
74
93
  }
75
- const onYdocUpdate = (update, origin) => {
76
- if (origin === IFRAME_ORIGIN) return;
77
- postToIframe("ydoc-update", update);
78
- };
79
94
  const onAwarenessUpdate = ({
80
95
  added,
81
96
  updated,
@@ -85,57 +100,55 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
85
100
  const changes = [...added, ...updated, ...removed];
86
101
  if (changes.length === 0) return;
87
102
  const update = encodeAwarenessUpdate(awareness, changes);
103
+ logMessage("send", "awareness-update", update);
88
104
  postToIframe("awareness-update", update);
89
105
  };
90
106
  const onMessage = (event) => {
91
107
  if (event.source !== iframe.contentWindow) return;
92
108
  const { type: msgType, payload } = event.data;
93
109
  if (msgType === "init") {
94
- console.log(
95
- `[iframe-bridge server] received init — connected=${connected}`
96
- );
110
+ logMessage("recv", "init", payload);
97
111
  if (!connected) {
98
112
  setConnected(true);
99
113
  }
100
114
  const docState = Y.encodeStateAsUpdate(ydoc);
101
- console.log(
102
- `[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`
103
- );
104
- const awarenessState = encodeAwarenessUpdate(
105
- awareness,
106
- Array.from(awareness.getStates().keys())
107
- );
115
+ logMessage("send", "ydoc-sync", { bytes: docState.length });
108
116
  postToIframe("ydoc-sync", new Uint8Array(Array.from(docState)));
109
117
  const cw = iframe.contentWindow;
110
118
  if (cw) {
111
- cw.postMessage(
112
- {
113
- type: "awareness-sync",
114
- payload: Array.from(awarenessState),
115
- serverClientId: awareness.clientID
116
- },
117
- "*"
118
- );
119
+ const message = {
120
+ type: "awareness-sync",
121
+ payload: Array.from(encodeAwarenessUpdate(
122
+ awareness,
123
+ Array.from(awareness.getStates().keys())
124
+ )),
125
+ serverClientId: awareness.clientID
126
+ };
127
+ logMessage("send", "awareness-sync", message);
128
+ cw.postMessage(message, "*");
119
129
  }
120
130
  postUndoStateToIframe();
121
131
  } else if (msgType === "ping") {
132
+ logMessage("recv", "ping", payload);
122
133
  const cw = iframe.contentWindow;
123
134
  if (cw) {
124
- cw.postMessage(
125
- { type: "pong", serverClientId: awareness.clientID },
126
- "*"
127
- );
135
+ const message = { type: "pong", serverClientId: awareness.clientID };
136
+ logMessage("send", "pong", message);
137
+ cw.postMessage(message, "*");
128
138
  }
129
139
  } else if (msgType === "ydoc-update") {
140
+ logMessage("recv", "ydoc-update", payload);
130
141
  const update = new Uint8Array(payload);
131
142
  const isBaseline = event.data.isBaseline;
132
143
  const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
133
144
  Y.applyUpdate(ydoc, update, applyOrigin);
134
145
  } else if (msgType === "awareness-local-state") {
146
+ logMessage("recv", "awareness-local-state", payload);
135
147
  applyingIframeUpdate = true;
136
148
  awareness.setLocalState(event.data.state);
137
149
  applyingIframeUpdate = false;
138
150
  } else if (msgType === "awareness-update") {
151
+ logMessage("recv", "awareness-update", payload);
139
152
  applyingIframeUpdate = true;
140
153
  applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);
141
154
  applyingIframeUpdate = false;
@@ -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}\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 } = options ?? {};\n let connected = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\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 connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type, payload: payload ? Array.from(payload) : [] },\n \"*\",\n );\n }\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 cw.postMessage(state, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n postToIframe(\"ydoc-update\", update);\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 postToIframe(\"awareness-update\", update);\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 === \"init\") {\n console.log(\n `[iframe-bridge server] received init — connected=${connected}`,\n );\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n console.log(\n `[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`,\n );\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n {\n type: \"awareness-sync\",\n payload: Array.from(awarenessState),\n serverClientId: awareness.clientID,\n },\n \"*\",\n );\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n }\n } else if (msgType === \"ydoc-update\") {\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 } else if (msgType === \"awareness-local-state\") {\n applyingIframeUpdate = true;\n awareness.setLocalState(event.data.state);\n applyingIframeUpdate = false;\n } else if (msgType === \"awareness-update\") {\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\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 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/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.debug(\"[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 applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\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 connectListeners.forEach((fn) => fn());\n } else {\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 logMessage(\"send\", \"ydoc-update\", update);\n postToIframe(\"ydoc-update\", update);\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 postToIframe(\"awareness-update\", update);\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 === \"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 postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n )),\n serverClientId: awareness.clientID,\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 };\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 } else if (msgType === \"awareness-local-state\") {\n logMessage(\"recv\", \"awareness-local-state\", payload);\n applyingIframeUpdate = true;\n awareness.setLocalState(event.data.state);\n applyingIframeUpdate = false;\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 === \"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 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "y-mxgraph",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "description": "Yjs binding for draw.io (mxGraph) documents",
5
5
  "keywords": [
6
6
  "yjs",