@soulcraft/brainy 5.7.9 → 5.7.10

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.
@@ -458,6 +458,30 @@ export declare abstract class BaseStorage extends BaseStorageAdapter {
458
458
  * Verb type is a required field in HNSWVerb
459
459
  */
460
460
  protected getVerbType(verb: HNSWVerb | GraphVerb): VerbType;
461
+ /**
462
+ * Deserialize HNSW connections from JSON storage format
463
+ *
464
+ * Converts plain object { "0": ["id1"], "1": ["id2"] }
465
+ * into Map<number, Set<string>>
466
+ *
467
+ * v5.7.10: Central helper to fix serialization bug across all code paths
468
+ * Root cause: JSON.stringify(Map) = {} (empty object), must reconstruct on read
469
+ */
470
+ protected deserializeConnections(connections: any): Map<number, Set<string>>;
471
+ /**
472
+ * Deserialize HNSWNoun from JSON storage format
473
+ *
474
+ * v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
475
+ * Fixes: "TypeError: noun.connections.entries is not a function"
476
+ */
477
+ protected deserializeNoun(data: any): HNSWNoun;
478
+ /**
479
+ * Deserialize HNSWVerb from JSON storage format
480
+ *
481
+ * v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
482
+ * Fixes same serialization bug for verbs
483
+ */
484
+ protected deserializeVerb(data: any): HNSWVerb;
461
485
  /**
462
486
  * Save a noun to storage (type-first path)
463
487
  */
@@ -912,8 +912,11 @@ export class BaseStorage extends BaseStorageAdapter {
912
912
  if (!nounPath.endsWith('.json'))
913
913
  continue;
914
914
  try {
915
- const noun = await this.readWithInheritance(nounPath);
916
- if (noun) {
915
+ const rawNoun = await this.readWithInheritance(nounPath);
916
+ if (rawNoun) {
917
+ // v5.7.10: Deserialize connections Map from JSON storage format
918
+ // Replaces v5.7.8 manual deserialization (removed 13 lines at 1156-1168)
919
+ const noun = this.deserializeNoun(rawNoun);
917
920
  // Load metadata
918
921
  const metadataPath = getNounMetadataPath(type, noun.id);
919
922
  const metadata = await this.readWithInheritance(metadataPath);
@@ -925,24 +928,10 @@ export class BaseStorage extends BaseStorageAdapter {
925
928
  continue;
926
929
  }
927
930
  }
928
- // v5.7.8: Convert connections from plain object to Map (JSON deserialization fix)
929
- // When loaded from JSON, Map becomes plain object - must reconstruct
930
- const connections = new Map();
931
- if (noun.connections && typeof noun.connections === 'object') {
932
- for (const [levelStr, ids] of Object.entries(noun.connections)) {
933
- if (Array.isArray(ids)) {
934
- connections.set(parseInt(levelStr, 10), new Set(ids));
935
- }
936
- else if (ids && typeof ids === 'object') {
937
- // Handle if it's already an array-like or Set-like object
938
- connections.set(parseInt(levelStr, 10), new Set(Object.values(ids)));
939
- }
940
- }
941
- }
942
931
  // Combine noun + metadata (v5.4.0: Extract standard fields to top-level)
943
932
  collectedNouns.push({
944
933
  ...noun,
945
- connections, // Use reconstructed Map instead of plain object
934
+ // v5.7.10: connections already deserialized by deserializeNoun()
946
935
  type: metadata.noun || type, // Required: Extract type from metadata
947
936
  confidence: metadata.confidence,
948
937
  weight: metadata.weight,
@@ -1731,6 +1720,64 @@ export class BaseStorage extends BaseStorageAdapter {
1731
1720
  return 'relatedTo';
1732
1721
  }
1733
1722
  // ============================================================================
1723
+ // DESERIALIZATION HELPERS (v5.7.10)
1724
+ // Centralized Map/Set reconstruction from JSON storage format
1725
+ // ============================================================================
1726
+ /**
1727
+ * Deserialize HNSW connections from JSON storage format
1728
+ *
1729
+ * Converts plain object { "0": ["id1"], "1": ["id2"] }
1730
+ * into Map<number, Set<string>>
1731
+ *
1732
+ * v5.7.10: Central helper to fix serialization bug across all code paths
1733
+ * Root cause: JSON.stringify(Map) = {} (empty object), must reconstruct on read
1734
+ */
1735
+ deserializeConnections(connections) {
1736
+ const result = new Map();
1737
+ if (!connections || typeof connections !== 'object') {
1738
+ return result;
1739
+ }
1740
+ // Already a Map (in-memory, not from JSON)
1741
+ if (connections instanceof Map) {
1742
+ return connections;
1743
+ }
1744
+ // Deserialize from plain object
1745
+ for (const [levelStr, ids] of Object.entries(connections)) {
1746
+ if (Array.isArray(ids)) {
1747
+ result.set(parseInt(levelStr, 10), new Set(ids));
1748
+ }
1749
+ else if (ids && typeof ids === 'object') {
1750
+ // Handle Set-like or array-like objects
1751
+ result.set(parseInt(levelStr, 10), new Set(Object.values(ids)));
1752
+ }
1753
+ }
1754
+ return result;
1755
+ }
1756
+ /**
1757
+ * Deserialize HNSWNoun from JSON storage format
1758
+ *
1759
+ * v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
1760
+ * Fixes: "TypeError: noun.connections.entries is not a function"
1761
+ */
1762
+ deserializeNoun(data) {
1763
+ return {
1764
+ ...data,
1765
+ connections: this.deserializeConnections(data.connections)
1766
+ };
1767
+ }
1768
+ /**
1769
+ * Deserialize HNSWVerb from JSON storage format
1770
+ *
1771
+ * v5.7.10: Ensures connections are properly reconstructed from Map → object → Map
1772
+ * Fixes same serialization bug for verbs
1773
+ */
1774
+ deserializeVerb(data) {
1775
+ return {
1776
+ ...data,
1777
+ connections: this.deserializeConnections(data.connections)
1778
+ };
1779
+ }
1780
+ // ============================================================================
1734
1781
  // ABSTRACT METHOD IMPLEMENTATIONS (v5.4.0)
1735
1782
  // Converted from abstract to concrete - all adapters now have built-in type-aware
1736
1783
  // ============================================================================
@@ -1760,7 +1807,9 @@ export class BaseStorage extends BaseStorageAdapter {
1760
1807
  if (cachedType) {
1761
1808
  const path = getNounVectorPath(cachedType, id);
1762
1809
  // COW-aware read (v5.0.1): Use COW helper for branch isolation
1763
- return await this.readWithInheritance(path);
1810
+ const data = await this.readWithInheritance(path);
1811
+ // v5.7.10: Deserialize connections Map from JSON storage format
1812
+ return data ? this.deserializeNoun(data) : null;
1764
1813
  }
1765
1814
  // Need to search across all types (expensive, but cached after first access)
1766
1815
  for (let i = 0; i < NOUN_TYPE_COUNT; i++) {
@@ -1772,7 +1821,8 @@ export class BaseStorage extends BaseStorageAdapter {
1772
1821
  if (noun) {
1773
1822
  // Cache the type for next time
1774
1823
  this.nounTypeCache.set(id, type);
1775
- return noun;
1824
+ // v5.7.10: Deserialize connections Map from JSON storage format
1825
+ return this.deserializeNoun(noun);
1776
1826
  }
1777
1827
  }
1778
1828
  catch (error) {
@@ -1796,7 +1846,8 @@ export class BaseStorage extends BaseStorageAdapter {
1796
1846
  // COW-aware read (v5.0.1): Use COW helper for branch isolation
1797
1847
  const noun = await this.readWithInheritance(path);
1798
1848
  if (noun) {
1799
- nouns.push(noun);
1849
+ // v5.7.10: Deserialize connections Map from JSON storage format
1850
+ nouns.push(this.deserializeNoun(noun));
1800
1851
  // Cache the type
1801
1852
  this.nounTypeCache.set(noun.id, type);
1802
1853
  }
@@ -1891,7 +1942,8 @@ export class BaseStorage extends BaseStorageAdapter {
1891
1942
  const path = getVerbVectorPath(cachedType, id);
1892
1943
  // COW-aware read (v5.0.1): Use COW helper for branch isolation
1893
1944
  const verb = await this.readWithInheritance(path);
1894
- return verb;
1945
+ // v5.7.10: Deserialize connections Map from JSON storage format
1946
+ return verb ? this.deserializeVerb(verb) : null;
1895
1947
  }
1896
1948
  // Search across all types (only on first access)
1897
1949
  for (let i = 0; i < VERB_TYPE_COUNT; i++) {
@@ -1903,7 +1955,8 @@ export class BaseStorage extends BaseStorageAdapter {
1903
1955
  if (verb) {
1904
1956
  // Cache the type for next time (read from verb.verb field)
1905
1957
  this.verbTypeCache.set(id, verb.verb);
1906
- return verb;
1958
+ // v5.7.10: Deserialize connections Map from JSON storage format
1959
+ return this.deserializeVerb(verb);
1907
1960
  }
1908
1961
  }
1909
1962
  catch (error) {
@@ -2040,29 +2093,24 @@ export class BaseStorage extends BaseStorageAdapter {
2040
2093
  for (const path of paths) {
2041
2094
  try {
2042
2095
  // COW-aware read (v5.0.1): Use COW helper for branch isolation
2043
- const hnswVerb = await this.readWithInheritance(path);
2044
- if (!hnswVerb)
2096
+ const rawVerb = await this.readWithInheritance(path);
2097
+ if (!rawVerb)
2045
2098
  continue;
2099
+ // v5.7.10: Deserialize connections Map from JSON storage format
2100
+ // Replaces v5.7.8 manual deserialization (lines 2599-2605)
2101
+ const hnswVerb = this.deserializeVerb(rawVerb);
2046
2102
  // Cache type from HNSWVerb for future O(1) retrievals
2047
2103
  this.verbTypeCache.set(hnswVerb.id, hnswVerb.verb);
2048
2104
  // Load metadata separately (optional in v4.0.0!)
2049
2105
  // FIX: Don't skip verbs without metadata - metadata is optional!
2050
2106
  const metadata = await this.getVerbMetadata(hnswVerb.id);
2051
- // Create HNSWVerbWithMetadata (verbs don't have level field)
2052
- // Convert connections from plain object to Map<number, Set<string>>
2053
- const connectionsMap = new Map();
2054
- if (hnswVerb.connections && typeof hnswVerb.connections === 'object') {
2055
- for (const [level, ids] of Object.entries(hnswVerb.connections)) {
2056
- connectionsMap.set(Number(level), new Set(ids));
2057
- }
2058
- }
2059
2107
  // v4.8.0: Extract standard fields from metadata to top-level
2060
2108
  const metadataObj = (metadata || {});
2061
2109
  const { createdAt, updatedAt, confidence, weight, service, data, createdBy, ...customMetadata } = metadataObj;
2062
2110
  const verbWithMetadata = {
2063
2111
  id: hnswVerb.id,
2064
2112
  vector: [...hnswVerb.vector],
2065
- connections: connectionsMap,
2113
+ connections: hnswVerb.connections, // v5.7.10: Already deserialized
2066
2114
  verb: hnswVerb.verb,
2067
2115
  sourceId: hnswVerb.sourceId,
2068
2116
  targetId: hnswVerb.targetId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "5.7.9",
3
+ "version": "5.7.10",
4
4
  "description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. Stage 3 CANONICAL: 42 nouns × 127 verbs covering 96-97% of all human knowledge.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",