nostr-wot-sdk 0.3.0 → 0.3.2

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 ADDED
@@ -0,0 +1,135 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.3.2] - 2025-02-05
9
+
10
+ ### Changed
11
+
12
+ - **Scoring formula changed from multiplicative to additive** to match extension
13
+ - Old: `score = baseScore × distanceWeight × (1 + bonuses)`
14
+ - New: `score = (baseScore × distanceWeight) + bonuses`
15
+ - Example: 2 hops + 30% path bonus = 0.5 + 0.3 = 0.80 (was 0.65)
16
+ - This produces higher scores for pubkeys with multiple paths
17
+
18
+ ## [0.3.1] - 2025-02-05
19
+
20
+ ### Added
21
+
22
+ - `getDistanceBatch(targets, includePaths?)` now accepts an optional `includePaths` parameter
23
+ - When `false` (default): returns `{ pubkey: hops }` - backwards compatible
24
+ - When `true`: returns `{ pubkey: { hops, paths } }` - includes path count for scoring
25
+
26
+ ### Changed
27
+
28
+ - `getTrustScoreBatch` now uses path counts internally for accurate trust score calculation with path bonuses
29
+
30
+ ## [0.3.0] - 2025-02-05
31
+
32
+ ### Added
33
+
34
+ - **Automatic Extension Connection for React** - Zero-config React integration
35
+ - `WoTProvider` now auto-connects to the extension by default (`useExtension: true`)
36
+ - New `useExtension()` hook for accessing extension connection state
37
+ - Extension state includes: `isConnected`, `isConnecting`, `isInstalled`, `isChecked`, `error`, `connect()`
38
+
39
+ - **Event-based Extension Connection Flow**
40
+ - New `src/extension.ts` module with reliable extension detection
41
+ - `checkExtension(timeout?)` - Check if extension is installed using `nostr-wot-check` → `nostr-wot-present` handshake
42
+ - `connectExtension(timeout?)` - Connect to extension using `nostr-wot-connect` → `nostr-wot-ready` handshake
43
+ - `checkAndConnect(options?)` - Combined check and connect in one call
44
+ - `ExtensionConnector` class - Stateful connector with subscription support for state changes
45
+ - `getDefaultConnector()` / `resetDefaultConnector()` - Singleton pattern for shared connector
46
+
47
+ - **New Extension Events Protocol**
48
+ | Event | Direction | Purpose |
49
+ |-------|-----------|---------|
50
+ | `nostr-wot-check` | Page → Extension | Check if extension installed |
51
+ | `nostr-wot-present` | Extension → Page | Response confirming presence |
52
+ | `nostr-wot-connect` | Page → Extension | Request API injection |
53
+ | `nostr-wot-ready` | Extension → Page | API is ready at `window.nostr.wot` |
54
+ | `nostr-wot-error` | Extension → Page | Injection failed with error |
55
+
56
+ ### Changed
57
+
58
+ - `WoTProvider` props are now optional - just `<WoTProvider>` works out of the box
59
+ - `WoT` class `getExtension()` now uses event-based connection flow for reliable detection
60
+
61
+ ### Usage
62
+
63
+ ```tsx
64
+ import { WoTProvider, useWoT, useExtension } from 'nostr-wot-sdk/react';
65
+
66
+ // Just wrap your app - no config needed
67
+ function App() {
68
+ return (
69
+ <WoTProvider>
70
+ <YourApp />
71
+ </WoTProvider>
72
+ );
73
+ }
74
+
75
+ // Check extension status
76
+ function Status() {
77
+ const { isConnected, isConnecting } = useExtension();
78
+ if (isConnecting) return <span>Connecting...</span>;
79
+ return <span>{isConnected ? 'Connected' : 'Not connected'}</span>;
80
+ }
81
+
82
+ // Use WoT data
83
+ function Profile({ pubkey }) {
84
+ const { distance, score, loading } = useWoT(pubkey);
85
+ // ...
86
+ }
87
+ ```
88
+
89
+ ## [0.2.0] - 2025-02-04
90
+
91
+ ### Added
92
+
93
+ - Full extension API support matching [nostr-wot-extension](https://github.com/nostr-wot/nostr-wot-extension)
94
+ - Extension-first architecture: extension always takes priority when available
95
+ - New extension-only methods:
96
+ - `getFollows(pubkey?)` - Get follow list
97
+ - `getCommonFollows(pubkey)` - Get mutual follows
98
+ - `getPath(target)` - Get actual path to target
99
+ - `getStats()` - Get graph statistics
100
+ - `isConfigured()` - Check extension configuration status
101
+ - `getExtensionConfig()` - Get extension configuration
102
+ - Batch operations:
103
+ - `getDistanceBatch(targets)` - Batch distance queries
104
+ - `getTrustScoreBatch(targets)` - Batch trust score queries
105
+ - `filterByWoT(pubkeys, options?)` - Filter pubkeys by WoT membership
106
+ - React integration (`nostr-wot-sdk/react`):
107
+ - `WoTProvider` - Context provider
108
+ - `useWoT(pubkey)` - Full WoT data hook
109
+ - `useIsInWoT(pubkey)` - Boolean WoT check
110
+ - `useTrustScore(pubkey)` - Trust score hook
111
+ - `useBatchWoT(pubkeys)` - Batch queries hook
112
+ - Local mode (`nostr-wot-sdk/local`) for server-side usage
113
+ - GitHub Actions workflows for CI and npm publishing
114
+
115
+ ### Changed
116
+
117
+ - `WoTOptions.useExtension` controls extension usage (default: `false`)
118
+ - Fallback configuration via `WoTOptions.fallback` for oracle mode when extension unavailable
119
+
120
+ ## [0.1.0] - 2025-02-03
121
+
122
+ ### Added
123
+
124
+ - Initial release
125
+ - Core `WoT` class with oracle-based queries
126
+ - Methods: `getDistance`, `isInMyWoT`, `getTrustScore`, `getDistanceBetween`, `batchCheck`, `getDetails`
127
+ - Custom scoring configuration
128
+ - TypeScript support with full type definitions
129
+ - Error classes: `WoTError`, `NetworkError`, `NotFoundError`, `TimeoutError`, `ValidationError`
130
+
131
+ [0.3.2]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.3.1...v0.3.2
132
+ [0.3.1]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.3.0...v0.3.1
133
+ [0.3.0]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.2.0...v0.3.0
134
+ [0.2.0]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.1.0...v0.2.0
135
+ [0.1.0]: https://github.com/nostr-wot/nostr-wot-sdk/releases/tag/v0.1.0
package/README.md CHANGED
@@ -161,17 +161,22 @@ const config = await wot.getExtensionConfig();
161
161
 
162
162
  ### Batch Operations
163
163
 
164
- #### `getDistanceBatch(targets)`
164
+ #### `getDistanceBatch(targets, includePaths?)`
165
165
 
166
166
  Get distances for multiple pubkeys in a single call.
167
167
  ```javascript
168
+ // Without paths (faster, default)
168
169
  const distances = await wot.getDistanceBatch(['pk1...', 'pk2...']);
169
170
  // Returns: { 'pk1...': 2, 'pk2...': null }
171
+
172
+ // With paths (includes path count for scoring)
173
+ const details = await wot.getDistanceBatch(['pk1...', 'pk2...'], true);
174
+ // Returns: { 'pk1...': { hops: 2, paths: 5 }, 'pk2...': null }
170
175
  ```
171
176
 
172
177
  #### `getTrustScoreBatch(targets)`
173
178
 
174
- Get trust scores for multiple pubkeys in a single call.
179
+ Get trust scores for multiple pubkeys in a single call. Uses path counts internally for accurate scoring.
175
180
  ```javascript
176
181
  const scores = await wot.getTrustScoreBatch(['pk1...', 'pk2...']);
177
182
  // Returns: { 'pk1...': 0.72, 'pk2...': null }
@@ -316,21 +321,23 @@ const wot = new WoT({
316
321
  3: 0.25, // 3 hops
317
322
  4: 0.1, // 4+ hops
318
323
  },
319
- // Bonus multipliers
320
- mutualBonus: 0.5, // +50% for mutual follows
321
- pathBonus: 0.1, // +10% per additional path
322
- maxPathBonus: 0.5, // Cap path bonus at +50%
324
+ // Bonus values (additive)
325
+ mutualBonus: 0.5, // +0.5 for mutual follows
326
+ pathBonus: 0.1, // +0.1 per additional path
327
+ maxPathBonus: 0.5, // Cap path bonus at +0.5
323
328
  }
324
329
  });
325
330
  ```
326
331
 
327
332
  ### Scoring Formula
328
333
  ```
329
- score = baseScore × distanceWeight × (1 + bonuses)
334
+ score = (baseScore × distanceWeight) + bonuses
330
335
 
331
336
  where:
332
337
  baseScore = 1 / (hops + 1)
333
338
  bonuses = mutualBonus (if mutual) + min(pathBonus × (paths - 1), maxPathBonus)
339
+
340
+ Example: 2 hops + 30% path bonus = 0.5 + 0.3 = 0.80
334
341
  ```
335
342
 
336
343
  ## Server-Side Local Mode
package/dist/index.cjs CHANGED
@@ -127,7 +127,7 @@ function calculateTrustScore(result, scoring) {
127
127
  const pathCountBonus = Math.min(pathBonus * (paths - 1), maxPathBonus);
128
128
  bonuses += pathCountBonus;
129
129
  }
130
- const score = baseScore * distanceWeight * (1 + bonuses);
130
+ const score = baseScore * distanceWeight + bonuses;
131
131
  return Math.min(1, Math.max(0, score));
132
132
  }
133
133
  async function fetchWithTimeout(url, options = {}) {
@@ -844,12 +844,7 @@ var WoT = class {
844
844
  const normalizedTarget = this.validatePubkey(target, "target");
845
845
  return ext.getPath(normalizedTarget);
846
846
  }
847
- /**
848
- * Get distances for multiple pubkeys in a single call
849
- * @param targets - Array of target pubkeys
850
- * @returns Record of pubkey to hop count (null if not connected)
851
- */
852
- async getDistanceBatch(targets) {
847
+ async getDistanceBatch(targets, includePaths = false) {
853
848
  if (!Array.isArray(targets) || targets.length === 0) {
854
849
  return {};
855
850
  }
@@ -858,7 +853,20 @@ var WoT = class {
858
853
  );
859
854
  const ext = await this.getExtension();
860
855
  if (ext) {
861
- return ext.getDistanceBatch(normalizedTargets);
856
+ if (includePaths) {
857
+ return ext.getDistanceBatch(normalizedTargets, true);
858
+ }
859
+ return ext.getDistanceBatch(normalizedTargets, false);
860
+ }
861
+ if (includePaths) {
862
+ const results2 = {};
863
+ await Promise.all(
864
+ normalizedTargets.map(async (pubkey) => {
865
+ const details = await this.getDetails(pubkey);
866
+ results2[pubkey] = details ? { hops: details.hops, paths: details.paths } : null;
867
+ })
868
+ );
869
+ return results2;
862
870
  }
863
871
  const results = {};
864
872
  await Promise.all(
package/dist/index.d.cts CHANGED
@@ -328,9 +328,19 @@ interface NostrWoTExtension {
328
328
  getConfig(): Promise<ExtensionConfig>;
329
329
  /**
330
330
  * Get distances for multiple pubkeys in a single call
331
- * @returns Map of pubkey to hop count (null if not connected)
332
- */
333
- getDistanceBatch(targets: string[]): Promise<Record<string, number | null>>;
331
+ * @param targets - Array of target pubkeys
332
+ * @param includePaths - When true, includes path count for each target
333
+ * @returns Map of pubkey to hop count (or { hops, paths } if includePaths is true)
334
+ */
335
+ getDistanceBatch(targets: string[], includePaths?: false): Promise<Record<string, number | null>>;
336
+ getDistanceBatch(targets: string[], includePaths: true): Promise<Record<string, {
337
+ hops: number;
338
+ paths: number;
339
+ } | null>>;
340
+ getDistanceBatch(targets: string[], includePaths?: boolean): Promise<Record<string, number | {
341
+ hops: number;
342
+ paths: number;
343
+ } | null>>;
334
344
  /**
335
345
  * Get trust scores for multiple pubkeys in a single call
336
346
  * @returns Map of pubkey to trust score (null if not connected)
@@ -537,9 +547,18 @@ declare class WoT {
537
547
  /**
538
548
  * Get distances for multiple pubkeys in a single call
539
549
  * @param targets - Array of target pubkeys
540
- * @returns Record of pubkey to hop count (null if not connected)
541
- */
542
- getDistanceBatch(targets: string[]): Promise<Record<string, number | null>>;
550
+ * @param includePaths - When true, includes path count for each target
551
+ * @returns Record of pubkey to hop count (or { hops, paths } if includePaths is true)
552
+ */
553
+ getDistanceBatch(targets: string[], includePaths?: false): Promise<Record<string, number | null>>;
554
+ getDistanceBatch(targets: string[], includePaths: true): Promise<Record<string, {
555
+ hops: number;
556
+ paths: number;
557
+ } | null>>;
558
+ getDistanceBatch(targets: string[], includePaths?: boolean): Promise<Record<string, number | {
559
+ hops: number;
560
+ paths: number;
561
+ } | null>>;
543
562
  /**
544
563
  * Get trust scores for multiple pubkeys in a single call
545
564
  * @param targets - Array of target pubkeys
@@ -742,12 +761,14 @@ declare function normalizePubkey(pubkey: string): string;
742
761
  * Calculates trust score based on distance result and scoring config
743
762
  *
744
763
  * Formula:
745
- * score = baseScore × distanceWeight × (1 + bonuses)
764
+ * score = (baseScore × distanceWeight) + bonuses
746
765
  *
747
766
  * where:
748
767
  * baseScore = 1 / (hops + 1)
749
768
  * bonuses = mutualBonus (if mutual) + min(pathBonus × (paths - 1), maxPathBonus)
750
769
  *
770
+ * Example: 2 hops + 30% path bonus = 0.5 + 0.3 = 0.80
771
+ *
751
772
  * Note: `mutual` is optional (only available from oracle, not extension)
752
773
  */
753
774
  declare function calculateTrustScore(result: DistanceResult, scoring: ScoringConfig): number;
package/dist/index.d.ts CHANGED
@@ -328,9 +328,19 @@ interface NostrWoTExtension {
328
328
  getConfig(): Promise<ExtensionConfig>;
329
329
  /**
330
330
  * Get distances for multiple pubkeys in a single call
331
- * @returns Map of pubkey to hop count (null if not connected)
332
- */
333
- getDistanceBatch(targets: string[]): Promise<Record<string, number | null>>;
331
+ * @param targets - Array of target pubkeys
332
+ * @param includePaths - When true, includes path count for each target
333
+ * @returns Map of pubkey to hop count (or { hops, paths } if includePaths is true)
334
+ */
335
+ getDistanceBatch(targets: string[], includePaths?: false): Promise<Record<string, number | null>>;
336
+ getDistanceBatch(targets: string[], includePaths: true): Promise<Record<string, {
337
+ hops: number;
338
+ paths: number;
339
+ } | null>>;
340
+ getDistanceBatch(targets: string[], includePaths?: boolean): Promise<Record<string, number | {
341
+ hops: number;
342
+ paths: number;
343
+ } | null>>;
334
344
  /**
335
345
  * Get trust scores for multiple pubkeys in a single call
336
346
  * @returns Map of pubkey to trust score (null if not connected)
@@ -537,9 +547,18 @@ declare class WoT {
537
547
  /**
538
548
  * Get distances for multiple pubkeys in a single call
539
549
  * @param targets - Array of target pubkeys
540
- * @returns Record of pubkey to hop count (null if not connected)
541
- */
542
- getDistanceBatch(targets: string[]): Promise<Record<string, number | null>>;
550
+ * @param includePaths - When true, includes path count for each target
551
+ * @returns Record of pubkey to hop count (or { hops, paths } if includePaths is true)
552
+ */
553
+ getDistanceBatch(targets: string[], includePaths?: false): Promise<Record<string, number | null>>;
554
+ getDistanceBatch(targets: string[], includePaths: true): Promise<Record<string, {
555
+ hops: number;
556
+ paths: number;
557
+ } | null>>;
558
+ getDistanceBatch(targets: string[], includePaths?: boolean): Promise<Record<string, number | {
559
+ hops: number;
560
+ paths: number;
561
+ } | null>>;
543
562
  /**
544
563
  * Get trust scores for multiple pubkeys in a single call
545
564
  * @param targets - Array of target pubkeys
@@ -742,12 +761,14 @@ declare function normalizePubkey(pubkey: string): string;
742
761
  * Calculates trust score based on distance result and scoring config
743
762
  *
744
763
  * Formula:
745
- * score = baseScore × distanceWeight × (1 + bonuses)
764
+ * score = (baseScore × distanceWeight) + bonuses
746
765
  *
747
766
  * where:
748
767
  * baseScore = 1 / (hops + 1)
749
768
  * bonuses = mutualBonus (if mutual) + min(pathBonus × (paths - 1), maxPathBonus)
750
769
  *
770
+ * Example: 2 hops + 30% path bonus = 0.5 + 0.3 = 0.80
771
+ *
751
772
  * Note: `mutual` is optional (only available from oracle, not extension)
752
773
  */
753
774
  declare function calculateTrustScore(result: DistanceResult, scoring: ScoringConfig): number;
package/dist/index.js CHANGED
@@ -125,7 +125,7 @@ function calculateTrustScore(result, scoring) {
125
125
  const pathCountBonus = Math.min(pathBonus * (paths - 1), maxPathBonus);
126
126
  bonuses += pathCountBonus;
127
127
  }
128
- const score = baseScore * distanceWeight * (1 + bonuses);
128
+ const score = baseScore * distanceWeight + bonuses;
129
129
  return Math.min(1, Math.max(0, score));
130
130
  }
131
131
  async function fetchWithTimeout(url, options = {}) {
@@ -842,12 +842,7 @@ var WoT = class {
842
842
  const normalizedTarget = this.validatePubkey(target, "target");
843
843
  return ext.getPath(normalizedTarget);
844
844
  }
845
- /**
846
- * Get distances for multiple pubkeys in a single call
847
- * @param targets - Array of target pubkeys
848
- * @returns Record of pubkey to hop count (null if not connected)
849
- */
850
- async getDistanceBatch(targets) {
845
+ async getDistanceBatch(targets, includePaths = false) {
851
846
  if (!Array.isArray(targets) || targets.length === 0) {
852
847
  return {};
853
848
  }
@@ -856,7 +851,20 @@ var WoT = class {
856
851
  );
857
852
  const ext = await this.getExtension();
858
853
  if (ext) {
859
- return ext.getDistanceBatch(normalizedTargets);
854
+ if (includePaths) {
855
+ return ext.getDistanceBatch(normalizedTargets, true);
856
+ }
857
+ return ext.getDistanceBatch(normalizedTargets, false);
858
+ }
859
+ if (includePaths) {
860
+ const results2 = {};
861
+ await Promise.all(
862
+ normalizedTargets.map(async (pubkey) => {
863
+ const details = await this.getDetails(pubkey);
864
+ results2[pubkey] = details ? { hops: details.hops, paths: details.paths } : null;
865
+ })
866
+ );
867
+ return results2;
860
868
  }
861
869
  const results = {};
862
870
  await Promise.all(
@@ -92,7 +92,7 @@ function calculateTrustScore(result, scoring) {
92
92
  const pathCountBonus = Math.min(pathBonus * (paths - 1), maxPathBonus);
93
93
  bonuses += pathCountBonus;
94
94
  }
95
- const score = baseScore * distanceWeight * (1 + bonuses);
95
+ const score = baseScore * distanceWeight + bonuses;
96
96
  return Math.min(1, Math.max(0, score));
97
97
  }
98
98
  function chunk(array, size) {