nostr-wot-sdk 0.6.2 → 0.7.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/CHANGELOG.md +23 -0
- package/README.md +197 -0
- package/dist/relay/index.cjs +880 -0
- package/dist/relay/index.cjs.map +1 -0
- package/dist/relay/index.d.cts +280 -0
- package/dist/relay/index.d.ts +280 -0
- package/dist/relay/index.js +876 -0
- package/dist/relay/index.js.map +1 -0
- package/dist/relay/react/index.cjs +967 -0
- package/dist/relay/react/index.cjs.map +1 -0
- package/dist/relay/react/index.d.cts +342 -0
- package/dist/relay/react/index.d.ts +342 -0
- package/dist/relay/react/index.js +957 -0
- package/dist/relay/react/index.js.map +1 -0
- package/package.json +26 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.0] - 2026-02-23
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Relay utilities subpackage** (`nostr-wot-sdk/relay`) — Reusable relay infrastructure extracted from nostr-wot-feed
|
|
13
|
+
- `QueryBatcher` — Debounced query batching with progressive relay queries and automatic filter merging
|
|
14
|
+
- `RelayPool` — Pool lifecycle management, subscriptions, publishing, status tracking, and NIP-65 relay list fetching
|
|
15
|
+
- `RelayStats` — Per-relay performance tracking (latency, success rate, exponential backoff) with optional persistence adapter
|
|
16
|
+
- All classes are pool-agnostic via a `PoolLike` interface that duck-types against `SimplePool` from nostr-tools
|
|
17
|
+
- All timing constants are configurable via options with sensible defaults
|
|
18
|
+
|
|
19
|
+
- **Relay React integration** (`nostr-wot-sdk/relay/react`)
|
|
20
|
+
- `RelayProvider` — Context provider that manages RelayPool, QueryBatcher, and RelayStats lifecycle
|
|
21
|
+
- `useRelayPool()` — Access the RelayPool instance
|
|
22
|
+
- `useQueryBatcher()` — Access the QueryBatcher instance
|
|
23
|
+
- `useRelayStats()` — Access the RelayStats instance
|
|
24
|
+
- `useRelayStatuses()` — Reactive relay connection statuses and connected count
|
|
25
|
+
|
|
26
|
+
- `nostr-tools` as optional peer dependency (`>=2.0.0`)
|
|
27
|
+
|
|
28
|
+
- New types: `PoolLike`, `SubCloser`, `NostrEvent`, `NostrFilter`, `QueryBatcherOptions`, `QueryOptions`, `RelayPoolOptions`, `RelayStatus`, `RelayStatsOptions`, `RelayMetrics`, `RelayStatsData`, `RelayStatsPersistence`
|
|
29
|
+
|
|
8
30
|
## [0.6.2] - 2026-02-22
|
|
9
31
|
|
|
10
32
|
### Fixed
|
|
@@ -270,6 +292,7 @@ function Profile({ pubkey }) {
|
|
|
270
292
|
- TypeScript support with full type definitions
|
|
271
293
|
- Error classes: `WoTError`, `NetworkError`, `NotFoundError`, `TimeoutError`, `ValidationError`
|
|
272
294
|
|
|
295
|
+
[0.7.0]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.6.2...v0.7.0
|
|
273
296
|
[0.6.2]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.6.1...v0.6.2
|
|
274
297
|
[0.6.1]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.6.0...v0.6.1
|
|
275
298
|
[0.6.0]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.5.3...v0.6.0
|
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ const hops = await wot.getDistance('def456...');
|
|
|
59
59
|
- **Cross-Site Trust** — Extension provides same WoT data on all websites
|
|
60
60
|
- **Offline Support** — Extension caches data locally for offline queries
|
|
61
61
|
- **Batch Queries** — Check multiple pubkeys efficiently
|
|
62
|
+
- **Relay Utilities** — Reusable relay pool, query batching, and stats tracking
|
|
62
63
|
- **TypeScript** — Full type definitions included
|
|
63
64
|
|
|
64
65
|
## API Reference
|
|
@@ -443,6 +444,202 @@ ext.isChecked() // Check complete
|
|
|
443
444
|
ext.refresh() // Function to re-check extension availability
|
|
444
445
|
```
|
|
445
446
|
|
|
447
|
+
## Relay Utilities
|
|
448
|
+
|
|
449
|
+
The SDK includes a standalone relay subpackage for managing Nostr relay connections, batching queries, and tracking relay performance. These utilities are pool-agnostic and work with any `PoolLike` implementation (e.g. `SimplePool` from nostr-tools).
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
npm install nostr-wot-sdk nostr-tools
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Basic Usage
|
|
456
|
+
|
|
457
|
+
```javascript
|
|
458
|
+
import { QueryBatcher, RelayPool, RelayStats } from 'nostr-wot-sdk/relay';
|
|
459
|
+
import { SimplePool } from 'nostr-tools';
|
|
460
|
+
|
|
461
|
+
// Optional: track relay performance
|
|
462
|
+
const stats = new RelayStats();
|
|
463
|
+
await stats.init(); // works in-memory, or pass a persistence adapter
|
|
464
|
+
|
|
465
|
+
// Create a relay pool
|
|
466
|
+
const pool = new RelayPool({
|
|
467
|
+
urls: ['wss://relay.damus.io', 'wss://nos.lol'],
|
|
468
|
+
prioritizeUrls: (urls) => stats.getPrioritizedUrls(urls),
|
|
469
|
+
onStatusChange: (statuses) => console.log('Relay statuses:', statuses),
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Initialize with SimplePool
|
|
473
|
+
pool.ensurePool(() => new SimplePool());
|
|
474
|
+
|
|
475
|
+
// Query with automatic batching + progressive results
|
|
476
|
+
const events = await pool.query(
|
|
477
|
+
{ kinds: [1], limit: 50 },
|
|
478
|
+
{ onUpdate: (partial) => renderNotes(partial) }
|
|
479
|
+
);
|
|
480
|
+
|
|
481
|
+
// Subscribe to live events
|
|
482
|
+
const sub = pool.subscribe({ kinds: [1], since: Math.floor(Date.now() / 1000) }, {
|
|
483
|
+
onEvent: (event) => console.log('New note:', event.content),
|
|
484
|
+
onEose: () => console.log('Caught up'),
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Clean up
|
|
488
|
+
sub.close();
|
|
489
|
+
pool.destroy();
|
|
490
|
+
stats.destroy();
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### QueryBatcher
|
|
494
|
+
|
|
495
|
+
Debounces and merges concurrent relay queries for efficiency. Queries made within the debounce window are batched together, compatible filters are merged, and results stream progressively.
|
|
496
|
+
|
|
497
|
+
```javascript
|
|
498
|
+
import { QueryBatcher } from 'nostr-wot-sdk/relay';
|
|
499
|
+
import { SimplePool } from 'nostr-tools';
|
|
500
|
+
|
|
501
|
+
const batcher = new QueryBatcher(new SimplePool(), {
|
|
502
|
+
debounceMs: 100, // batch window (default: 100)
|
|
503
|
+
collectionWindowMs: 200, // wait after first event (default: 200)
|
|
504
|
+
maxWaitMs: 5000, // hard timeout (default: 5000)
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// These concurrent queries get merged into fewer relay requests
|
|
508
|
+
const [profiles, contacts] = await Promise.all([
|
|
509
|
+
batcher.query(['wss://relay.damus.io'], { kinds: [0], authors: pubkeys }),
|
|
510
|
+
batcher.query(['wss://relay.damus.io'], { kinds: [3], authors: pubkeys }),
|
|
511
|
+
]);
|
|
512
|
+
|
|
513
|
+
// For user-initiated actions, bypass the debounce
|
|
514
|
+
const notes = await batcher.queryImmediate(urls, { kinds: [1], limit: 20 });
|
|
515
|
+
|
|
516
|
+
batcher.destroy();
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
### RelayStats
|
|
520
|
+
|
|
521
|
+
Tracks per-relay latency, success rate, and applies exponential backoff to failing relays.
|
|
522
|
+
|
|
523
|
+
```javascript
|
|
524
|
+
import { RelayStats } from 'nostr-wot-sdk/relay';
|
|
525
|
+
|
|
526
|
+
const stats = new RelayStats({ maxBackoffMs: 30000 });
|
|
527
|
+
|
|
528
|
+
// Optional: load persisted stats (e.g. from IndexedDB)
|
|
529
|
+
await stats.init({
|
|
530
|
+
load: async () => db.getAll('relayStats'),
|
|
531
|
+
save: async (data) => db.putAll('relayStats', data),
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// Record relay performance
|
|
535
|
+
stats.recordSuccess('wss://relay.damus.io', 150); // 150ms latency
|
|
536
|
+
stats.recordFailure('wss://slow.relay.com', 'timeout');
|
|
537
|
+
|
|
538
|
+
// Get URLs sorted by reliability + speed
|
|
539
|
+
const prioritized = stats.getPrioritizedUrls([
|
|
540
|
+
'wss://relay.damus.io',
|
|
541
|
+
'wss://slow.relay.com',
|
|
542
|
+
'wss://nos.lol',
|
|
543
|
+
]);
|
|
544
|
+
|
|
545
|
+
// Check backoff status
|
|
546
|
+
stats.isBackedOff('wss://slow.relay.com'); // true
|
|
547
|
+
|
|
548
|
+
stats.destroy();
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### RelayPool
|
|
552
|
+
|
|
553
|
+
Manages pool lifecycle, subscriptions, publishing, and NIP-65 relay list fetching.
|
|
554
|
+
|
|
555
|
+
```javascript
|
|
556
|
+
import { RelayPool } from 'nostr-wot-sdk/relay';
|
|
557
|
+
import { SimplePool } from 'nostr-tools';
|
|
558
|
+
|
|
559
|
+
const pool = new RelayPool({
|
|
560
|
+
urls: ['wss://relay.damus.io', 'wss://nos.lol'],
|
|
561
|
+
authorChunkSize: 150, // chunk large author lists
|
|
562
|
+
onRelaysChanged: (urls) => saveToSettings(urls),
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
pool.ensurePool(() => new SimplePool());
|
|
566
|
+
|
|
567
|
+
// Subscribe to notes from followed authors (auto-chunks large lists)
|
|
568
|
+
const sub = pool.subscribeAuthors(followedPubkeys, { kinds: [1], since, limit: 200 },
|
|
569
|
+
(event) => addToFeed(event),
|
|
570
|
+
() => console.log('Caught up')
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
// Publish events
|
|
574
|
+
await pool.publish(signedEvent);
|
|
575
|
+
|
|
576
|
+
// Fetch a user's NIP-65 relay list
|
|
577
|
+
const userRelays = await pool.fetchUserRelays(pubkey);
|
|
578
|
+
|
|
579
|
+
// Manage relays
|
|
580
|
+
pool.addRelay('wss://new.relay.com');
|
|
581
|
+
pool.removeRelay('wss://old.relay.com');
|
|
582
|
+
|
|
583
|
+
pool.destroy();
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### React Integration
|
|
587
|
+
|
|
588
|
+
```javascript
|
|
589
|
+
import { RelayProvider, useRelayPool, useRelayStatuses } from 'nostr-wot-sdk/relay/react';
|
|
590
|
+
import { SimplePool } from 'nostr-tools';
|
|
591
|
+
|
|
592
|
+
function App() {
|
|
593
|
+
return (
|
|
594
|
+
<RelayProvider
|
|
595
|
+
urls={['wss://relay.damus.io', 'wss://nos.lol']}
|
|
596
|
+
createPool={() => new SimplePool()}
|
|
597
|
+
enableStats={true}
|
|
598
|
+
>
|
|
599
|
+
<Feed />
|
|
600
|
+
</RelayProvider>
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function Feed() {
|
|
605
|
+
const pool = useRelayPool();
|
|
606
|
+
const { statuses, connectedCount } = useRelayStatuses();
|
|
607
|
+
|
|
608
|
+
useEffect(() => {
|
|
609
|
+
pool.query({ kinds: [1], limit: 30 }).then(setNotes);
|
|
610
|
+
}, [pool]);
|
|
611
|
+
|
|
612
|
+
return (
|
|
613
|
+
<div>
|
|
614
|
+
<span>{connectedCount} relays connected</span>
|
|
615
|
+
{/* render notes */}
|
|
616
|
+
</div>
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
#### Provider Props
|
|
622
|
+
|
|
623
|
+
| Prop | Type | Default | Description |
|
|
624
|
+
|------|------|---------|-------------|
|
|
625
|
+
| `urls` | `string[]` | required | Relay WebSocket URLs |
|
|
626
|
+
| `createPool` | `() => PoolLike` | required | Factory to create pool instance |
|
|
627
|
+
| `poolOptions` | `Partial<RelayPoolOptions>` | — | Options for RelayPool |
|
|
628
|
+
| `batcherOptions` | `QueryBatcherOptions` | — | Options for QueryBatcher |
|
|
629
|
+
| `statsPersistence` | `RelayStatsPersistence` | — | Persistence adapter for stats |
|
|
630
|
+
| `statsOptions` | `RelayStatsOptions` | — | Options for RelayStats |
|
|
631
|
+
| `enableStats` | `boolean` | `true` | Enable relay performance tracking |
|
|
632
|
+
|
|
633
|
+
#### Available Hooks
|
|
634
|
+
|
|
635
|
+
| Hook | Returns | Description |
|
|
636
|
+
|------|---------|-------------|
|
|
637
|
+
| `useRelayPool()` | `RelayPool` | Access the RelayPool instance |
|
|
638
|
+
| `useQueryBatcher()` | `QueryBatcher` | Access the QueryBatcher instance |
|
|
639
|
+
| `useRelayStats()` | `RelayStats \| null` | Access RelayStats (null if disabled) |
|
|
640
|
+
| `useRelayStatuses()` | `{ statuses, connectedCount }` | Reactive connection statuses |
|
|
641
|
+
| `useRelayContext()` | `RelayContextValue` | Full context with all instances |
|
|
642
|
+
|
|
446
643
|
## TypeScript
|
|
447
644
|
|
|
448
645
|
Full type definitions included:
|