nostr-wot-sdk 0.5.0 → 0.5.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 CHANGED
@@ -5,6 +5,32 @@ 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.5.2] - 2025-02-05
9
+
10
+ ### Added
11
+
12
+ - **`getExtensionStatus()` method** - Detailed extension connection status
13
+ - `'connected'` - Extension enabled and working on this domain
14
+ - `'not-enabled'` - Extension installed but not enabled for this domain
15
+ - `'unavailable'` - Not installed (or local dev build)
16
+ - `'not-browser'` - SSR/Node.js environment
17
+
18
+ - **`extensionId` option** - Chrome Web Store extension ID for install detection
19
+ - Enables detecting "installed but not enabled" state via `chrome-extension://` URL check
20
+ - Pass your extension's ID: `new WoT({ extensionId: 'abcdef...' })`
21
+
22
+ - New `ExtensionConnectionStatus` type export
23
+
24
+ ## [0.5.1] - 2025-02-05
25
+
26
+ ### Removed
27
+
28
+ - Removed deprecated extension connection utilities entirely:
29
+ - `checkExtension`, `connectExtension`, `checkAndConnect`
30
+ - `ExtensionConnector`, `getDefaultConnector`, `resetDefaultConnector`
31
+ - `ExtensionConnectionState`, `ExtensionConnectionResult`, `ExtensionConnectionOptions` types
32
+ - Removed `src/extension.ts` file
33
+
8
34
  ## [0.5.0] - 2025-02-05
9
35
 
10
36
  ### Changed
@@ -24,7 +50,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
24
50
 
25
51
  - Removed event-based connection (`nostr-wot-check`, `nostr-wot-connect` events)
26
52
  - Removed `ExtensionConnectionOptions` from React exports
27
- - Extension connection utilities (`checkExtension`, `connectExtension`, `checkAndConnect`, `ExtensionConnector`) are still exported but deprecated
28
53
 
29
54
  ## [0.4.2] - 2025-02-05
30
55
 
@@ -201,6 +226,8 @@ function Profile({ pubkey }) {
201
226
  - TypeScript support with full type definitions
202
227
  - Error classes: `WoTError`, `NetworkError`, `NotFoundError`, `TimeoutError`, `ValidationError`
203
228
 
229
+ [0.5.2]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.5.1...v0.5.2
230
+ [0.5.1]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.5.0...v0.5.1
204
231
  [0.5.0]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.4.2...v0.5.0
205
232
  [0.4.2]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.4.1...v0.4.2
206
233
  [0.4.1]: https://github.com/nostr-wot/nostr-wot-sdk/compare/v0.4.0...v0.4.1
package/README.md CHANGED
@@ -75,6 +75,7 @@ const wot = new WoT(options);
75
75
  | `maxHops` | number | `3` | Default max search depth |
76
76
  | `timeout` | number | `5000` | Request timeout (ms) |
77
77
  | `fallback` | object | — | Fallback config when extension unavailable |
78
+ | `extensionId` | string | — | Chrome Web Store extension ID (for detecting "installed but not enabled" state) |
78
79
 
79
80
  Trust scores are calculated by the extension and not configurable via the SDK.
80
81
 
@@ -147,6 +148,23 @@ const usingExt = await wot.isUsingExtension();
147
148
  // Returns: boolean
148
149
  ```
149
150
 
151
+ #### `getExtensionStatus()`
152
+
153
+ Get detailed extension connection status. Useful for showing appropriate UI based on why the extension isn't working.
154
+ ```javascript
155
+ const status = await wot.getExtensionStatus();
156
+ // Returns: 'connected' | 'not-enabled' | 'unavailable' | 'not-browser'
157
+ ```
158
+
159
+ | Status | Description |
160
+ |--------|-------------|
161
+ | `'connected'` | Extension is enabled and working on this domain |
162
+ | `'not-enabled'` | Extension is installed but not enabled for this domain |
163
+ | `'unavailable'` | Extension is not installed (or using local dev build) |
164
+ | `'not-browser'` | Running in SSR/Node.js environment |
165
+
166
+ **Note:** Detecting `'not-enabled'` requires providing the `extensionId` option.
167
+
150
168
  #### `getExtensionConfig()`
151
169
 
152
170
  Get extension's configuration.
package/dist/index.cjs CHANGED
@@ -86,26 +86,28 @@ function chunk(array, size) {
86
86
  }
87
87
 
88
88
  // src/wot.ts
89
+ var DEFAULT_EXTENSION_ID = "";
89
90
  var WoT = class {
90
91
  constructor(options = {}) {
91
92
  this.extension = null;
92
93
  this.extensionPubkey = null;
93
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
94
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
94
95
  this.fallbackOptions = (_a = options.fallback) != null ? _a : null;
96
+ this.extensionId = (_b = options.extensionId) != null ? _b : DEFAULT_EXTENSION_ID;
95
97
  if (options.myPubkey && isValidPubkey(options.myPubkey)) {
96
98
  this.fallbackPubkey = normalizePubkey(options.myPubkey);
97
- } else if ((_b = this.fallbackOptions) == null ? void 0 : _b.myPubkey) {
99
+ } else if ((_c = this.fallbackOptions) == null ? void 0 : _c.myPubkey) {
98
100
  this.fallbackPubkey = normalizePubkey(this.fallbackOptions.myPubkey);
99
101
  } else {
100
102
  this.fallbackPubkey = null;
101
103
  }
102
- const oracleUrl = (_e = (_d = options.oracle) != null ? _d : (_c = this.fallbackOptions) == null ? void 0 : _c.oracle) != null ? _e : DEFAULT_ORACLE;
104
+ const oracleUrl = (_f = (_e = options.oracle) != null ? _e : (_d = this.fallbackOptions) == null ? void 0 : _d.oracle) != null ? _f : DEFAULT_ORACLE;
103
105
  if (!isValidOracleUrl(oracleUrl)) {
104
106
  throw new ValidationError("oracle must be a valid HTTPS URL", "oracle");
105
107
  }
106
108
  this.oracle = oracleUrl;
107
- this.maxHops = (_h = (_g = options.maxHops) != null ? _g : (_f = this.fallbackOptions) == null ? void 0 : _f.maxHops) != null ? _h : DEFAULT_MAX_HOPS;
108
- this.timeout = (_k = (_j = options.timeout) != null ? _j : (_i = this.fallbackOptions) == null ? void 0 : _i.timeout) != null ? _k : DEFAULT_TIMEOUT;
109
+ this.maxHops = (_i = (_h = options.maxHops) != null ? _h : (_g = this.fallbackOptions) == null ? void 0 : _g.maxHops) != null ? _i : DEFAULT_MAX_HOPS;
110
+ this.timeout = (_l = (_k = options.timeout) != null ? _k : (_j = this.fallbackOptions) == null ? void 0 : _j.timeout) != null ? _l : DEFAULT_TIMEOUT;
109
111
  }
110
112
  /**
111
113
  * Checks if browser extension is available and returns it
@@ -419,6 +421,33 @@ var WoT = class {
419
421
  const ext = await this.getExtension();
420
422
  return ext !== null;
421
423
  }
424
+ /**
425
+ * Get detailed extension connection status
426
+ * Can detect if extension is installed but not enabled for this domain
427
+ * @returns Connection status: 'connected' | 'not-enabled' | 'unavailable' | 'not-browser'
428
+ */
429
+ async getExtensionStatus() {
430
+ var _a;
431
+ if (typeof window === "undefined") {
432
+ return "not-browser";
433
+ }
434
+ if ((_a = window.nostr) == null ? void 0 : _a.wot) {
435
+ return "connected";
436
+ }
437
+ if (this.extensionId) {
438
+ try {
439
+ const response = await fetch(
440
+ `chrome-extension://${this.extensionId}/detect.json`,
441
+ { method: "HEAD" }
442
+ );
443
+ if (response.ok) {
444
+ return "not-enabled";
445
+ }
446
+ } catch (e) {
447
+ }
448
+ }
449
+ return "unavailable";
450
+ }
422
451
  /**
423
452
  * Get extension configuration (only available when using extension)
424
453
  * @returns Extension config or null if not using extension
@@ -577,235 +606,9 @@ var WoT = class {
577
606
  }
578
607
  };
579
608
 
580
- // src/extension.ts
581
- function checkExtension(timeout = 100) {
582
- return new Promise((resolve) => {
583
- var _a;
584
- if (typeof window === "undefined") {
585
- resolve(false);
586
- return;
587
- }
588
- const win = window;
589
- if ((_a = win.nostr) == null ? void 0 : _a.wot) {
590
- resolve(true);
591
- return;
592
- }
593
- const handler = () => {
594
- window.removeEventListener("nostr-wot-present", handler);
595
- clearTimeout(timer);
596
- resolve(true);
597
- };
598
- window.addEventListener("nostr-wot-present", handler);
599
- const timer = setTimeout(() => {
600
- window.removeEventListener("nostr-wot-present", handler);
601
- resolve(false);
602
- }, timeout);
603
- window.dispatchEvent(new CustomEvent("nostr-wot-check"));
604
- });
605
- }
606
- function connectExtension(timeout = 5e3) {
607
- return new Promise((resolve, reject) => {
608
- var _a;
609
- if (typeof window === "undefined") {
610
- reject(new Error("Not in browser environment"));
611
- return;
612
- }
613
- const win = window;
614
- if ((_a = win.nostr) == null ? void 0 : _a.wot) {
615
- resolve(win.nostr.wot);
616
- return;
617
- }
618
- let timer;
619
- const onReady = () => {
620
- var _a2;
621
- cleanup();
622
- const ext = (_a2 = window.nostr) == null ? void 0 : _a2.wot;
623
- if (ext) {
624
- resolve(ext);
625
- } else {
626
- reject(new Error("Extension ready event fired but API not found"));
627
- }
628
- };
629
- const onError = (e) => {
630
- cleanup();
631
- const detail = e.detail;
632
- reject(new Error((detail == null ? void 0 : detail.error) || "Connection failed"));
633
- };
634
- const cleanup = () => {
635
- clearTimeout(timer);
636
- window.removeEventListener("nostr-wot-ready", onReady);
637
- window.removeEventListener("nostr-wot-error", onError);
638
- };
639
- window.addEventListener("nostr-wot-ready", onReady);
640
- window.addEventListener("nostr-wot-error", onError);
641
- window.dispatchEvent(new CustomEvent("nostr-wot-connect"));
642
- timer = setTimeout(() => {
643
- cleanup();
644
- reject(new Error("Connection timeout"));
645
- }, timeout);
646
- });
647
- }
648
- async function checkAndConnect(options = {}) {
649
- var _a;
650
- const { checkTimeout = 100, connectTimeout = 5e3, autoConnect = true } = options;
651
- if (typeof window === "undefined") {
652
- return { state: "not-installed", extension: null };
653
- }
654
- const win = window;
655
- if ((_a = win.nostr) == null ? void 0 : _a.wot) {
656
- return { state: "connected", extension: win.nostr.wot };
657
- }
658
- const isInstalled = await checkExtension(checkTimeout);
659
- if (!isInstalled) {
660
- return { state: "not-installed", extension: null };
661
- }
662
- if (!autoConnect) {
663
- return { state: "idle", extension: null };
664
- }
665
- try {
666
- const extension = await connectExtension(connectTimeout);
667
- return { state: "connected", extension };
668
- } catch (error) {
669
- return {
670
- state: "error",
671
- extension: null,
672
- error: error instanceof Error ? error.message : "Unknown error"
673
- };
674
- }
675
- }
676
- var ExtensionConnector = class {
677
- constructor(options = {}) {
678
- this.options = options;
679
- this.state = "idle";
680
- this.extension = null;
681
- this.connectionPromise = null;
682
- this.listeners = /* @__PURE__ */ new Set();
683
- }
684
- /**
685
- * Get current state
686
- */
687
- getState() {
688
- return this.state;
689
- }
690
- /**
691
- * Get current extension instance
692
- */
693
- getExtension() {
694
- return this.extension;
695
- }
696
- /**
697
- * Get current error
698
- */
699
- getError() {
700
- return this.error;
701
- }
702
- /**
703
- * Get current result
704
- */
705
- getResult() {
706
- return {
707
- state: this.state,
708
- extension: this.extension,
709
- error: this.error
710
- };
711
- }
712
- /**
713
- * Subscribe to state changes
714
- */
715
- subscribe(listener) {
716
- this.listeners.add(listener);
717
- return () => {
718
- this.listeners.delete(listener);
719
- };
720
- }
721
- notify() {
722
- const result = this.getResult();
723
- for (const listener of this.listeners) {
724
- listener(result);
725
- }
726
- }
727
- setState(state, extension = null, error) {
728
- this.state = state;
729
- this.extension = extension;
730
- this.error = error;
731
- this.notify();
732
- }
733
- /**
734
- * Connect to the extension
735
- * Returns existing promise if already connecting
736
- */
737
- async connect() {
738
- if (this.state === "connected" && this.extension) {
739
- return this.getResult();
740
- }
741
- if (this.connectionPromise) {
742
- return this.connectionPromise;
743
- }
744
- this.connectionPromise = this.doConnect();
745
- const result = await this.connectionPromise;
746
- this.connectionPromise = null;
747
- return result;
748
- }
749
- async doConnect() {
750
- var _a;
751
- const { checkTimeout = 100, connectTimeout = 5e3 } = this.options;
752
- if (typeof window === "undefined") {
753
- this.setState("not-installed");
754
- return this.getResult();
755
- }
756
- const win = window;
757
- if ((_a = win.nostr) == null ? void 0 : _a.wot) {
758
- this.setState("connected", win.nostr.wot);
759
- return this.getResult();
760
- }
761
- this.setState("checking");
762
- const isInstalled = await checkExtension(checkTimeout);
763
- if (!isInstalled) {
764
- this.setState("not-installed");
765
- return this.getResult();
766
- }
767
- this.setState("connecting");
768
- try {
769
- const extension = await connectExtension(connectTimeout);
770
- this.setState("connected", extension);
771
- } catch (error) {
772
- this.setState(
773
- "error",
774
- null,
775
- error instanceof Error ? error.message : "Unknown error"
776
- );
777
- }
778
- return this.getResult();
779
- }
780
- /**
781
- * Reset state and disconnect
782
- */
783
- reset() {
784
- this.state = "idle";
785
- this.extension = null;
786
- this.error = void 0;
787
- this.connectionPromise = null;
788
- this.notify();
789
- }
790
- };
791
- var defaultConnector = null;
792
- function getDefaultConnector(options) {
793
- if (!defaultConnector) {
794
- defaultConnector = new ExtensionConnector(options);
795
- }
796
- return defaultConnector;
797
- }
798
- function resetDefaultConnector() {
799
- if (defaultConnector) {
800
- defaultConnector.reset();
801
- defaultConnector = null;
802
- }
803
- }
804
-
805
609
  exports.DEFAULT_MAX_HOPS = DEFAULT_MAX_HOPS;
806
610
  exports.DEFAULT_ORACLE = DEFAULT_ORACLE;
807
611
  exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
808
- exports.ExtensionConnector = ExtensionConnector;
809
612
  exports.MAX_BATCH_SIZE = MAX_BATCH_SIZE;
810
613
  exports.NetworkError = NetworkError;
811
614
  exports.NotFoundError = NotFoundError;
@@ -813,11 +616,6 @@ exports.TimeoutError = TimeoutError;
813
616
  exports.ValidationError = ValidationError;
814
617
  exports.WoT = WoT;
815
618
  exports.WoTError = WoTError;
816
- exports.checkAndConnect = checkAndConnect;
817
- exports.checkExtension = checkExtension;
818
- exports.connectExtension = connectExtension;
819
- exports.getDefaultConnector = getDefaultConnector;
820
619
  exports.isValidOracleUrl = isValidOracleUrl;
821
620
  exports.isValidPubkey = isValidPubkey;
822
621
  exports.normalizePubkey = normalizePubkey;
823
- exports.resetDefaultConnector = resetDefaultConnector;
package/dist/index.d.cts CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Extension connection status
3
+ */
4
+ type ExtensionConnectionStatus = 'connected' | 'not-enabled' | 'unavailable' | 'not-browser';
1
5
  /**
2
6
  * Configuration for trust score calculation (used by extension)
3
7
  */
@@ -73,6 +77,11 @@ interface WoTOptions {
73
77
  * Recommended to provide myPubkey here for oracle fallback.
74
78
  */
75
79
  fallback?: WoTFallbackOptions;
80
+ /**
81
+ * Chrome Web Store extension ID (for install detection)
82
+ * Only needed if you want to detect "installed but not enabled" state
83
+ */
84
+ extensionId?: string;
76
85
  }
77
86
  /**
78
87
  * Options for query methods
@@ -364,6 +373,7 @@ declare class WoT {
364
373
  private readonly maxHops;
365
374
  private readonly timeout;
366
375
  private readonly fallbackOptions;
376
+ private readonly extensionId;
367
377
  private extension;
368
378
  private extensionPubkey;
369
379
  constructor(options?: WoTOptions);
@@ -443,6 +453,12 @@ declare class WoT {
443
453
  * Check if extension is available and being used
444
454
  */
445
455
  isUsingExtension(): Promise<boolean>;
456
+ /**
457
+ * Get detailed extension connection status
458
+ * Can detect if extension is installed but not enabled for this domain
459
+ * @returns Connection status: 'connected' | 'not-enabled' | 'unavailable' | 'not-browser'
460
+ */
461
+ getExtensionStatus(): Promise<ExtensionConnectionStatus>;
446
462
  /**
447
463
  * Get extension configuration (only available when using extension)
448
464
  * @returns Extension config or null if not using extension
@@ -539,110 +555,6 @@ declare class WoT {
539
555
  getTrustScoreBatch(targets: string[]): Promise<Record<string, number | null>>;
540
556
  }
541
557
 
542
- /**
543
- * Extension connection module
544
- * Handles the event-based communication flow with the nostr-wot browser extension
545
- */
546
-
547
- /**
548
- * Extension connection state
549
- */
550
- type ExtensionConnectionState = 'idle' | 'checking' | 'connecting' | 'connected' | 'not-installed' | 'error';
551
- /**
552
- * Extension connection result
553
- */
554
- interface ExtensionConnectionResult {
555
- state: ExtensionConnectionState;
556
- extension: NostrWoTExtension | null;
557
- error?: string;
558
- }
559
- /**
560
- * Extension connection options
561
- */
562
- interface ExtensionConnectionOptions {
563
- /**
564
- * Timeout for extension check (ms)
565
- * @default 100
566
- */
567
- checkTimeout?: number;
568
- /**
569
- * Timeout for connection (ms)
570
- * @default 5000
571
- */
572
- connectTimeout?: number;
573
- /**
574
- * Auto-connect after checking
575
- * @default true
576
- */
577
- autoConnect?: boolean;
578
- }
579
- /**
580
- * Check if the nostr-wot extension is installed
581
- * Uses the nostr-wot-check / nostr-wot-present event handshake
582
- */
583
- declare function checkExtension(timeout?: number): Promise<boolean>;
584
- /**
585
- * Connect to the nostr-wot extension
586
- * Uses the nostr-wot-connect / nostr-wot-ready event handshake
587
- */
588
- declare function connectExtension(timeout?: number): Promise<NostrWoTExtension>;
589
- /**
590
- * Check and connect to the extension in one call
591
- */
592
- declare function checkAndConnect(options?: ExtensionConnectionOptions): Promise<ExtensionConnectionResult>;
593
- /**
594
- * Extension connector class for managing connection state
595
- */
596
- declare class ExtensionConnector {
597
- private options;
598
- private state;
599
- private extension;
600
- private error;
601
- private connectionPromise;
602
- private listeners;
603
- constructor(options?: ExtensionConnectionOptions);
604
- /**
605
- * Get current state
606
- */
607
- getState(): ExtensionConnectionState;
608
- /**
609
- * Get current extension instance
610
- */
611
- getExtension(): NostrWoTExtension | null;
612
- /**
613
- * Get current error
614
- */
615
- getError(): string | undefined;
616
- /**
617
- * Get current result
618
- */
619
- getResult(): ExtensionConnectionResult;
620
- /**
621
- * Subscribe to state changes
622
- */
623
- subscribe(listener: (result: ExtensionConnectionResult) => void): () => void;
624
- private notify;
625
- private setState;
626
- /**
627
- * Connect to the extension
628
- * Returns existing promise if already connecting
629
- */
630
- connect(): Promise<ExtensionConnectionResult>;
631
- private doConnect;
632
- /**
633
- * Reset state and disconnect
634
- */
635
- reset(): void;
636
- }
637
- /**
638
- * Get the default extension connector
639
- */
640
- declare function getDefaultConnector(options?: ExtensionConnectionOptions): ExtensionConnector;
641
- /**
642
- * Reset the default connector (useful for testing)
643
- */
644
- declare function resetDefaultConnector(): void;
645
-
646
558
  /**
647
559
  * Base error class for WoT SDK errors
648
560
  */
@@ -708,4 +620,4 @@ declare const MAX_BATCH_SIZE = 10000;
708
620
  */
709
621
  declare function normalizePubkey(pubkey: string): string;
710
622
 
711
- export { type BatchResult, DEFAULT_MAX_HOPS, DEFAULT_ORACLE, DEFAULT_TIMEOUT, type DistanceBatchOptions, type DistanceResult, type ExtensionConfig, type ExtensionConnectionOptions, type ExtensionConnectionResult, type ExtensionConnectionState, ExtensionConnector, type ExtensionDistanceResult, type ExtensionStatus, type GraphStats, MAX_BATCH_SIZE, NetworkError, type NostrContactEvent, type NostrWindow, type NostrWoTExtension, NotFoundError, type QueryOptions, type ScoringConfig, TimeoutError, ValidationError, WoT, WoTError, type WoTFallbackOptions, type WoTOptions, checkAndConnect, checkExtension, connectExtension, getDefaultConnector, isValidOracleUrl, isValidPubkey, normalizePubkey, resetDefaultConnector };
623
+ export { type BatchResult, DEFAULT_MAX_HOPS, DEFAULT_ORACLE, DEFAULT_TIMEOUT, type DistanceBatchOptions, type DistanceResult, type ExtensionConfig, type ExtensionConnectionStatus, type ExtensionDistanceResult, type ExtensionStatus, type GraphStats, MAX_BATCH_SIZE, NetworkError, type NostrContactEvent, type NostrWindow, type NostrWoTExtension, NotFoundError, type QueryOptions, type ScoringConfig, TimeoutError, ValidationError, WoT, WoTError, type WoTFallbackOptions, type WoTOptions, isValidOracleUrl, isValidPubkey, normalizePubkey };