fix: add debug logging and re-emit peer-candidate for Automerge sync

- Add extensive debug logging to track sync message flow
- Re-emit peer-candidate after documentId is set to trigger Repo sync
- Fix timing issue where peer connected before document existed
- This should enable Automerge binary sync protocol (task-027)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-25 18:59:21 -05:00
parent 8628ccc1b8
commit 6864137f85
1 changed files with 56 additions and 2 deletions

View File

@ -258,10 +258,19 @@ export class CloudflareNetworkAdapter extends NetworkAdapter {
* @param documentId The Automerge document ID to use for incoming messages
*/
setDocumentId(documentId: string): void {
const previousDocId = this.currentDocumentId
this.currentDocumentId = documentId
console.log(`🔌 CloudflareAdapter.setDocumentId():`, {
documentId,
previousDocId,
hasServerPeer: !!this.serverPeerId,
wsOpen: this.websocket?.readyState === WebSocket.OPEN
})
// Process any buffered binary messages now that we have a documentId
if (this.pendingBinaryMessages.length > 0) {
console.log(`🔌 CloudflareAdapter: Processing ${this.pendingBinaryMessages.length} buffered binary messages`)
const bufferedMessages = this.pendingBinaryMessages
this.pendingBinaryMessages = []
@ -276,6 +285,18 @@ export class CloudflareNetworkAdapter extends NetworkAdapter {
this.emit('message', message)
}
}
// CRITICAL: Re-emit peer-candidate now that we have a documentId
// This triggers the Repo to sync this document with the server peer
// Without this, the Repo may have connected before the document was created
// and won't know to sync the document with the peer
if (this.serverPeerId && this.websocket?.readyState === WebSocket.OPEN && !previousDocId) {
console.log(`🔌 CloudflareAdapter: Re-emitting peer-candidate after documentId set`)
this.emit('peer-candidate', {
peerId: this.serverPeerId,
peerMetadata: { storageId: undefined, isEphemeral: false }
})
}
}
/**
@ -286,7 +307,15 @@ export class CloudflareNetworkAdapter extends NetworkAdapter {
}
connect(peerId: PeerId, peerMetadata?: PeerMetadata): void {
console.log(`🔌 CloudflareAdapter.connect() called:`, {
peerId,
peerMetadata,
roomId: this.roomId,
isConnecting: this.isConnecting
})
if (this.isConnecting) {
console.log(`🔌 CloudflareAdapter.connect(): Already connecting, skipping`)
return
}
@ -324,13 +353,18 @@ export class CloudflareNetworkAdapter extends NetworkAdapter {
this.startKeepAlive()
// Emit 'ready' event for Automerge Repo
// @ts-expect-error - 'ready' event is valid but not in NetworkAdapterEvents type
this.emit('ready', { network: this })
console.log(`🔌 CloudflareAdapter: Emitting 'ready' event`)
// Use type assertion to emit 'ready' event which isn't in NetworkAdapterEvents
;(this as any).emit('ready', { network: this })
// Create a server peer ID based on the room
this.serverPeerId = `server-${this.roomId}` as PeerId
// Emit 'peer-candidate' to announce the server as a sync peer
console.log(`🔌 CloudflareAdapter: Emitting 'peer-candidate' for server:`, {
peerId: this.serverPeerId,
peerMetadata: { storageId: undefined, isEphemeral: false }
})
this.emit('peer-candidate', {
peerId: this.serverPeerId,
peerMetadata: { storageId: undefined, isEphemeral: false }
@ -473,25 +507,45 @@ export class CloudflareNetworkAdapter extends NetworkAdapter {
}
send(message: Message): void {
// DEBUG: Log all outgoing messages to trace Automerge Repo sync
const isBinarySync = message.type === 'sync' &&
((message as any).data instanceof ArrayBuffer || (message as any).data instanceof Uint8Array)
console.log(`📤 CloudflareAdapter.send():`, {
type: message.type,
isBinarySync,
hasData: !!(message as any).data,
dataType: (message as any).data ? (message as any).data.constructor?.name : 'none',
documentId: (message as any).documentId,
targetId: (message as any).targetId,
senderId: (message as any).senderId,
wsOpen: this.websocket?.readyState === WebSocket.OPEN
})
// Capture documentId from outgoing sync messages
if (message.type === 'sync' && (message as any).documentId) {
const docId = (message as any).documentId
if (this.currentDocumentId !== docId) {
this.currentDocumentId = docId
console.log(`📤 CloudflareAdapter: Captured documentId: ${docId}`)
}
}
if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
// Check if this is a binary sync message from Automerge Repo
if (message.type === 'sync' && (message as any).data instanceof ArrayBuffer) {
console.log(`📤 CloudflareAdapter: Sending binary ArrayBuffer (${(message as any).data.byteLength} bytes)`)
this.websocket.send((message as any).data)
return
} else if (message.type === 'sync' && (message as any).data instanceof Uint8Array) {
console.log(`📤 CloudflareAdapter: Sending binary Uint8Array (${(message as any).data.byteLength} bytes)`)
this.websocket.send((message as any).data)
return
} else {
console.log(`📤 CloudflareAdapter: Sending JSON message`)
this.websocket.send(JSON.stringify(message))
}
} else {
console.warn(`📤 CloudflareAdapter: WebSocket not open, message not sent`)
}
}