y-mxgraph 0.8.4 → 0.8.5

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.
@@ -152,6 +152,11 @@ function createIframeBridgeProvider(ydoc, options) {
152
152
  let currentMxLike = null;
153
153
  let pendingUndoState = null;
154
154
  let connected = false;
155
+ let forceFullSync = false;
156
+ const MAX_QUEUE_SIZE = 1e3;
157
+ const pendingYdocUpdates = [];
158
+ let seq = 0;
159
+ const unackedYdocUpdates = /* @__PURE__ */ new Map();
155
160
  let initRetryTimer = null;
156
161
  const connectListeners = /* @__PURE__ */ new Set();
157
162
  const disconnectListeners = /* @__PURE__ */ new Set();
@@ -184,11 +189,14 @@ function createIframeBridgeProvider(ydoc, options) {
184
189
  const id = getEffectiveClientId();
185
190
  if (state === null) {
186
191
  localStates.delete(id);
192
+ const update = { added: [], updated: [], removed: [id] };
193
+ updateHandlers.forEach((handler) => handler(update));
187
194
  } else {
195
+ const existed = localStates.has(id);
188
196
  localStates.set(id, state);
197
+ const update = { added: !existed ? [id] : [], updated: [id], removed: [] };
198
+ updateHandlers.forEach((handler) => handler(update));
189
199
  }
190
- const update = { added: state && !localStates.has(id) ? [id] : [], updated: state ? [id] : [], removed: state === null ? [id] : [] };
191
- updateHandlers.forEach((handler) => handler(update));
192
200
  },
193
201
  setLocalStateField(field, value) {
194
202
  const id = getEffectiveClientId();
@@ -248,10 +256,23 @@ function createIframeBridgeProvider(ydoc, options) {
248
256
  if (connected === value) return;
249
257
  connected = value;
250
258
  if (value) {
259
+ forceFullSync = false;
251
260
  snapshotLocalAwarenessState();
261
+ while (pendingYdocUpdates.length > 0) {
262
+ const { update, isBaseline } = pendingYdocUpdates.shift();
263
+ const seqNum = ++seq;
264
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
265
+ window.parent.postMessage(message, "*");
266
+ unackedYdocUpdates.set(seqNum, { update, isBaseline });
267
+ }
268
+ for (const [savedSeq, { update, isBaseline }] of unackedYdocUpdates) {
269
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: savedSeq };
270
+ window.parent.postMessage(message, "*");
271
+ }
252
272
  connectListeners.forEach((fn) => fn());
253
273
  } else {
254
274
  lastLocalAwarenessSnapshot = null;
275
+ unackedYdocUpdates.clear();
255
276
  disconnectListeners.forEach((fn) => fn());
256
277
  }
257
278
  }
@@ -259,6 +280,13 @@ function createIframeBridgeProvider(ydoc, options) {
259
280
  if (initRetryTimer) {
260
281
  clearInterval(initRetryTimer);
261
282
  }
283
+ if (!forceFullSync && pendingYdocUpdates.length > 0) {
284
+ const updates = pendingYdocUpdates.splice(0);
285
+ parentPostMessage({
286
+ type: "ydoc-pending-updates",
287
+ payload: updates.map((u) => ({ update: Array.from(u.update), isBaseline: u.isBaseline }))
288
+ });
289
+ }
262
290
  parentPostMessage({ type: "init" });
263
291
  initRetryTimer = setInterval(() => {
264
292
  if (!connected) {
@@ -269,9 +297,20 @@ function createIframeBridgeProvider(ydoc, options) {
269
297
  const onYdocUpdate = (update, origin) => {
270
298
  if (applyingParentUpdate) return;
271
299
  const isBaseline = origin === null || origin === void 0;
272
- const message = { type: "ydoc-update", payload: Array.from(update), isBaseline };
300
+ if (!connected) {
301
+ if (pendingYdocUpdates.length >= MAX_QUEUE_SIZE) {
302
+ forceFullSync = true;
303
+ pendingYdocUpdates.length = 0;
304
+ console.warn("[iframe-bridge] queue full, forcing full sync on reconnect");
305
+ }
306
+ pendingYdocUpdates.push({ update, isBaseline });
307
+ return;
308
+ }
309
+ const seqNum = ++seq;
310
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
273
311
  logMessage("send", "ydoc-update", message);
274
312
  window.parent.postMessage(message, "*");
313
+ unackedYdocUpdates.set(seqNum, { update, isBaseline });
275
314
  };
276
315
  const onAwarenessUpdate = ({
277
316
  added,
@@ -326,8 +365,16 @@ function createIframeBridgeProvider(ydoc, options) {
326
365
  };
327
366
  const onMessage = (event) => {
328
367
  if (event.source !== window.parent) return;
368
+ if (!event.data || typeof event.data !== "object") return;
329
369
  const { type, payload, serverClientId: receivedServerId } = event.data;
330
370
  logMessage("recv", type, payload);
371
+ if (type === "ydoc-update-ack") {
372
+ const ackSeq = event.data.seq;
373
+ if (ackSeq != null) {
374
+ unackedYdocUpdates.delete(ackSeq);
375
+ }
376
+ return;
377
+ }
331
378
  if (type === "pong" && receivedServerId != null) {
332
379
  serverClientId = receivedServerId;
333
380
  return;
@@ -379,6 +426,7 @@ function createIframeBridgeProvider(ydoc, options) {
379
426
  const parsedStates = parseAwarenessPayload(new Uint8Array(payload));
380
427
  applyingParentUpdate = true;
381
428
  const changedClientIds = [];
429
+ const removedClientIds = [];
382
430
  for (const [id, state] of parsedStates) {
383
431
  const existed = localStates.has(id);
384
432
  const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);
@@ -387,8 +435,14 @@ function createIframeBridgeProvider(ydoc, options) {
387
435
  changedClientIds.push(id);
388
436
  }
389
437
  }
390
- if (changedClientIds.length > 0) {
391
- const update = { added: [], updated: changedClientIds, removed: [] };
438
+ for (const [id] of localStates) {
439
+ if (!parsedStates.has(id)) {
440
+ localStates.delete(id);
441
+ removedClientIds.push(id);
442
+ }
443
+ }
444
+ if (changedClientIds.length > 0 || removedClientIds.length > 0) {
445
+ const update = { added: [], updated: changedClientIds, removed: removedClientIds };
392
446
  updateHandlers.forEach((handler) => handler(update));
393
447
  }
394
448
  applyingParentUpdate = false;
@@ -574,6 +628,11 @@ function createIframeBridgeProvider(ydoc, options) {
574
628
  return cleanup;
575
629
  },
576
630
  destroy: () => {
631
+ while (pendingYdocUpdates.length > 0) {
632
+ const { update, isBaseline } = pendingYdocUpdates.shift();
633
+ window.parent.postMessage({ type: "ydoc-update", payload: Array.from(update), isBaseline }, "*");
634
+ }
635
+ unackedYdocUpdates.clear();
577
636
  ydoc.off("update", onYdocUpdate);
578
637
  if (useExternalAwareness) {
579
638
  awareness$1.off("update", onAwarenessUpdate);
@@ -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} from \"y-protocols/awareness\";\nconst IFRAME_BRIDGE_STATE_KEYS = new Set([\"cursor\", \"selection\"]);\n\nfunction isIframeBridgeStateKey(key: string): boolean {\n return IFRAME_BRIDGE_STATE_KEYS.has(key);\n}\n\nfunction getAwarenessStateFieldChanges(\n prev: Record<string, unknown> | null,\n next: Record<string, unknown> | null,\n): Array<{ key: string; value: unknown }> {\n const keys = new Set([\n ...Object.keys(prev ?? {}),\n ...Object.keys(next ?? {}),\n ]);\n const changes: Array<{ key: string; value: unknown }> = [];\n for (const key of keys) {\n const prevValue = prev?.[key];\n const nextValue = next?.[key];\n if (JSON.stringify(prevValue) !== JSON.stringify(nextValue)) {\n changes.push({ key, value: nextValue });\n }\n }\n return changes;\n}\n\n/**\n * Awareness-like 接口,只需要支持本地状态管理。\n * 用于 iframe-bridge provider 不需要与父页面同步 awareness 的场景。\n */\nexport interface AwarenessLike {\n readonly clientID: number;\n readonly states: Map<number, Record<string, unknown>>;\n getStates(): Map<number, Record<string, unknown>>;\n getLocalState(): Record<string, unknown> | null;\n setLocalState(state: Record<string, unknown> | null): void;\n setLocalStateField(field: string, value: unknown): void;\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n}\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 awareness?: Awareness;\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n awareness: Awareness;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n setLocalFields: (fields: Record<string, unknown>) => 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\nfunction parseAwarenessPayload(data: Uint8Array): Map<number, Record<string, unknown>> {\n const result = new Map<number, Record<string, unknown>>();\n let pos = 0;\n const [count, pos2] = readVarUint(data, pos);\n pos = pos2;\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(data, pos);\n pos = pos3;\n const [, pos4] = readVarUint(data, pos);\n pos = pos4;\n const [stateStr, pos5] = readVarString(data, pos);\n pos = pos5;\n\n if (stateStr) {\n try {\n result.set(clientID, JSON.parse(stateStr));\n } catch {\n // no-op\n }\n }\n }\n return result;\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { awareness: externalAwareness, 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 pendingUndoState: {\n undoStackSize?: number;\n redoStackSize?: number;\n } | 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 let lastLocalAwarenessSnapshot: Record<string, unknown> | null = null;\n\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge provider]\", ...args)\n : () => undefined;\n\n const useExternalAwareness = !!externalAwareness;\n let awareness: Awareness | AwarenessLike;\n const localStates = new Map<number, Record<string, unknown>>();\n const localClientId = Math.floor(Math.random() * 2147483647) + 1;\n const updateHandlers = new Set<(update: { added: number[]; updated: number[]; removed: number[] }) => void>();\n\n function createAwarenessLike(): AwarenessLike {\n function getEffectiveClientId() {\n return serverClientId ?? localClientId;\n }\n\n return {\n get clientID() {\n return getEffectiveClientId();\n },\n get states() {\n return localStates;\n },\n getStates() {\n return new Map(localStates);\n },\n getLocalState() {\n return localStates.get(getEffectiveClientId()) ?? null;\n },\n setLocalState(state: Record<string, unknown> | null) {\n const id = getEffectiveClientId();\n if (state === null) {\n localStates.delete(id);\n } else {\n localStates.set(id, state);\n }\n const update = { added: state && !localStates.has(id) ? [id] : [], updated: state ? [id] : [], removed: state === null ? [id] : [] };\n updateHandlers.forEach(handler => handler(update));\n },\n setLocalStateField(field: string, value: unknown) {\n const id = getEffectiveClientId();\n const current = localStates.get(id) || {};\n const newState = { ...current, [field]: value };\n localStates.set(id, newState);\n postSetLocalStateToParent(field, value);\n const update = { added: [], updated: [id], removed: [] };\n updateHandlers.forEach(handler => handler(update));\n },\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.add(handler);\n }\n },\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.delete(handler);\n }\n },\n };\n }\n\n if (externalAwareness) {\n awareness = externalAwareness;\n } else {\n awareness = createAwarenessLike();\n }\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 snapshotLocalAwarenessState() {\n const state = awareness.getLocalState();\n lastLocalAwarenessSnapshot = state ? { ...state } : null;\n }\n\n function postSetLocalStateToParent(key: string, value: unknown) {\n if (!connected || !isIframeBridgeStateKey(key)) return;\n const message = { type: \"set-local-state\", key, value };\n logMessage(\"send\", \"set-local-state\", message);\n window.parent.postMessage(message, \"*\");\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 snapshotLocalAwarenessState();\n connectListeners.forEach((fn) => fn());\n } else {\n lastLocalAwarenessSnapshot = null;\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) {\n return;\n }\n if (!connected) {\n return;\n }\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const nextState = awareness.getLocalState();\n const fieldChanges = getAwarenessStateFieldChanges(\n lastLocalAwarenessSnapshot,\n nextState,\n );\n lastLocalAwarenessSnapshot = nextState ? { ...nextState } : null;\n for (const { key, value } of fieldChanges) {\n postSetLocalStateToParent(key, value);\n }\n };\n\n const syncUndoStateFromServer = (\n mxLike: MxLike,\n data: { undoStackSize?: number; redoStackSize?: number },\n ) => {\n const { undoStackSize, redoStackSize } = data;\n const oldIndex = mxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n applyingParentUpdate = true;\n mxLike.history = new Array(newTotal).fill({});\n mxLike.indexOfNextAdd = newIndex;\n if (newTotal === 0) {\n mxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n mxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n applyingParentUpdate = false;\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 const prevLocalId = localClientId;\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n if (useExternalAwareness === false && receivedServerId != null && prevLocalId !== receivedServerId) {\n const tempState = localStates.get(prevLocalId);\n if (tempState) {\n localStates.delete(prevLocalId);\n localStates.set(receivedServerId, tempState);\n }\n }\n }\n\n if (useExternalAwareness) {\n const serverId = receivedServerId ?? serverClientId;\n const localId = awareness.clientID;\n \n applyingParentUpdate = true;\n if (serverId != null && serverId !== localId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localId,\n );\n \n if (type === \"awareness-sync\") {\n (awareness as Awareness).meta.delete(localId);\n awareness.setLocalState(null);\n }\n \n applyAwarenessUpdate(awareness as Awareness, remapped, null);\n } else {\n applyAwarenessUpdate(awareness as Awareness, new Uint8Array(payload), null);\n }\n applyingParentUpdate = false;\n } else {\n const parsedStates = parseAwarenessPayload(new Uint8Array(payload));\n applyingParentUpdate = true;\n const changedClientIds: number[] = [];\n for (const [id, state] of parsedStates) {\n const existed = localStates.has(id);\n const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);\n localStates.set(id, state);\n if (changed) {\n changedClientIds.push(id);\n }\n }\n if (changedClientIds.length > 0) {\n const update = { added: [], updated: changedClientIds, removed: [] };\n updateHandlers.forEach(handler => handler(update));\n }\n applyingParentUpdate = false;\n }\n } else if (type === \"undo-state\") {\n if (!currentMxLike) {\n pendingUndoState = event.data;\n } else {\n syncUndoStateFromServer(currentMxLike, event.data);\n }\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n // 只有真正的 y-protocols Awareness 实例才有 .on() 方法\n // AwarenessLike(内部创建的)通过 updateHandlers 管理回调\n if (useExternalAwareness) {\n (awareness as Awareness).on(\"update\", onAwarenessUpdate);\n }\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 get awareness() {\n return awareness as Awareness;\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 setLocalFields(fields: Record<string, unknown>) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n // 通知父页面更新本地字段,父页面可选择是否处理\n if (connected) {\n const message = { type: \"set-local-fields\", fields };\n logMessage(\"send\", \"set-local-fields\", fields);\n window.parent.postMessage(message, \"*\");\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 // bindUndoManager 已安装:不替换 editor.undoManager,只委托 undo/redo\n if (originUndoManager && \"_y\" in originUndoManager) {\n const mxLike = originUndoManager as MxLike;\n const origUndo = mxLike.undo.bind(mxLike);\n const origRedo = mxLike.redo.bind(mxLike);\n const origCanUndo = mxLike.canUndo.bind(mxLike);\n const origCanRedo = mxLike.canRedo.bind(mxLike);\n currentMxLike = mxLike;\n mxLike.undo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n } else {\n origUndo();\n }\n };\n mxLike.redo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n } else {\n origRedo();\n }\n };\n mxLike.canUndo = () => mxLike.indexOfNextAdd > 0;\n mxLike.canRedo = () => mxLike.indexOfNextAdd < mxLike.history.length;\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n const cleanup = () => {\n mxLike.undo = origUndo;\n mxLike.redo = origRedo;\n mxLike.canUndo = origCanUndo;\n mxLike.canRedo = origCanRedo;\n currentMxLike = null;\n pendingUndoState = null;\n };\n currentCleanup = cleanup;\n return cleanup;\n }\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 unknown as DrawioEditor[\"undoManager\"];\n editor.undoListener = function () {};\n\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n pendingUndoState = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n if (useExternalAwareness) {\n (awareness as Awareness).off(\"update\", onAwarenessUpdate);\n }\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","localClientId","Y","applyAwarenessUpdate","mxLike","cleanup"],"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} from \"y-protocols/awareness\";\nconst IFRAME_BRIDGE_STATE_KEYS = new Set([\"cursor\", \"selection\"]);\n\nfunction isIframeBridgeStateKey(key: string): boolean {\n return IFRAME_BRIDGE_STATE_KEYS.has(key);\n}\n\nfunction getAwarenessStateFieldChanges(\n prev: Record<string, unknown> | null,\n next: Record<string, unknown> | null,\n): Array<{ key: string; value: unknown }> {\n const keys = new Set([\n ...Object.keys(prev ?? {}),\n ...Object.keys(next ?? {}),\n ]);\n const changes: Array<{ key: string; value: unknown }> = [];\n for (const key of keys) {\n const prevValue = prev?.[key];\n const nextValue = next?.[key];\n if (JSON.stringify(prevValue) !== JSON.stringify(nextValue)) {\n changes.push({ key, value: nextValue });\n }\n }\n return changes;\n}\n\n/**\n * Awareness-like 接口,只需要支持本地状态管理。\n * 用于 iframe-bridge provider 不需要与父页面同步 awareness 的场景。\n */\nexport interface AwarenessLike {\n readonly clientID: number;\n readonly states: Map<number, Record<string, unknown>>;\n getStates(): Map<number, Record<string, unknown>>;\n getLocalState(): Record<string, unknown> | null;\n setLocalState(state: Record<string, unknown> | null): void;\n setLocalStateField(field: string, value: unknown): void;\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n}\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 awareness?: Awareness;\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n awareness: Awareness;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n setLocalFields: (fields: Record<string, unknown>) => 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\nfunction parseAwarenessPayload(data: Uint8Array): Map<number, Record<string, unknown>> {\n const result = new Map<number, Record<string, unknown>>();\n let pos = 0;\n const [count, pos2] = readVarUint(data, pos);\n pos = pos2;\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(data, pos);\n pos = pos3;\n const [, pos4] = readVarUint(data, pos);\n pos = pos4;\n const [stateStr, pos5] = readVarString(data, pos);\n pos = pos5;\n\n if (stateStr) {\n try {\n result.set(clientID, JSON.parse(stateStr));\n } catch {\n // no-op\n }\n }\n }\n return result;\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { awareness: externalAwareness, 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 pendingUndoState: {\n undoStackSize?: number;\n redoStackSize?: number;\n } | null = null;\n let connected = false;\n let forceFullSync = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingYdocUpdates: Array<{update: Uint8Array, isBaseline: boolean}> = [];\n let seq = 0;\n const unackedYdocUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean }>();\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let lastLocalAwarenessSnapshot: Record<string, unknown> | null = null;\n\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge provider]\", ...args)\n : () => undefined;\n\n const useExternalAwareness = !!externalAwareness;\n let awareness: Awareness | AwarenessLike;\n const localStates = new Map<number, Record<string, unknown>>();\n const localClientId = Math.floor(Math.random() * 2147483647) + 1;\n const updateHandlers = new Set<(update: { added: number[]; updated: number[]; removed: number[] }) => void>();\n\n function createAwarenessLike(): AwarenessLike {\n function getEffectiveClientId() {\n return serverClientId ?? localClientId;\n }\n\n return {\n get clientID() {\n return getEffectiveClientId();\n },\n get states() {\n return localStates;\n },\n getStates() {\n return new Map(localStates);\n },\n getLocalState() {\n return localStates.get(getEffectiveClientId()) ?? null;\n },\n setLocalState(state: Record<string, unknown> | null) {\n const id = getEffectiveClientId();\n if (state === null) {\n localStates.delete(id);\n const update = { added: [], updated: [], removed: [id] };\n updateHandlers.forEach(handler => handler(update));\n } else {\n const existed = localStates.has(id);\n localStates.set(id, state);\n const update = { added: !existed ? [id] : [], updated: [id], removed: [] };\n updateHandlers.forEach(handler => handler(update));\n }\n },\n setLocalStateField(field: string, value: unknown) {\n const id = getEffectiveClientId();\n const current = localStates.get(id) || {};\n const newState = { ...current, [field]: value };\n localStates.set(id, newState);\n postSetLocalStateToParent(field, value);\n const update = { added: [], updated: [id], removed: [] };\n updateHandlers.forEach(handler => handler(update));\n },\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.add(handler);\n }\n },\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.delete(handler);\n }\n },\n };\n }\n\n if (externalAwareness) {\n awareness = externalAwareness;\n } else {\n awareness = createAwarenessLike();\n }\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 snapshotLocalAwarenessState() {\n const state = awareness.getLocalState();\n lastLocalAwarenessSnapshot = state ? { ...state } : null;\n }\n\n function postSetLocalStateToParent(key: string, value: unknown) {\n if (!connected || !isIframeBridgeStateKey(key)) return;\n const message = { type: \"set-local-state\", key, value };\n logMessage(\"send\", \"set-local-state\", message);\n window.parent.postMessage(message, \"*\");\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 forceFullSync = false;\n snapshotLocalAwarenessState();\n while (pendingYdocUpdates.length > 0) {\n const { update, isBaseline } = pendingYdocUpdates.shift()!;\n const seqNum = ++seq;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n window.parent.postMessage(message, \"*\");\n unackedYdocUpdates.set(seqNum, { update, isBaseline });\n }\n for (const [savedSeq, { update, isBaseline }] of unackedYdocUpdates) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq };\n window.parent.postMessage(message, \"*\");\n }\n connectListeners.forEach((fn) => fn());\n } else {\n lastLocalAwarenessSnapshot = null;\n unackedYdocUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n if (!forceFullSync && pendingYdocUpdates.length > 0) {\n const updates = pendingYdocUpdates.splice(0);\n parentPostMessage({\n type: \"ydoc-pending-updates\",\n payload: updates.map(u => ({ update: Array.from(u.update), isBaseline: u.isBaseline })),\n });\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 if (!connected) {\n if (pendingYdocUpdates.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingYdocUpdates.length = 0;\n console.warn(\"[iframe-bridge] queue full, forcing full sync on reconnect\");\n }\n pendingYdocUpdates.push({ update, isBaseline });\n return;\n }\n const seqNum = ++seq;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n logMessage(\"send\", \"ydoc-update\", message);\n window.parent.postMessage(message, \"*\");\n unackedYdocUpdates.set(seqNum, { update, isBaseline });\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) {\n return;\n }\n if (!connected) {\n return;\n }\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const nextState = awareness.getLocalState();\n const fieldChanges = getAwarenessStateFieldChanges(\n lastLocalAwarenessSnapshot,\n nextState,\n );\n lastLocalAwarenessSnapshot = nextState ? { ...nextState } : null;\n for (const { key, value } of fieldChanges) {\n postSetLocalStateToParent(key, value);\n }\n };\n\n const syncUndoStateFromServer = (\n mxLike: MxLike,\n data: { undoStackSize?: number; redoStackSize?: number },\n ) => {\n const { undoStackSize, redoStackSize } = data;\n const oldIndex = mxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n applyingParentUpdate = true;\n mxLike.history = new Array(newTotal).fill({});\n mxLike.indexOfNextAdd = newIndex;\n if (newTotal === 0) {\n mxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n mxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n applyingParentUpdate = false;\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n if (!event.data || typeof event.data !== 'object') return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n logMessage(\"recv\", type, payload);\n if (type === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedYdocUpdates.delete(ackSeq);\n }\n return;\n }\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n logMessage(\"recv\", type, payload);\n const prevLocalId = localClientId;\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n if (useExternalAwareness === false && receivedServerId != null && prevLocalId !== receivedServerId) {\n const tempState = localStates.get(prevLocalId);\n if (tempState) {\n localStates.delete(prevLocalId);\n localStates.set(receivedServerId, tempState);\n }\n }\n }\n\n if (useExternalAwareness) {\n const serverId = receivedServerId ?? serverClientId;\n const localId = awareness.clientID;\n \n applyingParentUpdate = true;\n if (serverId != null && serverId !== localId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localId,\n );\n \n if (type === \"awareness-sync\") {\n (awareness as Awareness).meta.delete(localId);\n awareness.setLocalState(null);\n }\n \n applyAwarenessUpdate(awareness as Awareness, remapped, null);\n } else {\n applyAwarenessUpdate(awareness as Awareness, new Uint8Array(payload), null);\n }\n applyingParentUpdate = false;\n } else {\n const parsedStates = parseAwarenessPayload(new Uint8Array(payload));\n applyingParentUpdate = true;\n const changedClientIds: number[] = [];\n const removedClientIds: number[] = [];\n for (const [id, state] of parsedStates) {\n const existed = localStates.has(id);\n const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);\n localStates.set(id, state);\n if (changed) {\n changedClientIds.push(id);\n }\n }\n for (const [id] of localStates) {\n if (!parsedStates.has(id)) {\n localStates.delete(id);\n removedClientIds.push(id);\n }\n }\n if (changedClientIds.length > 0 || removedClientIds.length > 0) {\n const update = { added: [], updated: changedClientIds, removed: removedClientIds };\n updateHandlers.forEach(handler => handler(update));\n }\n applyingParentUpdate = false;\n }\n } else if (type === \"undo-state\") {\n if (!currentMxLike) {\n pendingUndoState = event.data;\n } else {\n syncUndoStateFromServer(currentMxLike, event.data);\n }\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n // 只有真正的 y-protocols Awareness 实例才有 .on() 方法\n // AwarenessLike(内部创建的)通过 updateHandlers 管理回调\n if (useExternalAwareness) {\n (awareness as Awareness).on(\"update\", onAwarenessUpdate);\n }\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 get awareness() {\n return awareness as Awareness;\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 setLocalFields(fields: Record<string, unknown>) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n // 通知父页面更新本地字段,父页面可选择是否处理\n if (connected) {\n const message = { type: \"set-local-fields\", fields };\n logMessage(\"send\", \"set-local-fields\", fields);\n window.parent.postMessage(message, \"*\");\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 // bindUndoManager 已安装:不替换 editor.undoManager,只委托 undo/redo\n if (originUndoManager && \"_y\" in originUndoManager) {\n const mxLike = originUndoManager as MxLike;\n const origUndo = mxLike.undo.bind(mxLike);\n const origRedo = mxLike.redo.bind(mxLike);\n const origCanUndo = mxLike.canUndo.bind(mxLike);\n const origCanRedo = mxLike.canRedo.bind(mxLike);\n currentMxLike = mxLike;\n mxLike.undo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n } else {\n origUndo();\n }\n };\n mxLike.redo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n } else {\n origRedo();\n }\n };\n mxLike.canUndo = () => mxLike.indexOfNextAdd > 0;\n mxLike.canRedo = () => mxLike.indexOfNextAdd < mxLike.history.length;\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n const cleanup = () => {\n mxLike.undo = origUndo;\n mxLike.redo = origRedo;\n mxLike.canUndo = origCanUndo;\n mxLike.canRedo = origCanRedo;\n currentMxLike = null;\n pendingUndoState = null;\n };\n currentCleanup = cleanup;\n return cleanup;\n }\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 unknown as DrawioEditor[\"undoManager\"];\n editor.undoListener = function () {};\n\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n pendingUndoState = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n while (pendingYdocUpdates.length > 0) {\n const { update, isBaseline } = pendingYdocUpdates.shift()!;\n window.parent.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n unackedYdocUpdates.clear();\n ydoc.off(\"update\", onYdocUpdate);\n if (useExternalAwareness) {\n (awareness as Awareness).off(\"update\", onAwarenessUpdate);\n }\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","localClientId","Y","applyAwarenessUpdate","mxLike","cleanup"],"mappings":"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"}
@@ -133,6 +133,11 @@ function createIframeBridgeProvider(ydoc, options) {
133
133
  let currentMxLike = null;
134
134
  let pendingUndoState = null;
135
135
  let connected = false;
136
+ let forceFullSync = false;
137
+ const MAX_QUEUE_SIZE = 1e3;
138
+ const pendingYdocUpdates = [];
139
+ let seq = 0;
140
+ const unackedYdocUpdates = /* @__PURE__ */ new Map();
136
141
  let initRetryTimer = null;
137
142
  const connectListeners = /* @__PURE__ */ new Set();
138
143
  const disconnectListeners = /* @__PURE__ */ new Set();
@@ -165,11 +170,14 @@ function createIframeBridgeProvider(ydoc, options) {
165
170
  const id = getEffectiveClientId();
166
171
  if (state === null) {
167
172
  localStates.delete(id);
173
+ const update = { added: [], updated: [], removed: [id] };
174
+ updateHandlers.forEach((handler) => handler(update));
168
175
  } else {
176
+ const existed = localStates.has(id);
169
177
  localStates.set(id, state);
178
+ const update = { added: !existed ? [id] : [], updated: [id], removed: [] };
179
+ updateHandlers.forEach((handler) => handler(update));
170
180
  }
171
- const update = { added: state && !localStates.has(id) ? [id] : [], updated: state ? [id] : [], removed: state === null ? [id] : [] };
172
- updateHandlers.forEach((handler) => handler(update));
173
181
  },
174
182
  setLocalStateField(field, value) {
175
183
  const id = getEffectiveClientId();
@@ -229,10 +237,23 @@ function createIframeBridgeProvider(ydoc, options) {
229
237
  if (connected === value) return;
230
238
  connected = value;
231
239
  if (value) {
240
+ forceFullSync = false;
232
241
  snapshotLocalAwarenessState();
242
+ while (pendingYdocUpdates.length > 0) {
243
+ const { update, isBaseline } = pendingYdocUpdates.shift();
244
+ const seqNum = ++seq;
245
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
246
+ window.parent.postMessage(message, "*");
247
+ unackedYdocUpdates.set(seqNum, { update, isBaseline });
248
+ }
249
+ for (const [savedSeq, { update, isBaseline }] of unackedYdocUpdates) {
250
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: savedSeq };
251
+ window.parent.postMessage(message, "*");
252
+ }
233
253
  connectListeners.forEach((fn) => fn());
234
254
  } else {
235
255
  lastLocalAwarenessSnapshot = null;
256
+ unackedYdocUpdates.clear();
236
257
  disconnectListeners.forEach((fn) => fn());
237
258
  }
238
259
  }
@@ -240,6 +261,13 @@ function createIframeBridgeProvider(ydoc, options) {
240
261
  if (initRetryTimer) {
241
262
  clearInterval(initRetryTimer);
242
263
  }
264
+ if (!forceFullSync && pendingYdocUpdates.length > 0) {
265
+ const updates = pendingYdocUpdates.splice(0);
266
+ parentPostMessage({
267
+ type: "ydoc-pending-updates",
268
+ payload: updates.map((u) => ({ update: Array.from(u.update), isBaseline: u.isBaseline }))
269
+ });
270
+ }
243
271
  parentPostMessage({ type: "init" });
244
272
  initRetryTimer = setInterval(() => {
245
273
  if (!connected) {
@@ -250,9 +278,20 @@ function createIframeBridgeProvider(ydoc, options) {
250
278
  const onYdocUpdate = (update, origin) => {
251
279
  if (applyingParentUpdate) return;
252
280
  const isBaseline = origin === null || origin === void 0;
253
- const message = { type: "ydoc-update", payload: Array.from(update), isBaseline };
281
+ if (!connected) {
282
+ if (pendingYdocUpdates.length >= MAX_QUEUE_SIZE) {
283
+ forceFullSync = true;
284
+ pendingYdocUpdates.length = 0;
285
+ console.warn("[iframe-bridge] queue full, forcing full sync on reconnect");
286
+ }
287
+ pendingYdocUpdates.push({ update, isBaseline });
288
+ return;
289
+ }
290
+ const seqNum = ++seq;
291
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
254
292
  logMessage("send", "ydoc-update", message);
255
293
  window.parent.postMessage(message, "*");
294
+ unackedYdocUpdates.set(seqNum, { update, isBaseline });
256
295
  };
257
296
  const onAwarenessUpdate = ({
258
297
  added,
@@ -307,8 +346,16 @@ function createIframeBridgeProvider(ydoc, options) {
307
346
  };
308
347
  const onMessage = (event) => {
309
348
  if (event.source !== window.parent) return;
349
+ if (!event.data || typeof event.data !== "object") return;
310
350
  const { type, payload, serverClientId: receivedServerId } = event.data;
311
351
  logMessage("recv", type, payload);
352
+ if (type === "ydoc-update-ack") {
353
+ const ackSeq = event.data.seq;
354
+ if (ackSeq != null) {
355
+ unackedYdocUpdates.delete(ackSeq);
356
+ }
357
+ return;
358
+ }
312
359
  if (type === "pong" && receivedServerId != null) {
313
360
  serverClientId = receivedServerId;
314
361
  return;
@@ -360,6 +407,7 @@ function createIframeBridgeProvider(ydoc, options) {
360
407
  const parsedStates = parseAwarenessPayload(new Uint8Array(payload));
361
408
  applyingParentUpdate = true;
362
409
  const changedClientIds = [];
410
+ const removedClientIds = [];
363
411
  for (const [id, state] of parsedStates) {
364
412
  const existed = localStates.has(id);
365
413
  const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);
@@ -368,8 +416,14 @@ function createIframeBridgeProvider(ydoc, options) {
368
416
  changedClientIds.push(id);
369
417
  }
370
418
  }
371
- if (changedClientIds.length > 0) {
372
- const update = { added: [], updated: changedClientIds, removed: [] };
419
+ for (const [id] of localStates) {
420
+ if (!parsedStates.has(id)) {
421
+ localStates.delete(id);
422
+ removedClientIds.push(id);
423
+ }
424
+ }
425
+ if (changedClientIds.length > 0 || removedClientIds.length > 0) {
426
+ const update = { added: [], updated: changedClientIds, removed: removedClientIds };
373
427
  updateHandlers.forEach((handler) => handler(update));
374
428
  }
375
429
  applyingParentUpdate = false;
@@ -555,6 +609,11 @@ function createIframeBridgeProvider(ydoc, options) {
555
609
  return cleanup;
556
610
  },
557
611
  destroy: () => {
612
+ while (pendingYdocUpdates.length > 0) {
613
+ const { update, isBaseline } = pendingYdocUpdates.shift();
614
+ window.parent.postMessage({ type: "ydoc-update", payload: Array.from(update), isBaseline }, "*");
615
+ }
616
+ unackedYdocUpdates.clear();
558
617
  ydoc.off("update", onYdocUpdate);
559
618
  if (useExternalAwareness) {
560
619
  awareness.off("update", onAwarenessUpdate);
@@ -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} from \"y-protocols/awareness\";\nconst IFRAME_BRIDGE_STATE_KEYS = new Set([\"cursor\", \"selection\"]);\n\nfunction isIframeBridgeStateKey(key: string): boolean {\n return IFRAME_BRIDGE_STATE_KEYS.has(key);\n}\n\nfunction getAwarenessStateFieldChanges(\n prev: Record<string, unknown> | null,\n next: Record<string, unknown> | null,\n): Array<{ key: string; value: unknown }> {\n const keys = new Set([\n ...Object.keys(prev ?? {}),\n ...Object.keys(next ?? {}),\n ]);\n const changes: Array<{ key: string; value: unknown }> = [];\n for (const key of keys) {\n const prevValue = prev?.[key];\n const nextValue = next?.[key];\n if (JSON.stringify(prevValue) !== JSON.stringify(nextValue)) {\n changes.push({ key, value: nextValue });\n }\n }\n return changes;\n}\n\n/**\n * Awareness-like 接口,只需要支持本地状态管理。\n * 用于 iframe-bridge provider 不需要与父页面同步 awareness 的场景。\n */\nexport interface AwarenessLike {\n readonly clientID: number;\n readonly states: Map<number, Record<string, unknown>>;\n getStates(): Map<number, Record<string, unknown>>;\n getLocalState(): Record<string, unknown> | null;\n setLocalState(state: Record<string, unknown> | null): void;\n setLocalStateField(field: string, value: unknown): void;\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n}\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 awareness?: Awareness;\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n awareness: Awareness;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n setLocalFields: (fields: Record<string, unknown>) => 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\nfunction parseAwarenessPayload(data: Uint8Array): Map<number, Record<string, unknown>> {\n const result = new Map<number, Record<string, unknown>>();\n let pos = 0;\n const [count, pos2] = readVarUint(data, pos);\n pos = pos2;\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(data, pos);\n pos = pos3;\n const [, pos4] = readVarUint(data, pos);\n pos = pos4;\n const [stateStr, pos5] = readVarString(data, pos);\n pos = pos5;\n\n if (stateStr) {\n try {\n result.set(clientID, JSON.parse(stateStr));\n } catch {\n // no-op\n }\n }\n }\n return result;\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { awareness: externalAwareness, 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 pendingUndoState: {\n undoStackSize?: number;\n redoStackSize?: number;\n } | 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 let lastLocalAwarenessSnapshot: Record<string, unknown> | null = null;\n\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge provider]\", ...args)\n : () => undefined;\n\n const useExternalAwareness = !!externalAwareness;\n let awareness: Awareness | AwarenessLike;\n const localStates = new Map<number, Record<string, unknown>>();\n const localClientId = Math.floor(Math.random() * 2147483647) + 1;\n const updateHandlers = new Set<(update: { added: number[]; updated: number[]; removed: number[] }) => void>();\n\n function createAwarenessLike(): AwarenessLike {\n function getEffectiveClientId() {\n return serverClientId ?? localClientId;\n }\n\n return {\n get clientID() {\n return getEffectiveClientId();\n },\n get states() {\n return localStates;\n },\n getStates() {\n return new Map(localStates);\n },\n getLocalState() {\n return localStates.get(getEffectiveClientId()) ?? null;\n },\n setLocalState(state: Record<string, unknown> | null) {\n const id = getEffectiveClientId();\n if (state === null) {\n localStates.delete(id);\n } else {\n localStates.set(id, state);\n }\n const update = { added: state && !localStates.has(id) ? [id] : [], updated: state ? [id] : [], removed: state === null ? [id] : [] };\n updateHandlers.forEach(handler => handler(update));\n },\n setLocalStateField(field: string, value: unknown) {\n const id = getEffectiveClientId();\n const current = localStates.get(id) || {};\n const newState = { ...current, [field]: value };\n localStates.set(id, newState);\n postSetLocalStateToParent(field, value);\n const update = { added: [], updated: [id], removed: [] };\n updateHandlers.forEach(handler => handler(update));\n },\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.add(handler);\n }\n },\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.delete(handler);\n }\n },\n };\n }\n\n if (externalAwareness) {\n awareness = externalAwareness;\n } else {\n awareness = createAwarenessLike();\n }\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 snapshotLocalAwarenessState() {\n const state = awareness.getLocalState();\n lastLocalAwarenessSnapshot = state ? { ...state } : null;\n }\n\n function postSetLocalStateToParent(key: string, value: unknown) {\n if (!connected || !isIframeBridgeStateKey(key)) return;\n const message = { type: \"set-local-state\", key, value };\n logMessage(\"send\", \"set-local-state\", message);\n window.parent.postMessage(message, \"*\");\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 snapshotLocalAwarenessState();\n connectListeners.forEach((fn) => fn());\n } else {\n lastLocalAwarenessSnapshot = null;\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) {\n return;\n }\n if (!connected) {\n return;\n }\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const nextState = awareness.getLocalState();\n const fieldChanges = getAwarenessStateFieldChanges(\n lastLocalAwarenessSnapshot,\n nextState,\n );\n lastLocalAwarenessSnapshot = nextState ? { ...nextState } : null;\n for (const { key, value } of fieldChanges) {\n postSetLocalStateToParent(key, value);\n }\n };\n\n const syncUndoStateFromServer = (\n mxLike: MxLike,\n data: { undoStackSize?: number; redoStackSize?: number },\n ) => {\n const { undoStackSize, redoStackSize } = data;\n const oldIndex = mxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n applyingParentUpdate = true;\n mxLike.history = new Array(newTotal).fill({});\n mxLike.indexOfNextAdd = newIndex;\n if (newTotal === 0) {\n mxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n mxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n applyingParentUpdate = false;\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 const prevLocalId = localClientId;\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n if (useExternalAwareness === false && receivedServerId != null && prevLocalId !== receivedServerId) {\n const tempState = localStates.get(prevLocalId);\n if (tempState) {\n localStates.delete(prevLocalId);\n localStates.set(receivedServerId, tempState);\n }\n }\n }\n\n if (useExternalAwareness) {\n const serverId = receivedServerId ?? serverClientId;\n const localId = awareness.clientID;\n \n applyingParentUpdate = true;\n if (serverId != null && serverId !== localId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localId,\n );\n \n if (type === \"awareness-sync\") {\n (awareness as Awareness).meta.delete(localId);\n awareness.setLocalState(null);\n }\n \n applyAwarenessUpdate(awareness as Awareness, remapped, null);\n } else {\n applyAwarenessUpdate(awareness as Awareness, new Uint8Array(payload), null);\n }\n applyingParentUpdate = false;\n } else {\n const parsedStates = parseAwarenessPayload(new Uint8Array(payload));\n applyingParentUpdate = true;\n const changedClientIds: number[] = [];\n for (const [id, state] of parsedStates) {\n const existed = localStates.has(id);\n const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);\n localStates.set(id, state);\n if (changed) {\n changedClientIds.push(id);\n }\n }\n if (changedClientIds.length > 0) {\n const update = { added: [], updated: changedClientIds, removed: [] };\n updateHandlers.forEach(handler => handler(update));\n }\n applyingParentUpdate = false;\n }\n } else if (type === \"undo-state\") {\n if (!currentMxLike) {\n pendingUndoState = event.data;\n } else {\n syncUndoStateFromServer(currentMxLike, event.data);\n }\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n // 只有真正的 y-protocols Awareness 实例才有 .on() 方法\n // AwarenessLike(内部创建的)通过 updateHandlers 管理回调\n if (useExternalAwareness) {\n (awareness as Awareness).on(\"update\", onAwarenessUpdate);\n }\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 get awareness() {\n return awareness as Awareness;\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 setLocalFields(fields: Record<string, unknown>) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n // 通知父页面更新本地字段,父页面可选择是否处理\n if (connected) {\n const message = { type: \"set-local-fields\", fields };\n logMessage(\"send\", \"set-local-fields\", fields);\n window.parent.postMessage(message, \"*\");\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 // bindUndoManager 已安装:不替换 editor.undoManager,只委托 undo/redo\n if (originUndoManager && \"_y\" in originUndoManager) {\n const mxLike = originUndoManager as MxLike;\n const origUndo = mxLike.undo.bind(mxLike);\n const origRedo = mxLike.redo.bind(mxLike);\n const origCanUndo = mxLike.canUndo.bind(mxLike);\n const origCanRedo = mxLike.canRedo.bind(mxLike);\n currentMxLike = mxLike;\n mxLike.undo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n } else {\n origUndo();\n }\n };\n mxLike.redo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n } else {\n origRedo();\n }\n };\n mxLike.canUndo = () => mxLike.indexOfNextAdd > 0;\n mxLike.canRedo = () => mxLike.indexOfNextAdd < mxLike.history.length;\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n const cleanup = () => {\n mxLike.undo = origUndo;\n mxLike.redo = origRedo;\n mxLike.canUndo = origCanUndo;\n mxLike.canRedo = origCanRedo;\n currentMxLike = null;\n pendingUndoState = null;\n };\n currentCleanup = cleanup;\n return cleanup;\n }\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 unknown as DrawioEditor[\"undoManager\"];\n editor.undoListener = function () {};\n\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n pendingUndoState = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n if (useExternalAwareness) {\n (awareness as Awareness).off(\"update\", onAwarenessUpdate);\n }\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":["localClientId","mxLike","cleanup"],"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} from \"y-protocols/awareness\";\nconst IFRAME_BRIDGE_STATE_KEYS = new Set([\"cursor\", \"selection\"]);\n\nfunction isIframeBridgeStateKey(key: string): boolean {\n return IFRAME_BRIDGE_STATE_KEYS.has(key);\n}\n\nfunction getAwarenessStateFieldChanges(\n prev: Record<string, unknown> | null,\n next: Record<string, unknown> | null,\n): Array<{ key: string; value: unknown }> {\n const keys = new Set([\n ...Object.keys(prev ?? {}),\n ...Object.keys(next ?? {}),\n ]);\n const changes: Array<{ key: string; value: unknown }> = [];\n for (const key of keys) {\n const prevValue = prev?.[key];\n const nextValue = next?.[key];\n if (JSON.stringify(prevValue) !== JSON.stringify(nextValue)) {\n changes.push({ key, value: nextValue });\n }\n }\n return changes;\n}\n\n/**\n * Awareness-like 接口,只需要支持本地状态管理。\n * 用于 iframe-bridge provider 不需要与父页面同步 awareness 的场景。\n */\nexport interface AwarenessLike {\n readonly clientID: number;\n readonly states: Map<number, Record<string, unknown>>;\n getStates(): Map<number, Record<string, unknown>>;\n getLocalState(): Record<string, unknown> | null;\n setLocalState(state: Record<string, unknown> | null): void;\n setLocalStateField(field: string, value: unknown): void;\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n}\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 awareness?: Awareness;\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n awareness: Awareness;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n setLocalFields: (fields: Record<string, unknown>) => 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\nfunction parseAwarenessPayload(data: Uint8Array): Map<number, Record<string, unknown>> {\n const result = new Map<number, Record<string, unknown>>();\n let pos = 0;\n const [count, pos2] = readVarUint(data, pos);\n pos = pos2;\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(data, pos);\n pos = pos3;\n const [, pos4] = readVarUint(data, pos);\n pos = pos4;\n const [stateStr, pos5] = readVarString(data, pos);\n pos = pos5;\n\n if (stateStr) {\n try {\n result.set(clientID, JSON.parse(stateStr));\n } catch {\n // no-op\n }\n }\n }\n return result;\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { awareness: externalAwareness, 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 pendingUndoState: {\n undoStackSize?: number;\n redoStackSize?: number;\n } | null = null;\n let connected = false;\n let forceFullSync = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingYdocUpdates: Array<{update: Uint8Array, isBaseline: boolean}> = [];\n let seq = 0;\n const unackedYdocUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean }>();\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let lastLocalAwarenessSnapshot: Record<string, unknown> | null = null;\n\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge provider]\", ...args)\n : () => undefined;\n\n const useExternalAwareness = !!externalAwareness;\n let awareness: Awareness | AwarenessLike;\n const localStates = new Map<number, Record<string, unknown>>();\n const localClientId = Math.floor(Math.random() * 2147483647) + 1;\n const updateHandlers = new Set<(update: { added: number[]; updated: number[]; removed: number[] }) => void>();\n\n function createAwarenessLike(): AwarenessLike {\n function getEffectiveClientId() {\n return serverClientId ?? localClientId;\n }\n\n return {\n get clientID() {\n return getEffectiveClientId();\n },\n get states() {\n return localStates;\n },\n getStates() {\n return new Map(localStates);\n },\n getLocalState() {\n return localStates.get(getEffectiveClientId()) ?? null;\n },\n setLocalState(state: Record<string, unknown> | null) {\n const id = getEffectiveClientId();\n if (state === null) {\n localStates.delete(id);\n const update = { added: [], updated: [], removed: [id] };\n updateHandlers.forEach(handler => handler(update));\n } else {\n const existed = localStates.has(id);\n localStates.set(id, state);\n const update = { added: !existed ? [id] : [], updated: [id], removed: [] };\n updateHandlers.forEach(handler => handler(update));\n }\n },\n setLocalStateField(field: string, value: unknown) {\n const id = getEffectiveClientId();\n const current = localStates.get(id) || {};\n const newState = { ...current, [field]: value };\n localStates.set(id, newState);\n postSetLocalStateToParent(field, value);\n const update = { added: [], updated: [id], removed: [] };\n updateHandlers.forEach(handler => handler(update));\n },\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.add(handler);\n }\n },\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.delete(handler);\n }\n },\n };\n }\n\n if (externalAwareness) {\n awareness = externalAwareness;\n } else {\n awareness = createAwarenessLike();\n }\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 snapshotLocalAwarenessState() {\n const state = awareness.getLocalState();\n lastLocalAwarenessSnapshot = state ? { ...state } : null;\n }\n\n function postSetLocalStateToParent(key: string, value: unknown) {\n if (!connected || !isIframeBridgeStateKey(key)) return;\n const message = { type: \"set-local-state\", key, value };\n logMessage(\"send\", \"set-local-state\", message);\n window.parent.postMessage(message, \"*\");\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 forceFullSync = false;\n snapshotLocalAwarenessState();\n while (pendingYdocUpdates.length > 0) {\n const { update, isBaseline } = pendingYdocUpdates.shift()!;\n const seqNum = ++seq;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n window.parent.postMessage(message, \"*\");\n unackedYdocUpdates.set(seqNum, { update, isBaseline });\n }\n for (const [savedSeq, { update, isBaseline }] of unackedYdocUpdates) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq };\n window.parent.postMessage(message, \"*\");\n }\n connectListeners.forEach((fn) => fn());\n } else {\n lastLocalAwarenessSnapshot = null;\n unackedYdocUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n if (!forceFullSync && pendingYdocUpdates.length > 0) {\n const updates = pendingYdocUpdates.splice(0);\n parentPostMessage({\n type: \"ydoc-pending-updates\",\n payload: updates.map(u => ({ update: Array.from(u.update), isBaseline: u.isBaseline })),\n });\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 if (!connected) {\n if (pendingYdocUpdates.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingYdocUpdates.length = 0;\n console.warn(\"[iframe-bridge] queue full, forcing full sync on reconnect\");\n }\n pendingYdocUpdates.push({ update, isBaseline });\n return;\n }\n const seqNum = ++seq;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n logMessage(\"send\", \"ydoc-update\", message);\n window.parent.postMessage(message, \"*\");\n unackedYdocUpdates.set(seqNum, { update, isBaseline });\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) {\n return;\n }\n if (!connected) {\n return;\n }\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const nextState = awareness.getLocalState();\n const fieldChanges = getAwarenessStateFieldChanges(\n lastLocalAwarenessSnapshot,\n nextState,\n );\n lastLocalAwarenessSnapshot = nextState ? { ...nextState } : null;\n for (const { key, value } of fieldChanges) {\n postSetLocalStateToParent(key, value);\n }\n };\n\n const syncUndoStateFromServer = (\n mxLike: MxLike,\n data: { undoStackSize?: number; redoStackSize?: number },\n ) => {\n const { undoStackSize, redoStackSize } = data;\n const oldIndex = mxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n applyingParentUpdate = true;\n mxLike.history = new Array(newTotal).fill({});\n mxLike.indexOfNextAdd = newIndex;\n if (newTotal === 0) {\n mxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n mxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n mxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n applyingParentUpdate = false;\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n if (!event.data || typeof event.data !== 'object') return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n logMessage(\"recv\", type, payload);\n if (type === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedYdocUpdates.delete(ackSeq);\n }\n return;\n }\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n logMessage(\"recv\", type, payload);\n const prevLocalId = localClientId;\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n if (useExternalAwareness === false && receivedServerId != null && prevLocalId !== receivedServerId) {\n const tempState = localStates.get(prevLocalId);\n if (tempState) {\n localStates.delete(prevLocalId);\n localStates.set(receivedServerId, tempState);\n }\n }\n }\n\n if (useExternalAwareness) {\n const serverId = receivedServerId ?? serverClientId;\n const localId = awareness.clientID;\n \n applyingParentUpdate = true;\n if (serverId != null && serverId !== localId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localId,\n );\n \n if (type === \"awareness-sync\") {\n (awareness as Awareness).meta.delete(localId);\n awareness.setLocalState(null);\n }\n \n applyAwarenessUpdate(awareness as Awareness, remapped, null);\n } else {\n applyAwarenessUpdate(awareness as Awareness, new Uint8Array(payload), null);\n }\n applyingParentUpdate = false;\n } else {\n const parsedStates = parseAwarenessPayload(new Uint8Array(payload));\n applyingParentUpdate = true;\n const changedClientIds: number[] = [];\n const removedClientIds: number[] = [];\n for (const [id, state] of parsedStates) {\n const existed = localStates.has(id);\n const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);\n localStates.set(id, state);\n if (changed) {\n changedClientIds.push(id);\n }\n }\n for (const [id] of localStates) {\n if (!parsedStates.has(id)) {\n localStates.delete(id);\n removedClientIds.push(id);\n }\n }\n if (changedClientIds.length > 0 || removedClientIds.length > 0) {\n const update = { added: [], updated: changedClientIds, removed: removedClientIds };\n updateHandlers.forEach(handler => handler(update));\n }\n applyingParentUpdate = false;\n }\n } else if (type === \"undo-state\") {\n if (!currentMxLike) {\n pendingUndoState = event.data;\n } else {\n syncUndoStateFromServer(currentMxLike, event.data);\n }\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n // 只有真正的 y-protocols Awareness 实例才有 .on() 方法\n // AwarenessLike(内部创建的)通过 updateHandlers 管理回调\n if (useExternalAwareness) {\n (awareness as Awareness).on(\"update\", onAwarenessUpdate);\n }\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 get awareness() {\n return awareness as Awareness;\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 setLocalFields(fields: Record<string, unknown>) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n // 通知父页面更新本地字段,父页面可选择是否处理\n if (connected) {\n const message = { type: \"set-local-fields\", fields };\n logMessage(\"send\", \"set-local-fields\", fields);\n window.parent.postMessage(message, \"*\");\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 // bindUndoManager 已安装:不替换 editor.undoManager,只委托 undo/redo\n if (originUndoManager && \"_y\" in originUndoManager) {\n const mxLike = originUndoManager as MxLike;\n const origUndo = mxLike.undo.bind(mxLike);\n const origRedo = mxLike.redo.bind(mxLike);\n const origCanUndo = mxLike.canUndo.bind(mxLike);\n const origCanRedo = mxLike.canRedo.bind(mxLike);\n currentMxLike = mxLike;\n mxLike.undo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n } else {\n origUndo();\n }\n };\n mxLike.redo = () => {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n } else {\n origRedo();\n }\n };\n mxLike.canUndo = () => mxLike.indexOfNextAdd > 0;\n mxLike.canRedo = () => mxLike.indexOfNextAdd < mxLike.history.length;\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n const cleanup = () => {\n mxLike.undo = origUndo;\n mxLike.redo = origRedo;\n mxLike.canUndo = origCanUndo;\n mxLike.canRedo = origCanRedo;\n currentMxLike = null;\n pendingUndoState = null;\n };\n currentCleanup = cleanup;\n return cleanup;\n }\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 unknown as DrawioEditor[\"undoManager\"];\n editor.undoListener = function () {};\n\n if (pendingUndoState) {\n syncUndoStateFromServer(mxLike, pendingUndoState);\n pendingUndoState = null;\n }\n window.parent.postMessage({ type: \"request-undo-state\" }, \"*\");\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n pendingUndoState = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n while (pendingYdocUpdates.length > 0) {\n const { update, isBaseline } = pendingYdocUpdates.shift()!;\n window.parent.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n unackedYdocUpdates.clear();\n ydoc.off(\"update\", onYdocUpdate);\n if (useExternalAwareness) {\n (awareness as Awareness).off(\"update\", onAwarenessUpdate);\n }\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":["localClientId","mxLike","cleanup"],"mappings":"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"}
@@ -57,10 +57,15 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
57
57
  log(direction, type, formatPayload(payload));
58
58
  }
59
59
  let connected = false;
60
+ let forceFullSync = false;
60
61
  let applyingIframeUpdate = false;
61
62
  const connectListeners = /* @__PURE__ */ new Set();
62
63
  const disconnectListeners = /* @__PURE__ */ new Set();
63
64
  let iframeOriginTracked = false;
65
+ const MAX_QUEUE_SIZE = 1e3;
66
+ const pendingUpdatesToIframe = [];
67
+ let serverSeq = 0;
68
+ const unackedServerUpdates = /* @__PURE__ */ new Map();
64
69
  function tryAddIframeOriginTracking() {
65
70
  if (!undoManager) return;
66
71
  try {
@@ -93,8 +98,29 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
93
98
  if (connected === value) return;
94
99
  connected = value;
95
100
  if (value) {
101
+ const wasForceFullSync = forceFullSync;
102
+ forceFullSync = false;
103
+ while (pendingUpdatesToIframe.length > 0) {
104
+ const { update, isBaseline } = pendingUpdatesToIframe.shift();
105
+ const seqNum = ++serverSeq;
106
+ const cw = iframe.contentWindow;
107
+ if (cw) {
108
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
109
+ cw.postMessage(message, "*");
110
+ }
111
+ unackedServerUpdates.set(seqNum, { update, isBaseline });
112
+ }
113
+ if (!wasForceFullSync) {
114
+ for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {
115
+ const cw = iframe.contentWindow;
116
+ if (cw) {
117
+ cw.postMessage({ type: "ydoc-update", payload: Array.from(update), isBaseline, seq: savedSeq }, "*");
118
+ }
119
+ }
120
+ }
96
121
  connectListeners.forEach((fn) => fn());
97
122
  } else {
123
+ unackedServerUpdates.clear();
98
124
  disconnectListeners.forEach((fn) => fn());
99
125
  }
100
126
  }
@@ -116,8 +142,24 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
116
142
  }
117
143
  const onYdocUpdate = (update, origin) => {
118
144
  if (origin === IFRAME_ORIGIN) return;
145
+ const isBaseline = origin === null || origin === void 0;
119
146
  logMessage("send", "ydoc-update", update);
120
- postToIframe("ydoc-update", update);
147
+ if (!connected) {
148
+ if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {
149
+ forceFullSync = true;
150
+ pendingUpdatesToIframe.length = 0;
151
+ console.warn("[iframe-bridge server] queue full, forcing full sync on reconnect");
152
+ }
153
+ pendingUpdatesToIframe.push({ update, isBaseline });
154
+ return;
155
+ }
156
+ const seqNum = ++serverSeq;
157
+ const cw = iframe.contentWindow;
158
+ if (cw) {
159
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
160
+ cw.postMessage(message, "*");
161
+ }
162
+ unackedServerUpdates.set(seqNum, { update, isBaseline });
121
163
  };
122
164
  function postUndoStateToIframe() {
123
165
  var _a, _b;
@@ -154,9 +196,21 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
154
196
  });
155
197
  };
156
198
  const onMessage = (event) => {
199
+ var _a, _b;
157
200
  if (event.source !== iframe.contentWindow) return;
158
201
  const { type: msgType, payload } = event.data;
159
- if (msgType === "init") {
202
+ if (msgType === "ydoc-pending-updates") {
203
+ logMessage("recv", "ydoc-pending-updates", payload);
204
+ if (Array.isArray(payload)) {
205
+ for (const item of payload) {
206
+ const arr = (_a = item.update) != null ? _a : item;
207
+ const isBaseline = (_b = item.isBaseline) != null ? _b : false;
208
+ const update = new Uint8Array(arr);
209
+ const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
210
+ Y__namespace.applyUpdate(ydoc, update, applyOrigin);
211
+ }
212
+ }
213
+ } else if (msgType === "init") {
160
214
  logMessage("recv", "init", payload);
161
215
  if (!connected) {
162
216
  setConnected(true);
@@ -164,6 +218,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
164
218
  const docState = Y__namespace.encodeStateAsUpdate(ydoc);
165
219
  logMessage("send", "ydoc-sync", { bytes: docState.length });
166
220
  postToIframe("ydoc-sync", new Uint8Array(Array.from(docState)));
221
+ unackedServerUpdates.clear();
167
222
  const cw = iframe.contentWindow;
168
223
  if (cw) {
169
224
  const states = awareness$1.getStates();
@@ -208,6 +263,18 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
208
263
  const isBaseline = event.data.isBaseline;
209
264
  const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
210
265
  Y__namespace.applyUpdate(ydoc, update, applyOrigin);
266
+ const inSeq = event.data.seq;
267
+ if (inSeq != null) {
268
+ const cw = iframe.contentWindow;
269
+ if (cw) {
270
+ cw.postMessage({ type: "ydoc-update-ack", seq: inSeq }, "*");
271
+ }
272
+ }
273
+ } else if (msgType === "ydoc-update-ack") {
274
+ const ackSeq = event.data.seq;
275
+ if (ackSeq != null) {
276
+ unackedServerUpdates.delete(ackSeq);
277
+ }
211
278
  } else if (msgType === "awareness-local-state") {
212
279
  logMessage("recv", "awareness-local-state (ignored)", payload);
213
280
  } else if (msgType === "set-local-state") {
@@ -285,6 +352,13 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
285
352
  }
286
353
  },
287
354
  destroy: () => {
355
+ while (pendingUpdatesToIframe.length > 0) {
356
+ const { update, isBaseline } = pendingUpdatesToIframe.shift();
357
+ const cw = iframe.contentWindow;
358
+ if (cw) {
359
+ cw.postMessage({ type: "ydoc-update", payload: Array.from(update), isBaseline }, "*");
360
+ }
361
+ }
288
362
  setConnected(false);
289
363
  postToIframe("disconnect");
290
364
  ydoc.off("update", onYdocUpdate);
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","sources":["../../../iframe-bridge/src/origin.ts","../../../iframe-bridge/src/server.ts"],"sourcesContent":["/**\n * iframe bridge 内部变更的 origin 标识。\n * 当 provider 端产生 ydoc-update 时,使用此 origin 标记,\n * 以便 server 端的 UndoManager 能正确追踪来自 iframe 的变更。\n */\nexport const IFRAME_ORIGIN: object = {};\n\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let 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 states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID };\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 // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n destroy: () => {\n 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.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let forceFullSync = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingUpdatesToIframe: Array<{ update: Uint8Array; isBaseline: boolean }> = [];\n let serverSeq = 0;\n const unackedServerUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean }>();\n\n function tryAddIframeOriginTracking() {\n if (!undoManager) return;\n try {\n if (typeof (undoManager as any).addTrackedOrigin === \"function\") {\n (undoManager as any).addTrackedOrigin(IFRAME_ORIGIN);\n iframeOriginTracked = true;\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n function tryRemoveIframeOriginTracking() {\n if (!undoManager || !iframeOriginTracked) return;\n try {\n if (typeof (undoManager as any).removeTrackedOrigin === \"function\") {\n (undoManager as any).removeTrackedOrigin(IFRAME_ORIGIN);\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n tryAddIframeOriginTracking();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n const wasForceFullSync = forceFullSync;\n forceFullSync = false;\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n }\n if (!wasForceFullSync) {\n for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq }, \"*\");\n }\n }\n }\n connectListeners.forEach((fn) => fn());\n } else {\n unackedServerUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n const message = { type, payload: payload ? Array.from(payload) : [] };\n logMessage(\"send\", type, payload);\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n function postObjectToIframe(message: Record<string, unknown>) {\n const type = message.type as string;\n logMessage(\"send\", type, message);\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n logMessage(\"send\", \"ydoc-update\", update);\n if (!connected) {\n if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingUpdatesToIframe.length = 0;\n console.warn(\"[iframe-bridge server] queue full, forcing full sync on reconnect\");\n }\n pendingUpdatesToIframe.push({ update, isBaseline });\n return;\n }\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n };\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n logMessage(\"send\", \"undo-state\", state);\n cw.postMessage(state, \"*\");\n }\n }\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n logMessage(\"send\", \"awareness-update\", update);\n postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"ydoc-pending-updates\") {\n logMessage(\"recv\", \"ydoc-pending-updates\", payload);\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const arr = item.update ?? item;\n const isBaseline = item.isBaseline ?? false;\n const update = new Uint8Array(arr);\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n }\n }\n } else if (msgType === \"init\") {\n logMessage(\"recv\", \"init\", payload);\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n logMessage(\"send\", \"ydoc-sync\", { bytes: docState.length });\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n unackedServerUpdates.clear();\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID };\n logMessage(\"send\", \"pong\", message);\n cw.postMessage(message, \"*\");\n }\n } else if (msgType === \"ydoc-update\") {\n logMessage(\"recv\", \"ydoc-update\", payload);\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n const inSeq = event.data.seq;\n if (inSeq != null) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update-ack\", seq: inSeq }, \"*\");\n }\n }\n } else if (msgType === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedServerUpdates.delete(ackSeq);\n }\n } else if (msgType === \"awareness-local-state\") {\n // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n destroy: () => {\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n }\n setConnected(false);\n postToIframe(\"disconnect\");\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.off(\"stack-item-popped\", onUndoPopped);\n undoManager.off(\"stack-cleared\", onStackCleared);\n undoManager.off(\"stack-item-added\", onStackItemAdded);\n tryRemoveIframeOriginTracking();\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
@@ -38,10 +38,15 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
38
38
  log(direction, type, formatPayload(payload));
39
39
  }
40
40
  let connected = false;
41
+ let forceFullSync = false;
41
42
  let applyingIframeUpdate = false;
42
43
  const connectListeners = /* @__PURE__ */ new Set();
43
44
  const disconnectListeners = /* @__PURE__ */ new Set();
44
45
  let iframeOriginTracked = false;
46
+ const MAX_QUEUE_SIZE = 1e3;
47
+ const pendingUpdatesToIframe = [];
48
+ let serverSeq = 0;
49
+ const unackedServerUpdates = /* @__PURE__ */ new Map();
45
50
  function tryAddIframeOriginTracking() {
46
51
  if (!undoManager) return;
47
52
  try {
@@ -74,8 +79,29 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
74
79
  if (connected === value) return;
75
80
  connected = value;
76
81
  if (value) {
82
+ const wasForceFullSync = forceFullSync;
83
+ forceFullSync = false;
84
+ while (pendingUpdatesToIframe.length > 0) {
85
+ const { update, isBaseline } = pendingUpdatesToIframe.shift();
86
+ const seqNum = ++serverSeq;
87
+ const cw = iframe.contentWindow;
88
+ if (cw) {
89
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
90
+ cw.postMessage(message, "*");
91
+ }
92
+ unackedServerUpdates.set(seqNum, { update, isBaseline });
93
+ }
94
+ if (!wasForceFullSync) {
95
+ for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {
96
+ const cw = iframe.contentWindow;
97
+ if (cw) {
98
+ cw.postMessage({ type: "ydoc-update", payload: Array.from(update), isBaseline, seq: savedSeq }, "*");
99
+ }
100
+ }
101
+ }
77
102
  connectListeners.forEach((fn) => fn());
78
103
  } else {
104
+ unackedServerUpdates.clear();
79
105
  disconnectListeners.forEach((fn) => fn());
80
106
  }
81
107
  }
@@ -97,8 +123,24 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
97
123
  }
98
124
  const onYdocUpdate = (update, origin) => {
99
125
  if (origin === IFRAME_ORIGIN) return;
126
+ const isBaseline = origin === null || origin === void 0;
100
127
  logMessage("send", "ydoc-update", update);
101
- postToIframe("ydoc-update", update);
128
+ if (!connected) {
129
+ if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {
130
+ forceFullSync = true;
131
+ pendingUpdatesToIframe.length = 0;
132
+ console.warn("[iframe-bridge server] queue full, forcing full sync on reconnect");
133
+ }
134
+ pendingUpdatesToIframe.push({ update, isBaseline });
135
+ return;
136
+ }
137
+ const seqNum = ++serverSeq;
138
+ const cw = iframe.contentWindow;
139
+ if (cw) {
140
+ const message = { type: "ydoc-update", payload: Array.from(update), isBaseline, seq: seqNum };
141
+ cw.postMessage(message, "*");
142
+ }
143
+ unackedServerUpdates.set(seqNum, { update, isBaseline });
102
144
  };
103
145
  function postUndoStateToIframe() {
104
146
  var _a, _b;
@@ -135,9 +177,21 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
135
177
  });
136
178
  };
137
179
  const onMessage = (event) => {
180
+ var _a, _b;
138
181
  if (event.source !== iframe.contentWindow) return;
139
182
  const { type: msgType, payload } = event.data;
140
- if (msgType === "init") {
183
+ if (msgType === "ydoc-pending-updates") {
184
+ logMessage("recv", "ydoc-pending-updates", payload);
185
+ if (Array.isArray(payload)) {
186
+ for (const item of payload) {
187
+ const arr = (_a = item.update) != null ? _a : item;
188
+ const isBaseline = (_b = item.isBaseline) != null ? _b : false;
189
+ const update = new Uint8Array(arr);
190
+ const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
191
+ Y.applyUpdate(ydoc, update, applyOrigin);
192
+ }
193
+ }
194
+ } else if (msgType === "init") {
141
195
  logMessage("recv", "init", payload);
142
196
  if (!connected) {
143
197
  setConnected(true);
@@ -145,6 +199,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
145
199
  const docState = Y.encodeStateAsUpdate(ydoc);
146
200
  logMessage("send", "ydoc-sync", { bytes: docState.length });
147
201
  postToIframe("ydoc-sync", new Uint8Array(Array.from(docState)));
202
+ unackedServerUpdates.clear();
148
203
  const cw = iframe.contentWindow;
149
204
  if (cw) {
150
205
  const states = awareness.getStates();
@@ -189,6 +244,18 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
189
244
  const isBaseline = event.data.isBaseline;
190
245
  const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
191
246
  Y.applyUpdate(ydoc, update, applyOrigin);
247
+ const inSeq = event.data.seq;
248
+ if (inSeq != null) {
249
+ const cw = iframe.contentWindow;
250
+ if (cw) {
251
+ cw.postMessage({ type: "ydoc-update-ack", seq: inSeq }, "*");
252
+ }
253
+ }
254
+ } else if (msgType === "ydoc-update-ack") {
255
+ const ackSeq = event.data.seq;
256
+ if (ackSeq != null) {
257
+ unackedServerUpdates.delete(ackSeq);
258
+ }
192
259
  } else if (msgType === "awareness-local-state") {
193
260
  logMessage("recv", "awareness-local-state (ignored)", payload);
194
261
  } else if (msgType === "set-local-state") {
@@ -266,6 +333,13 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
266
333
  }
267
334
  },
268
335
  destroy: () => {
336
+ while (pendingUpdatesToIframe.length > 0) {
337
+ const { update, isBaseline } = pendingUpdatesToIframe.shift();
338
+ const cw = iframe.contentWindow;
339
+ if (cw) {
340
+ cw.postMessage({ type: "ydoc-update", payload: Array.from(update), isBaseline }, "*");
341
+ }
342
+ }
269
343
  setConnected(false);
270
344
  postToIframe("disconnect");
271
345
  ydoc.off("update", onYdocUpdate);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../../iframe-bridge/src/origin.ts","../../../iframe-bridge/src/server.ts"],"sourcesContent":["/**\n * iframe bridge 内部变更的 origin 标识。\n * 当 provider 端产生 ydoc-update 时,使用此 origin 标记,\n * 以便 server 端的 UndoManager 能正确追踪来自 iframe 的变更。\n */\nexport const IFRAME_ORIGIN: object = {};\n\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n debug?: boolean;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager, debug = false } = options ?? {};\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let 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 states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID };\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 // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n destroy: () => {\n 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.log(\"[iframe-bridge server]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n let connected = false;\n let forceFullSync = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\n const MAX_QUEUE_SIZE = 1000;\n const pendingUpdatesToIframe: Array<{ update: Uint8Array; isBaseline: boolean }> = [];\n let serverSeq = 0;\n const unackedServerUpdates = new Map<number, { update: Uint8Array; isBaseline: boolean }>();\n\n function tryAddIframeOriginTracking() {\n if (!undoManager) return;\n try {\n if (typeof (undoManager as any).addTrackedOrigin === \"function\") {\n (undoManager as any).addTrackedOrigin(IFRAME_ORIGIN);\n iframeOriginTracked = true;\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n function tryRemoveIframeOriginTracking() {\n if (!undoManager || !iframeOriginTracked) return;\n try {\n if (typeof (undoManager as any).removeTrackedOrigin === \"function\") {\n (undoManager as any).removeTrackedOrigin(IFRAME_ORIGIN);\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n tryAddIframeOriginTracking();\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n const wasForceFullSync = forceFullSync;\n forceFullSync = false;\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n }\n if (!wasForceFullSync) {\n for (const [savedSeq, { update, isBaseline }] of unackedServerUpdates) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: savedSeq }, \"*\");\n }\n }\n }\n connectListeners.forEach((fn) => fn());\n } else {\n unackedServerUpdates.clear();\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n const message = { type, payload: payload ? Array.from(payload) : [] };\n logMessage(\"send\", type, payload);\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n function postObjectToIframe(message: Record<string, unknown>) {\n const type = message.type as string;\n logMessage(\"send\", type, message);\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(message, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n logMessage(\"send\", \"ydoc-update\", update);\n if (!connected) {\n if (pendingUpdatesToIframe.length >= MAX_QUEUE_SIZE) {\n forceFullSync = true;\n pendingUpdatesToIframe.length = 0;\n console.warn(\"[iframe-bridge server] queue full, forcing full sync on reconnect\");\n }\n pendingUpdatesToIframe.push({ update, isBaseline });\n return;\n }\n const seqNum = ++serverSeq;\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline, seq: seqNum };\n cw.postMessage(message, \"*\");\n }\n unackedServerUpdates.set(seqNum, { update, isBaseline });\n };\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n logMessage(\"send\", \"undo-state\", state);\n cw.postMessage(state, \"*\");\n }\n }\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n logMessage(\"send\", \"awareness-update\", update);\n postObjectToIframe({\n type: \"awareness-update\",\n payload: Array.from(update),\n serverClientId: awareness.clientID,\n });\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"ydoc-pending-updates\") {\n logMessage(\"recv\", \"ydoc-pending-updates\", payload);\n if (Array.isArray(payload)) {\n for (const item of payload) {\n const arr = item.update ?? item;\n const isBaseline = item.isBaseline ?? false;\n const update = new Uint8Array(arr);\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n }\n }\n } else if (msgType === \"init\") {\n logMessage(\"recv\", \"init\", payload);\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n logMessage(\"send\", \"ydoc-sync\", { bytes: docState.length });\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n unackedServerUpdates.clear();\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n const states = awareness.getStates();\n const statesArray = Array.from(states.entries());\n log(\"[DEBUG] server sending awareness-sync - detailed\", {\n serverClientId: awareness.clientID,\n statesCount: states.size,\n statesArray: statesArray.map(([id, state]) => ({\n clientId: id,\n user: (state as Record<string, unknown>)?.user,\n hasUser: !!(state as Record<string, unknown>)?.user,\n })),\n });\n const message = {\n type: \"awareness-sync\",\n payload: Array.from(encodeAwarenessUpdate(\n awareness,\n Array.from(states.keys()),\n )),\n serverClientId: awareness.clientID,\n };\n log(\"[DEBUG] server sending awareness-sync\", {\n serverClientId: awareness.clientID,\n states: Object.fromEntries(states),\n payloadLength: message.payload.length,\n });\n logMessage(\"send\", \"awareness-sync\", message);\n cw.postMessage(message, \"*\");\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n logMessage(\"recv\", \"ping\", payload);\n const cw = iframe.contentWindow;\n if (cw) {\n const message = { type: \"pong\", serverClientId: awareness.clientID };\n logMessage(\"send\", \"pong\", message);\n cw.postMessage(message, \"*\");\n }\n } else if (msgType === \"ydoc-update\") {\n logMessage(\"recv\", \"ydoc-update\", payload);\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n const inSeq = event.data.seq;\n if (inSeq != null) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update-ack\", seq: inSeq }, \"*\");\n }\n }\n } else if (msgType === \"ydoc-update-ack\") {\n const ackSeq = event.data.seq;\n if (ackSeq != null) {\n unackedServerUpdates.delete(ackSeq);\n }\n } else if (msgType === \"awareness-local-state\") {\n // 保留消息类型兼容旧客户端,已不再采用\n logMessage(\"recv\", \"awareness-local-state (ignored)\", payload);\n } else if (msgType === \"set-local-state\") {\n const { key, value } = event.data as {\n key?: string;\n value?: unknown;\n };\n logMessage(\"recv\", \"set-local-state\", { key, value });\n if (typeof key === \"string\") {\n applyingIframeUpdate = true;\n awareness.setLocalStateField(key, value);\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"awareness-update\") {\n logMessage(\"recv\", \"awareness-update\", payload);\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"set-local-fields\") {\n logMessage(\"recv\", \"set-local-fields\", payload);\n const { fields } = event.data;\n if (fields && typeof fields === \"object\") {\n applyingIframeUpdate = true;\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n applyingIframeUpdate = false;\n }\n } else if (msgType === \"request-undo-state\") {\n postUndoStateToIframe();\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n destroy: () => {\n while (pendingUpdatesToIframe.length > 0) {\n const { update, isBaseline } = pendingUpdatesToIframe.shift()!;\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type: \"ydoc-update\", payload: Array.from(update), isBaseline }, \"*\");\n }\n }\n setConnected(false);\n postToIframe(\"disconnect\");\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.off(\"stack-item-popped\", onUndoPopped);\n undoManager.off(\"stack-cleared\", onStackCleared);\n undoManager.off(\"stack-item-added\", onStackItemAdded);\n tryRemoveIframeOriginTracking();\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "y-mxgraph",
3
- "version": "0.8.4",
3
+ "version": "0.8.5",
4
4
  "description": "Yjs binding for draw.io (mxGraph) documents",
5
5
  "keywords": [
6
6
  "yjs",