y-mxgraph 0.6.10 → 0.6.12
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/iframe-bridge/provider.cjs +31 -12
- package/iframe-bridge/provider.cjs.map +1 -1
- package/iframe-bridge/provider.js +32 -13
- package/iframe-bridge/provider.js.map +1 -1
- package/iframe-bridge/server.cjs +18 -1
- package/iframe-bridge/server.cjs.map +1 -1
- package/iframe-bridge/server.js +18 -1
- package/iframe-bridge/server.js.map +1 -1
- package/package.json +1 -1
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';
|
|
@@ -76,11 +76,11 @@ function writeVarString(str) {
|
|
|
76
76
|
return [...writeVarUint(encoded.length), ...encoded];
|
|
77
77
|
}
|
|
78
78
|
function remapClientIdInUpdate(update, fromId, toId) {
|
|
79
|
-
const
|
|
79
|
+
const entries = [];
|
|
80
|
+
const seenClientIds = /* @__PURE__ */ new Set();
|
|
80
81
|
let pos = 0;
|
|
81
82
|
const [count, pos2] = readVarUint(update, pos);
|
|
82
83
|
pos = pos2;
|
|
83
|
-
result.push(...writeVarUint(count));
|
|
84
84
|
for (let i = 0; i < count; i++) {
|
|
85
85
|
const [clientID, pos3] = readVarUint(update, pos);
|
|
86
86
|
pos = pos3;
|
|
@@ -89,9 +89,18 @@ function remapClientIdInUpdate(update, fromId, toId) {
|
|
|
89
89
|
const [state, pos5] = readVarString(update, pos);
|
|
90
90
|
pos = pos5;
|
|
91
91
|
const mappedId = clientID === fromId ? toId : clientID;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
if (seenClientIds.has(mappedId)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
seenClientIds.add(mappedId);
|
|
96
|
+
entries.push({ clientID: mappedId, clock, state });
|
|
97
|
+
}
|
|
98
|
+
const result = [];
|
|
99
|
+
result.push(...writeVarUint(entries.length));
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
result.push(...writeVarUint(entry.clientID));
|
|
102
|
+
result.push(...writeVarUint(entry.clock));
|
|
103
|
+
result.push(...writeVarString(entry.state));
|
|
95
104
|
}
|
|
96
105
|
return new Uint8Array(result);
|
|
97
106
|
}
|
|
@@ -162,10 +171,9 @@ function createIframeBridgeProvider(ydoc, awareness$1, options) {
|
|
|
162
171
|
const localClientId = awareness$1.clientID;
|
|
163
172
|
const localChanged = changes.includes(localClientId);
|
|
164
173
|
if (!localChanged) return;
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
logMessage("send", "awareness-update", message);
|
|
174
|
+
const state = awareness$1.getLocalState();
|
|
175
|
+
const message = { type: "awareness-local-state", state };
|
|
176
|
+
logMessage("send", "awareness-local-state", state);
|
|
169
177
|
window.parent.postMessage(message, "*");
|
|
170
178
|
};
|
|
171
179
|
const onMessage = (event) => {
|
|
@@ -193,11 +201,22 @@ function createIframeBridgeProvider(ydoc, awareness$1, options) {
|
|
|
193
201
|
if (receivedServerId != null) {
|
|
194
202
|
serverClientId = receivedServerId;
|
|
195
203
|
}
|
|
204
|
+
const serverId = receivedServerId != null ? receivedServerId : serverClientId;
|
|
205
|
+
const localClientId = awareness$1.clientID;
|
|
196
206
|
applyingParentUpdate = true;
|
|
197
|
-
|
|
207
|
+
if (serverId != null && serverId !== localClientId) {
|
|
208
|
+
const remapped = remapClientIdInUpdate(
|
|
209
|
+
new Uint8Array(payload),
|
|
210
|
+
serverId,
|
|
211
|
+
localClientId
|
|
212
|
+
);
|
|
213
|
+
awareness.applyAwarenessUpdate(awareness$1, remapped, null);
|
|
214
|
+
} else {
|
|
215
|
+
awareness.applyAwarenessUpdate(awareness$1, new Uint8Array(payload), null);
|
|
216
|
+
}
|
|
198
217
|
let localUserSynced = false;
|
|
199
|
-
if (
|
|
200
|
-
const serverState = awareness$1.getStates().get(
|
|
218
|
+
if (serverId != null) {
|
|
219
|
+
const serverState = awareness$1.getStates().get(localClientId);
|
|
201
220
|
if (serverState) {
|
|
202
221
|
const serverUserAccount = (_a = serverState.user) == null ? void 0 : _a.account;
|
|
203
222
|
const serverUserName = (_b = serverState.user) == null ? void 0 : _b.name;
|
|
@@ -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 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 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, serverClientId)\n : update;\n\n const message = { type: \"awareness-update\", payload: Array.from(remapped) };\n logMessage(\"send\", \"awareness-update\", message);\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","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 entries: Array<{ clientID: number; clock: number; state: string }> = [];\n const seenClientIds = new Set<number>();\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\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 if (seenClientIds.has(mappedId)) {\n continue;\n }\n\n seenClientIds.add(mappedId);\n entries.push({ clientID: mappedId, clock, state });\n }\n\n const result: number[] = [];\n result.push(...writeVarUint(entries.length));\n for (const entry of entries) {\n result.push(...writeVarUint(entry.clientID));\n result.push(...writeVarUint(entry.clock));\n result.push(...writeVarString(entry.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 const serverId = receivedServerId ?? serverClientId;\n const localClientId = awareness.clientID;\n applyingParentUpdate = true;\n if (serverId != null && serverId !== localClientId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localClientId,\n );\n applyAwarenessUpdate(awareness, remapped, null);\n } else {\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n }\n\n // 从 server 的 awareness state 中提取 user 信息并同步到本地\n // 避免 binding 生成随机用户信息后通过 remap 覆盖父页面的真实用户\n let localUserSynced = false;\n if (serverId != null) {\n const serverState = awareness.getStates().get(localClientId);\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 {
|
|
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;
|
|
@@ -57,11 +57,11 @@ function writeVarString(str) {
|
|
|
57
57
|
return [...writeVarUint(encoded.length), ...encoded];
|
|
58
58
|
}
|
|
59
59
|
function remapClientIdInUpdate(update, fromId, toId) {
|
|
60
|
-
const
|
|
60
|
+
const entries = [];
|
|
61
|
+
const seenClientIds = /* @__PURE__ */ new Set();
|
|
61
62
|
let pos = 0;
|
|
62
63
|
const [count, pos2] = readVarUint(update, pos);
|
|
63
64
|
pos = pos2;
|
|
64
|
-
result.push(...writeVarUint(count));
|
|
65
65
|
for (let i = 0; i < count; i++) {
|
|
66
66
|
const [clientID, pos3] = readVarUint(update, pos);
|
|
67
67
|
pos = pos3;
|
|
@@ -70,9 +70,18 @@ function remapClientIdInUpdate(update, fromId, toId) {
|
|
|
70
70
|
const [state, pos5] = readVarString(update, pos);
|
|
71
71
|
pos = pos5;
|
|
72
72
|
const mappedId = clientID === fromId ? toId : clientID;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
if (seenClientIds.has(mappedId)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
seenClientIds.add(mappedId);
|
|
77
|
+
entries.push({ clientID: mappedId, clock, state });
|
|
78
|
+
}
|
|
79
|
+
const result = [];
|
|
80
|
+
result.push(...writeVarUint(entries.length));
|
|
81
|
+
for (const entry of entries) {
|
|
82
|
+
result.push(...writeVarUint(entry.clientID));
|
|
83
|
+
result.push(...writeVarUint(entry.clock));
|
|
84
|
+
result.push(...writeVarString(entry.state));
|
|
76
85
|
}
|
|
77
86
|
return new Uint8Array(result);
|
|
78
87
|
}
|
|
@@ -143,10 +152,9 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
143
152
|
const localClientId = awareness.clientID;
|
|
144
153
|
const localChanged = changes.includes(localClientId);
|
|
145
154
|
if (!localChanged) return;
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
logMessage("send", "awareness-update", message);
|
|
155
|
+
const state = awareness.getLocalState();
|
|
156
|
+
const message = { type: "awareness-local-state", state };
|
|
157
|
+
logMessage("send", "awareness-local-state", state);
|
|
150
158
|
window.parent.postMessage(message, "*");
|
|
151
159
|
};
|
|
152
160
|
const onMessage = (event) => {
|
|
@@ -174,11 +182,22 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
174
182
|
if (receivedServerId != null) {
|
|
175
183
|
serverClientId = receivedServerId;
|
|
176
184
|
}
|
|
185
|
+
const serverId = receivedServerId != null ? receivedServerId : serverClientId;
|
|
186
|
+
const localClientId = awareness.clientID;
|
|
177
187
|
applyingParentUpdate = true;
|
|
178
|
-
|
|
188
|
+
if (serverId != null && serverId !== localClientId) {
|
|
189
|
+
const remapped = remapClientIdInUpdate(
|
|
190
|
+
new Uint8Array(payload),
|
|
191
|
+
serverId,
|
|
192
|
+
localClientId
|
|
193
|
+
);
|
|
194
|
+
applyAwarenessUpdate(awareness, remapped, null);
|
|
195
|
+
} else {
|
|
196
|
+
applyAwarenessUpdate(awareness, new Uint8Array(payload), null);
|
|
197
|
+
}
|
|
179
198
|
let localUserSynced = false;
|
|
180
|
-
if (
|
|
181
|
-
const serverState = awareness.getStates().get(
|
|
199
|
+
if (serverId != null) {
|
|
200
|
+
const serverState = awareness.getStates().get(localClientId);
|
|
182
201
|
if (serverState) {
|
|
183
202
|
const serverUserAccount = (_a = serverState.user) == null ? void 0 : _a.account;
|
|
184
203
|
const serverUserName = (_b = serverState.user) == null ? void 0 : _b.name;
|
|
@@ -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 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 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, serverClientId)\n : update;\n\n const message = { type: \"awareness-update\", payload: Array.from(remapped) };\n logMessage(\"send\", \"awareness-update\", message);\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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"}
|
|
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 entries: Array<{ clientID: number; clock: number; state: string }> = [];\n const seenClientIds = new Set<number>();\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\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 if (seenClientIds.has(mappedId)) {\n continue;\n }\n\n seenClientIds.add(mappedId);\n entries.push({ clientID: mappedId, clock, state });\n }\n\n const result: number[] = [];\n result.push(...writeVarUint(entries.length));\n for (const entry of entries) {\n result.push(...writeVarUint(entry.clientID));\n result.push(...writeVarUint(entry.clock));\n result.push(...writeVarString(entry.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 const serverId = receivedServerId ?? serverClientId;\n const localClientId = awareness.clientID;\n applyingParentUpdate = true;\n if (serverId != null && serverId !== localClientId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localClientId,\n );\n applyAwarenessUpdate(awareness, remapped, null);\n } else {\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n }\n\n // 从 server 的 awareness state 中提取 user 信息并同步到本地\n // 避免 binding 生成随机用户信息后通过 remap 覆盖父页面的真实用户\n let localUserSynced = false;\n if (serverId != null) {\n const serverState = awareness.getStates().get(localClientId);\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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"}
|
package/iframe-bridge/server.cjs
CHANGED
|
@@ -87,6 +87,14 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
|
87
87
|
cw.postMessage(message, "*");
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
+
function postObjectToIframe(message) {
|
|
91
|
+
const type = message.type;
|
|
92
|
+
logMessage("send", type, message);
|
|
93
|
+
const cw = iframe.contentWindow;
|
|
94
|
+
if (cw) {
|
|
95
|
+
cw.postMessage(message, "*");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
90
98
|
const onYdocUpdate = (update, origin) => {
|
|
91
99
|
if (origin === IFRAME_ORIGIN) return;
|
|
92
100
|
logMessage("send", "ydoc-update", update);
|
|
@@ -120,7 +128,11 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
|
120
128
|
if (changes.length === 0) return;
|
|
121
129
|
const update = awareness.encodeAwarenessUpdate(awareness$1, changes);
|
|
122
130
|
logMessage("send", "awareness-update", update);
|
|
123
|
-
|
|
131
|
+
postObjectToIframe({
|
|
132
|
+
type: "awareness-update",
|
|
133
|
+
payload: Array.from(update),
|
|
134
|
+
serverClientId: awareness$1.clientID
|
|
135
|
+
});
|
|
124
136
|
};
|
|
125
137
|
const onMessage = (event) => {
|
|
126
138
|
if (event.source !== iframe.contentWindow) return;
|
|
@@ -161,6 +173,11 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
|
161
173
|
const isBaseline = event.data.isBaseline;
|
|
162
174
|
const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
|
|
163
175
|
Y__namespace.applyUpdate(ydoc, update, applyOrigin);
|
|
176
|
+
} else if (msgType === "awareness-local-state") {
|
|
177
|
+
logMessage("recv", "awareness-local-state", payload);
|
|
178
|
+
applyingIframeUpdate = true;
|
|
179
|
+
awareness$1.setLocalState(event.data.state);
|
|
180
|
+
applyingIframeUpdate = false;
|
|
164
181
|
} else if (msgType === "awareness-update") {
|
|
165
182
|
logMessage("recv", "awareness-update", payload);
|
|
166
183
|
applyingIframeUpdate = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs","sources":["../../../iframe-bridge/src/origin.ts","../../../iframe-bridge/src/server.ts"],"sourcesContent":["/**\n * iframe bridge 内部变更的 origin 标识。\n * 当 provider 端产生 ydoc-update 时,使用此 origin 标记,\n * 以便 server 端的 UndoManager 能正确追踪来自 iframe 的变更。\n */\nexport const IFRAME_ORIGIN: object = {};\n\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.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-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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"}
|
|
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 postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"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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"}
|
package/iframe-bridge/server.js
CHANGED
|
@@ -68,6 +68,14 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
68
68
|
cw.postMessage(message, "*");
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
+
function postObjectToIframe(message) {
|
|
72
|
+
const type = message.type;
|
|
73
|
+
logMessage("send", type, message);
|
|
74
|
+
const cw = iframe.contentWindow;
|
|
75
|
+
if (cw) {
|
|
76
|
+
cw.postMessage(message, "*");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
71
79
|
const onYdocUpdate = (update, origin) => {
|
|
72
80
|
if (origin === IFRAME_ORIGIN) return;
|
|
73
81
|
logMessage("send", "ydoc-update", update);
|
|
@@ -101,7 +109,11 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
101
109
|
if (changes.length === 0) return;
|
|
102
110
|
const update = encodeAwarenessUpdate(awareness, changes);
|
|
103
111
|
logMessage("send", "awareness-update", update);
|
|
104
|
-
|
|
112
|
+
postObjectToIframe({
|
|
113
|
+
type: "awareness-update",
|
|
114
|
+
payload: Array.from(update),
|
|
115
|
+
serverClientId: awareness.clientID
|
|
116
|
+
});
|
|
105
117
|
};
|
|
106
118
|
const onMessage = (event) => {
|
|
107
119
|
if (event.source !== iframe.contentWindow) return;
|
|
@@ -142,6 +154,11 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
142
154
|
const isBaseline = event.data.isBaseline;
|
|
143
155
|
const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
|
|
144
156
|
Y.applyUpdate(ydoc, update, applyOrigin);
|
|
157
|
+
} else if (msgType === "awareness-local-state") {
|
|
158
|
+
logMessage("recv", "awareness-local-state", payload);
|
|
159
|
+
applyingIframeUpdate = true;
|
|
160
|
+
awareness.setLocalState(event.data.state);
|
|
161
|
+
applyingIframeUpdate = false;
|
|
145
162
|
} else if (msgType === "awareness-update") {
|
|
146
163
|
logMessage("recv", "awareness-update", payload);
|
|
147
164
|
applyingIframeUpdate = true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../../iframe-bridge/src/origin.ts","../../../iframe-bridge/src/server.ts"],"sourcesContent":["/**\n * iframe bridge 内部变更的 origin 标识。\n * 当 provider 端产生 ydoc-update 时,使用此 origin 标记,\n * 以便 server 端的 UndoManager 能正确追踪来自 iframe 的变更。\n */\nexport const IFRAME_ORIGIN: object = {};\n\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.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-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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"}
|
|
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 postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"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":";;AAKO,MAAM,gBAAwB,CAAA;AAM9B,MAAM,kBAA0B,CAAA;ACUhC,SAAS,yBACd,QACA,MACA,WACA,SACoB;AACpB,QAAM,EAAE,aAAa,QAAQ,MAAA,IAAU,4BAAW,CAAA;AAClD,QAAM,MAAM,QACR,IAAI,SAAoB,QAAQ,MAAM,0BAA0B,GAAG,IAAI,IACvE,MAAM;AAEV,WAAS,cAAc,SAAkB;AACvC,QAAI,mBAAmB,YAAY;AACjC,aAAO,EAAE,OAAO,QAAQ,WAAA;AAAA,IAC1B;AACA,QAAI,MAAM,QAAQ,OAAO,KAAK,QAAQ,MAAM,CAAC,SAAS,OAAO,SAAS,QAAQ,GAAG;AAC/E,aAAO,EAAE,OAAO,QAAQ,OAAA;AAAA,IAC1B;AACA,WAAO;AAAA,EACT;AAEA,WAAS,WAAW,WAA4B,MAAc,SAAmB;AAC/E,QAAI,CAAC,MAAO;AACZ,QAAI,WAAW,MAAM,cAAc,OAAO,CAAC;AAAA,EAC7C;AAEA,MAAI,YAAY;AAChB,MAAI,uBAAuB;AAC3B,QAAM,uCAAuB,IAAA;AAC7B,QAAM,0CAA0B,IAAA;AAChC,MAAI,sBAAsB;AAE1B,WAAS,6BAA6B;AACpC,QAAI,CAAC,YAAa;AAClB,QAAI;AACF,UAAI,OAAQ,YAAoB,qBAAqB,YAAY;AAC9D,oBAAoB,iBAAiB,aAAa;AACnD,8BAAsB;AAAA,MACxB;AAAA,IACF,SAAS,OAAO;AACd,cAAQ;AAAA,QACN;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAEA,WAAS,gCAAgC;AACvC,QAAI,CAAC,eAAe,CAAC,oBAAqB;AAC1C,QAAI;AACF,UAAI,OAAQ,YAAoB,wBAAwB,YAAY;AACjE,oBAAoB,oBAAoB,aAAa;AAAA,MACxD;AAAA,IACF,SAAS,OAAO;AACd,cAAQ;AAAA,QACN;AAAA,QACA;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAEA,6BAAA;AAEA,WAAS,aAAa,OAAgB;AACpC,QAAI,cAAc,MAAO;AACzB,gBAAY;AACZ,QAAI,OAAO;AACT,uBAAiB,QAAQ,CAAC,OAAO,GAAA,CAAI;AAAA,IACvC,OAAO;AACL,0BAAoB,QAAQ,CAAC,OAAO,GAAA,CAAI;AAAA,IAC1C;AAAA,EACF;AAEA,WAAS,aAAa,MAAc,SAAsB;AACxD,UAAM,KAAK,OAAO;AAClB,UAAM,UAAU,EAAE,MAAM,SAAS,UAAU,MAAM,KAAK,OAAO,IAAI,GAAC;AAClE,eAAW,QAAQ,MAAM,OAAO;AAChC,QAAI,IAAI;AACN,SAAG,YAAY,SAAS,GAAG;AAAA,IAC7B;AAAA,EACF;AAEA,WAAS,mBAAmB,SAAkC;AAC5D,UAAM,OAAO,QAAQ;AACrB,eAAW,QAAQ,MAAM,OAAO;AAChC,UAAM,KAAK,OAAO;AAClB,QAAI,IAAI;AACN,SAAG,YAAY,SAAS,GAAG;AAAA,IAC7B;AAAA,EACF;AAEA,QAAM,eAAe,CAAC,QAAoB,WAAoB;AAC5D,QAAI,WAAW,cAAe;AAC9B,eAAW,QAAQ,eAAe,MAAM;AACxC,iBAAa,eAAe,MAAM;AAAA,EACpC;AAEA,WAAS,wBAAwB;AAtHnC,QAAA,IAAA;AAuHI,QAAI,CAAC,YAAa;AAClB,UAAM,YAAa,YAAoB;AACvC,UAAM,YAAa,YAAoB;AACvC,UAAM,KAAK,OAAO;AAClB,QAAI,IAAI;AACN,YAAM,QAAQ;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,YAAY,QAAA;AAAA,QACrB,SAAS,YAAY,QAAA;AAAA,QACrB,gBAAe,KAAA,aAAA,OAAA,SAAA,UAAW,WAAX,OAAA,KAAqB;AAAA,QACpC,gBAAe,KAAA,aAAA,OAAA,SAAA,UAAW,WAAX,OAAA,KAAqB;AAAA,MAAA;AAEtC,iBAAW,QAAQ,cAAc,KAAK;AACtC,SAAG,YAAY,OAAO,GAAG;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,oBAAoB,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA,MAKI;AACJ,QAAI,qBAAsB;AAC1B,UAAM,UAAU,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO;AACjD,QAAI,QAAQ,WAAW,EAAG;AAI1B,UAAM,SAAS,sBAAsB,WAAW,OAAO;AACvD,eAAW,QAAQ,oBAAoB,MAAM;AAC7C,uBAAmB;AAAA,MACjB,MAAM;AAAA,MACN,SAAS,MAAM,KAAK,MAAM;AAAA,MAC1B,gBAAgB,UAAU;AAAA,IAAA,CAC3B;AAAA,EACH;AAEA,QAAM,YAAY,CAAC,UAAwB;AACzC,QAAI,MAAM,WAAW,OAAO,cAAe;AAE3C,UAAM,EAAE,MAAM,SAAS,QAAA,IAAY,MAAM;AAEzC,QAAI,YAAY,QAAQ;AACtB,iBAAW,QAAQ,QAAQ,OAAO;AAClC,UAAI,CAAC,WAAW;AACd,qBAAa,IAAI;AAAA,MACnB;AACA,YAAM,WAAW,EAAE,oBAAoB,IAAI;AAC3C,iBAAW,QAAQ,aAAa,EAAE,OAAO,SAAS,QAAQ;AAC1D,mBAAa,aAAa,IAAI,WAAW,MAAM,KAAK,QAAQ,CAAC,CAAC;AAE9D,YAAM,KAAK,OAAO;AAClB,UAAI,IAAI;AACN,cAAM,UAAU;AAAA,UACd,MAAM;AAAA,UACN,SAAS,MAAM,KAAK;AAAA,YAClB;AAAA,YACA,MAAM,KAAK,UAAU,UAAA,EAAY,MAAM;AAAA,UAAA,CACxC;AAAA,UACD,gBAAgB,UAAU;AAAA,QAAA;AAE5B,mBAAW,QAAQ,kBAAkB,OAAO;AAC5C,WAAG,YAAY,SAAS,GAAG;AAAA,MAC7B;AAEA,4BAAA;AAAA,IACF,WAAW,YAAY,QAAQ;AAC7B,iBAAW,QAAQ,QAAQ,OAAO;AAClC,YAAM,KAAK,OAAO;AAClB,UAAI,IAAI;AACN,cAAM,UAAU,EAAE,MAAM,QAAQ,gBAAgB,UAAU,SAAA;AAC1D,mBAAW,QAAQ,QAAQ,OAAO;AAClC,WAAG,YAAY,SAAS,GAAG;AAAA,MAC7B;AAAA,IACF,WAAW,YAAY,eAAe;AACpC,iBAAW,QAAQ,eAAe,OAAO;AACzC,YAAM,SAAS,IAAI,WAAW,OAAO;AACrC,YAAM,aAAa,MAAM,KAAK;AAE9B,YAAM,cAAc,aAAa,kBAAkB;AACnD,QAAE,YAAY,MAAM,QAAQ,WAAW;AAAA,IAEzC,WAAW,YAAY,yBAAyB;AAC9C,iBAAW,QAAQ,yBAAyB,OAAO;AACnD,6BAAuB;AACvB,gBAAU,cAAc,MAAM,KAAK,KAAK;AACxC,6BAAuB;AAAA,IACzB,WAAW,YAAY,oBAAoB;AACzC,iBAAW,QAAQ,oBAAoB,OAAO;AAE9C,6BAAuB;AACvB,2BAAqB,WAAW,IAAI,WAAW,OAAO,GAAG,aAAa;AACtE,6BAAuB;AAAA,IACzB,WAAW,YAAY,UAAU,aAAa;AAC5C,kBAAY,KAAA;AACZ,4BAAA;AAAA,IACF,WAAW,YAAY,UAAU,aAAa;AAC5C,kBAAY,KAAA;AACZ,4BAAA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AACzB,0BAAA;AAAA,EACF;AAEA,QAAM,iBAAiB,MAAM;AAC3B,0BAAA;AAAA,EACF;AAEA,QAAM,mBAAmB,MAAM;AAC7B,0BAAA;AAAA,EACF;AAEA,OAAK,GAAG,UAAU,YAAY;AAC9B,YAAU,GAAG,UAAU,iBAAiB;AACxC,SAAO,iBAAiB,WAAW,SAAS;AAC5C,MAAI,aAAa;AACf,gBAAY,GAAG,qBAAqB,YAAY;AAChD,gBAAY,GAAG,iBAAiB,cAAc;AAC9C,gBAAY,GAAG,oBAAoB,gBAAgB;AAAA,EACrD;AAEA,SAAO;AAAA,IACL,IAAI,YAAY;AACd,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAgB;AACxB,uBAAiB,IAAI,EAAE;AACvB,aAAO,MAAM,iBAAiB,OAAO,EAAE;AAAA,IACzC;AAAA,IACA,aAAa,IAAgB;AAC3B,0BAAoB,IAAI,EAAE;AAC1B,aAAO,MAAM,oBAAoB,OAAO,EAAE;AAAA,IAC5C;AAAA,IACA,GAAG,OAAiC,IAAgB;AAClD,UAAI,UAAU,WAAW;AACvB,yBAAiB,IAAI,EAAE;AACvB,eAAO,MAAM,iBAAiB,OAAO,EAAE;AAAA,MACzC,OAAO;AACL,4BAAoB,IAAI,EAAE;AAC1B,eAAO,MAAM,oBAAoB,OAAO,EAAE;AAAA,MAC5C;AAAA,IACF;AAAA,IACA,SAAS,MAAM;AACb,mBAAa,KAAK;AAClB,mBAAa,YAAY;AACzB,WAAK,IAAI,UAAU,YAAY;AAC/B,gBAAU,IAAI,UAAU,iBAAiB;AACzC,aAAO,oBAAoB,WAAW,SAAS;AAC/C,UAAI,aAAa;AACf,oBAAY,IAAI,qBAAqB,YAAY;AACjD,oBAAY,IAAI,iBAAiB,cAAc;AAC/C,oBAAY,IAAI,oBAAoB,gBAAgB;AACpD,sCAAA;AAAA,MACF;AACA,uBAAiB,MAAA;AACjB,0BAAoB,MAAA;AAAA,IACtB;AAAA,EAAA;AAEJ;"}
|