fix: reconstruct Map from JSON for HNSW connections (v5.7.8 hotfix)

CRITICAL FIX for Workshop team's "noun.connections.entries is not a function" error.

**Root Cause:**
When HNSW data is loaded from JSON storage via getNounsWithPagination(), the
`connections` field (which should be Map<number, Set<string>>) is deserialized
as a plain JavaScript object {}. Subsequent code that expects a Map with
.entries() method crashes.

**The Fix:**
Added defensive deserialization in baseStorage.ts:1156-1168 to reconstruct
the Map and Sets from the plain object when loading from storage:

```typescript
const connections = new Map<number, Set<string>>()
if (noun.connections && typeof noun.connections === 'object') {
  for (const [levelStr, ids] of Object.entries(noun.connections)) {
    if (Array.isArray(ids)) {
      connections.set(parseInt(levelStr, 10), new Set<string>(ids))
    }
  }
}
```

**Impact:**
- Fixes HNSW index rebuild failures
- Workshop team can now use v5.7.7+ lazy loading without crashes
- All existing data formats supported (defensive checks)

**Testing:**
- Build:  PASS (zero TypeScript errors)
- Will run full test suite in CI

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
David Snelling 2025-11-13 10:45:06 -08:00
parent 5199da9737
commit f6f2717860

View file

@ -1153,9 +1153,24 @@ export abstract class BaseStorage extends BaseStorageAdapter {
} }
} }
// v5.7.8: Convert connections from plain object to Map (JSON deserialization fix)
// When loaded from JSON, Map becomes plain object - must reconstruct
const connections = new Map<number, Set<string>>()
if (noun.connections && typeof noun.connections === 'object') {
for (const [levelStr, ids] of Object.entries(noun.connections)) {
if (Array.isArray(ids)) {
connections.set(parseInt(levelStr, 10), new Set<string>(ids))
} else if (ids && typeof ids === 'object') {
// Handle if it's already an array-like or Set-like object
connections.set(parseInt(levelStr, 10), new Set<string>(Object.values(ids)))
}
}
}
// Combine noun + metadata (v5.4.0: Extract standard fields to top-level) // Combine noun + metadata (v5.4.0: Extract standard fields to top-level)
collectedNouns.push({ collectedNouns.push({
...noun, ...noun,
connections, // Use reconstructed Map instead of plain object
type: metadata.noun || type, // Required: Extract type from metadata type: metadata.noun || type, // Required: Extract type from metadata
confidence: metadata.confidence, confidence: metadata.confidence,
weight: metadata.weight, weight: metadata.weight,