@nostr-wot/graph 0.2.0 → 0.3.0

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.
package/README.md CHANGED
@@ -43,7 +43,8 @@ wg.filterByWoT(pubkeys); // trusted subset, sorted by score desc
43
43
  |---|---|
44
44
  | `load()` | Hydrate the cached graph from IndexedDB. |
45
45
  | `crawl(root, opts)` | BFS-fetch kind:3 to build/refresh the graph. Concurrent calls share one in-flight promise. |
46
- | `getDistance(pubkey)` | `{ hops, paths }` from the crawled root, or `null`. |
46
+ | `getDistance(pubkey, maxHops=6)` | `{ hops, paths }` from the crawled root, or `null`. |
47
+ | `getDistances(pubkeys, maxHops=6)` | Batch distances sharing one traversal. |
47
48
  | `getScore(pubkey)` | Trust score `0..1` (`calculateScore`). |
48
49
  | `isInWoT(pubkey, maxHops=2)` | Within `maxHops` of the root. |
49
50
  | `filterByWoT(pubkeys, opts?)` | Trusted subset, sorted by score descending. |
@@ -58,13 +59,14 @@ wg.filterByWoT(pubkeys); // trusted subset, sorted by score desc
58
59
 
59
60
  ```ts
60
61
  crawl(rootPubkey, {
61
- maxDepth?: number; // default 2
62
+ maxDepth?: number; // inclusive fetched author depth, default 2
63
+ maxHops?: number; // optional hop boundary; overrides maxDepth
62
64
  onProgress?: (p) => void; // { depth, fetched, queued }
63
65
  signal?: AbortSignal; // cancel
64
66
  }): Promise<CrawlResult>; // { fetched, nodes, edges, depth, durationMs, stoppedEarly }
65
67
  ```
66
68
 
67
- Crawls tolerate unreachable relays and only throw `CrawlError` if **zero** relays are configured. In Node without an IndexedDB polyfill the store runs memory-only (crawl/query work, nothing persists).
69
+ Crawls tolerate missing relay responses. Zero configured relays throw `CrawlError`; invalid options, transport exceptions and persistence failures propagate. In Node without an IndexedDB polyfill the store runs memory-only (crawl/query work, nothing persists).
68
70
 
69
71
  ## React (`/react`)
70
72
 
@@ -102,3 +104,44 @@ await wot.getDistance(target);
102
104
  ## License
103
105
 
104
106
  MIT
107
+
108
+
109
+ ## Graph efficiency and compatibility
110
+
111
+ Crawling batches up to 100 authors per subscription (configurable through
112
+ `GraphCrawler.batchSize`). The filter has no global event limit, so one author's
113
+ history cannot consume another author's slot. The newest kind:3 wins by timestamp,
114
+ then lowest event ID; versions persist with follow rows so stale refreshes cannot
115
+ replace a newer list. A missing response retains cached follows for traversal.
116
+ Unrequested authors, other kinds and timestamps more than 60 seconds in the future
117
+ are ignored. The supplied pool remains responsible for event signature verification.
118
+
119
+ `maxDepth` keeps its existing inclusive author-depth meaning: `maxDepth: 1` fetches
120
+ the root and its direct follows, discovering people two hops away. Use `maxHops: 2`
121
+ to fetch just the lists needed for two-hop reachability; authors at distance two
122
+ are not fetched. `maxHops: 0` sends no requests. Query defaults remain six hops;
123
+ pass an explicit query bound when exploring further.
124
+
125
+ Storage interns keys, deduplicates adjacency and maintains edge counts as rows
126
+ change. IndexedDB schema version 2 writes delta-varint rows; existing version-1
127
+ fixed-width rows load without a full rewrite and migrate when updated. Older SDK
128
+ versions cannot reopen an upgraded namespace (IndexedDB reports `VersionError`),
129
+ so rolling back requires a different namespace or clearing the upgraded database.
130
+ Transactions preserve pending rows on failure, serialize flushes, and atomically
131
+ write each batch of follow rows with its key mappings and event versions. Metadata
132
+ writes use one separate transaction. A crawl is **not** a whole-graph transaction:
133
+ partial results remain queryable, and stopped crawls are marked stale.
134
+
135
+ One numeric BFS serves repeated queries and shallower bounds until edges change
136
+ or a deeper traversal is needed. Duplicate follows do not inflate paths. Distances
137
+ use 32-bit arrays and path counts use 64-bit floats, saturated at
138
+ `Number.MAX_SAFE_INTEGER`; this uses more traversal memory than the former 8-bit
139
+ hops/32-bit paths while avoiding overflow. No TTL-based incremental relay sync,
140
+ root-based pruning or cross-instance live cache synchronization is implemented.
141
+
142
+ Run `npm run benchmark -w @nostr-wot/graph` from the repository root for reproducible
143
+ synthetic construction, cold traversal and warm retrieval timings. Regression
144
+ fixtures verify 251 authors use 4 requests instead of 251 (98.4% fewer), 1,000 dense
145
+ follow IDs use 1,000 encoded bytes instead of 4,000 (75% smaller payload), and 1,000
146
+ unchanged queries traverse a three-node graph only once. These figures describe
147
+ fixtures; relay latency and IndexedDB record overhead are not included.