Merge pull request #16 from Jeff-Emmett/add-runpod-AI-API

Add runpod ai api
This commit is contained in:
Jeff Emmett 2025-11-16 05:07:29 -07:00 committed by GitHub
commit 58bcd033d6
2 changed files with 72 additions and 66 deletions

View File

@ -116,59 +116,66 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
console.log("🔌 Initializing Automerge Repo with NetworkAdapter for room:", roomId) console.log("🔌 Initializing Automerge Repo with NetworkAdapter for room:", roomId)
if (mounted) { if (mounted) {
// CRITICAL: Create a new Automerge document (repo.create() generates a proper document ID) // CRITICAL: Use repo.find() with a consistent document ID based on roomId
// We can't use repo.find() with a custom ID because Automerge requires specific document ID formats // This ensures all windows/tabs share the same Automerge document and can sync properly
// Instead, we'll create a new document and load initial data from the server // Format: automerge:${roomId} matches what the server expects (see AutomergeDurableObject.ts line 327)
const handle = repo.create() const documentId = `automerge:${roomId}` as any
console.log(`🔌 Finding or creating Automerge document with ID: ${documentId}`)
console.log("Created Automerge handle via Repo:", { // Use repo.find() to get or create the document with this ID
// This ensures all windows share the same document instance
// Note: repo.find() returns a Promise, so we await it
const handle = await repo.find(documentId)
console.log("Found/Created Automerge handle via Repo:", {
handleId: handle.documentId, handleId: handle.documentId,
isReady: handle.isReady() isReady: handle.isReady(),
roomId: roomId
}) })
// Wait for the handle to be ready // Wait for the handle to be ready
await handle.whenReady() await handle.whenReady()
// CRITICAL: Always load initial data from the server // Initialize document with default store if it's new/empty
// The server stores documents in R2 as JSON, so we need to load and initialize the Automerge document const currentDoc = handle.doc() as any
console.log("📥 Loading initial data from server...") if (!currentDoc || !currentDoc.store || Object.keys(currentDoc.store).length === 0) {
try { console.log("📝 Document is new/empty - initializing with default store")
const response = await fetch(`${workerUrl}/room/${roomId}`)
if (response.ok) {
const serverDoc = await response.json() as TLStoreSnapshot
const serverShapeCount = serverDoc.store ? Object.values(serverDoc.store).filter((r: any) => r?.typeName === 'shape').length : 0
const serverRecordCount = Object.keys(serverDoc.store || {}).length
console.log(`📥 Loaded document from server: ${serverRecordCount} records, ${serverShapeCount} shapes`) // Try to load initial data from server for new documents
try {
const response = await fetch(`${workerUrl}/room/${roomId}`)
if (response.ok) {
const serverDoc = await response.json() as TLStoreSnapshot
const serverRecordCount = Object.keys(serverDoc.store || {}).length
// Initialize the Automerge document with server data if (serverDoc.store && serverRecordCount > 0) {
// CRITICAL: This will generate patches that should be caught by the handler in useAutomergeStoreV2 console.log(`📥 Loading ${serverRecordCount} records from server into new document`)
// The handler is set up before initializeStore() runs, so patches should be processed automatically handle.change((doc: any) => {
if (serverDoc.store && serverRecordCount > 0) { // Initialize store if it doesn't exist
handle.change((doc: any) => { if (!doc.store) {
// Initialize store if it doesn't exist doc.store = {}
if (!doc.store) { }
doc.store = {} // Copy all records from server document
} Object.entries(serverDoc.store).forEach(([id, record]) => {
// Copy all records from server document doc.store[id] = record
Object.entries(serverDoc.store).forEach(([id, record]) => { })
doc.store[id] = record
}) })
}) console.log(`✅ Initialized Automerge document with ${serverRecordCount} records from server`)
} else {
console.log(`✅ Initialized Automerge document with ${serverRecordCount} records from server`) console.log("📥 Server document is empty - document will start empty")
console.log(`📝 Patches should be generated and caught by handler in useAutomergeStoreV2`) }
} else if (response.status === 404) {
console.log("📥 No document found on server (404) - starting with empty document")
} else { } else {
console.log("📥 Server document is empty - starting with empty Automerge document") console.warn(`⚠️ Failed to load document from server: ${response.status} ${response.statusText}`)
} }
} else if (response.status === 404) { } catch (error) {
console.log("📥 No document found on server (404) - starting with empty document") console.error("❌ Error loading initial document from server:", error)
} else { // Continue anyway - document will start empty and sync via WebSocket
console.warn(`⚠️ Failed to load document from server: ${response.status} ${response.statusText}`)
} }
} catch (error) { } else {
console.error("❌ Error loading initial document from server:", error) const existingRecordCount = Object.keys(currentDoc.store || {}).length
// Continue anyway - user can still create new content console.log(`✅ Document already has ${existingRecordCount} records - ready to sync`)
} }
const finalDoc = handle.doc() as any const finalDoc = handle.doc() as any
@ -302,11 +309,9 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
} }
}) })
// Only log in dev mode to reduce overhead // CRITICAL: Always log saves to help debug persistence issues
if (process.env.NODE_ENV === 'development') { const shapeCount = Object.values(doc.store).filter((r: any) => r?.typeName === 'shape').length
const shapeCount = Object.values(doc.store).filter((r: any) => r?.typeName === 'shape').length console.log(`💾 Persisting document to worker for R2 storage: ${storeKeys} records, ${shapeCount} shapes`)
console.log(`💾 Persisting document to worker for R2 storage: ${storeKeys} records, ${shapeCount} shapes`)
}
// Send document state to worker via POST /room/:roomId // Send document state to worker via POST /room/:roomId
// This updates the worker's currentDoc so it can be persisted to R2 // This updates the worker's currentDoc so it can be persisted to R2
@ -325,10 +330,9 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
// Update last sent hash only after successful save // Update last sent hash only after successful save
lastSentHashRef.current = currentHash lastSentHashRef.current = currentHash
pendingSaveRef.current = false pendingSaveRef.current = false
if (process.env.NODE_ENV === 'development') { // CRITICAL: Always log successful saves
const shapeCount = Object.values(doc.store).filter((r: any) => r?.typeName === 'shape').length const finalShapeCount = Object.values(doc.store).filter((r: any) => r?.typeName === 'shape').length
console.log(`✅ Successfully sent document state to worker for persistence (${shapeCount} shapes)`) console.log(`✅ Successfully sent document state to worker for persistence (${finalShapeCount} shapes)`)
}
} catch (error) { } catch (error) {
console.error('❌ Error saving document to worker:', error) console.error('❌ Error saving document to worker:', error)
pendingSaveRef.current = false pendingSaveRef.current = false
@ -419,12 +423,9 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
// If all patches are for ephemeral records, skip persistence // If all patches are for ephemeral records, skip persistence
if (hasOnlyEphemeralChanges) { if (hasOnlyEphemeralChanges) {
// Only log in dev mode to reduce overhead console.log('🚫 Skipping persistence - only ephemeral changes detected:', {
if (process.env.NODE_ENV === 'development') { patchCount
console.log('🚫 Skipping persistence - only ephemeral changes detected:', { })
patchCount
})
}
return return
} }
@ -476,11 +477,9 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
}) })
if (allPinned) { if (allPinned) {
if (process.env.NODE_ENV === 'development') { console.log('🚫 Skipping persistence - only pinned-to-view position updates detected:', {
console.log('🚫 Skipping persistence - only pinned-to-view position updates detected:', { patchCount: payload.patches.length
patchCount: payload.patches.length })
})
}
return return
} }
@ -495,8 +494,8 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
return id && typeof id === 'string' && id.startsWith('shape:') return id && typeof id === 'string' && id.startsWith('shape:')
}) })
// Only log in dev mode and reduce logging frequency // CRITICAL: Always log shape changes to debug persistence
if (process.env.NODE_ENV === 'development' && shapePatches.length > 0) { if (shapePatches.length > 0) {
console.log('🔍 Automerge document changed with shape patches:', { console.log('🔍 Automerge document changed with shape patches:', {
patchCount: patchCount, patchCount: patchCount,
shapePatches: shapePatches.length shapePatches: shapePatches.length

7
wrangler.jsonc Normal file
View File

@ -0,0 +1,7 @@
{
"name": "jeffemmett-canvas",
"compatibility_date": "2025-11-16",
"assets": {
"directory": "./dist"
}
}