@konemono/nostr-login 1.11.21 → 1.11.23
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/dist/index.esm.js +7 -7
- package/dist/index.esm.js.map +1 -1
- package/dist/modules/AuthNostrService.d.ts +6 -9
- package/dist/modules/Nip46.d.ts +2 -1
- package/dist/unpkg.js +2 -2
- package/package.json +1 -1
- package/src/modules/AuthNostrService.ts +173 -143
- package/src/modules/Nip46.ts +44 -44
package/package.json
CHANGED
|
@@ -54,7 +54,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
54
54
|
private readonly MAX_RECONNECT_ATTEMPTS = 3;
|
|
55
55
|
private currentInfo?: Info;
|
|
56
56
|
private isReconnecting: boolean = false;
|
|
57
|
-
|
|
57
|
+
private reconnectTimer?: NodeJS.Timeout; // ★ 追加
|
|
58
58
|
|
|
59
59
|
nip04: {
|
|
60
60
|
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
@@ -302,24 +302,12 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
302
302
|
|
|
303
303
|
public async setConnect(info: Info) {
|
|
304
304
|
this.releaseSigner();
|
|
305
|
-
|
|
305
|
+
await this.startAuth();
|
|
306
306
|
await this.initSigner(info);
|
|
307
307
|
this.onAuth('login', info);
|
|
308
308
|
await this.endAuth();
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
public async authNip46(type: 'login' | 'signup', options: { name?: string; bunkerUrl: string; domain?: string; customRelays?: string[]; sk?: string }) {
|
|
312
|
-
const { bunkerUrl, sk, domain } = options;
|
|
313
|
-
const relay = options.customRelays?.[0] || bunkerUrlToInfo(bunkerUrl).relays?.[0];
|
|
314
|
-
if (!relay) throw new Error('No relay found in bunker URL');
|
|
315
|
-
|
|
316
|
-
await this.nostrConnect(relay, {
|
|
317
|
-
domain,
|
|
318
|
-
importConnect: true,
|
|
319
|
-
customRelays: options.customRelays,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
|
|
323
311
|
public async createAccount(nip05: string) {
|
|
324
312
|
const [name, domain] = nip05.split('@');
|
|
325
313
|
|
|
@@ -344,29 +332,7 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
344
332
|
};
|
|
345
333
|
}
|
|
346
334
|
|
|
347
|
-
|
|
348
|
-
this.signer = null;
|
|
349
|
-
this.signerErrCallback?.('cancelled');
|
|
350
|
-
this.localSigner = null;
|
|
351
|
-
|
|
352
|
-
// disconnect from signer relays
|
|
353
|
-
for (const r of this.ndk.pool.relays.keys()) {
|
|
354
|
-
this.ndk.pool.removeRelay(r);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
public async logout(keepSigner = false) {
|
|
359
|
-
if (!keepSigner) this.releaseSigner();
|
|
360
|
-
|
|
361
|
-
// move current to recent
|
|
362
|
-
localStorageRemoveCurrentAccount();
|
|
363
|
-
|
|
364
|
-
// notify everyone
|
|
365
|
-
this.onAuth('logout');
|
|
366
|
-
|
|
367
|
-
this.emit('updateAccounts');
|
|
368
|
-
}
|
|
369
|
-
|
|
335
|
+
|
|
370
336
|
private setUserInfo(userInfo: Info | null) {
|
|
371
337
|
this.params.userInfo = userInfo;
|
|
372
338
|
this.emit('onUserInfo', userInfo);
|
|
@@ -454,13 +420,6 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
454
420
|
} catch (e) {
|
|
455
421
|
console.log('onAuth error', e);
|
|
456
422
|
}
|
|
457
|
-
|
|
458
|
-
// ログイン完了後に接続確認を実行
|
|
459
|
-
if (type === 'login') {
|
|
460
|
-
setTimeout(() => {
|
|
461
|
-
this.ensureSigner().catch(e => console.log('Post-login connection check failed', e));
|
|
462
|
-
}, 1000);
|
|
463
|
-
}
|
|
464
423
|
}
|
|
465
424
|
|
|
466
425
|
private async createIframe(iframeUrl?: string) {
|
|
@@ -602,23 +561,24 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
602
561
|
});
|
|
603
562
|
});
|
|
604
563
|
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
564
|
+
this.signerPromise = new Promise<void>(async (ok, err) => {
|
|
565
|
+
this.signerErrCallback = err;
|
|
566
|
+
try {
|
|
567
|
+
await Promise.race([this.initSignerInternal(info, listen, connect, eventToAddAccount, ok), abortPromise]);
|
|
568
|
+
} catch (e) {
|
|
569
|
+
console.log('initSigner failure', e);
|
|
570
|
+
// ★ 追加: 失敗時のクリーンアップ
|
|
571
|
+
this.cleanup();
|
|
572
|
+
this.signer = null;
|
|
573
|
+
this.signerAbortController = undefined;
|
|
574
|
+
err(e);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
618
577
|
|
|
619
578
|
return this.signerPromise;
|
|
620
579
|
}
|
|
621
580
|
|
|
581
|
+
// ★ 修正: initSignerInternal
|
|
622
582
|
private async initSignerInternal(info: Info, listen: boolean, connect: boolean, eventToAddAccount: boolean, resolve: () => void) {
|
|
623
583
|
// リレー接続
|
|
624
584
|
if (info.relays && !info.iframeUrl) {
|
|
@@ -637,39 +597,8 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
637
597
|
info.iframeUrl ? new URL(info.iframeUrl!).origin : undefined
|
|
638
598
|
);
|
|
639
599
|
|
|
640
|
-
// ★
|
|
641
|
-
this.
|
|
642
|
-
this.signer.on('connectionLost', () => {
|
|
643
|
-
console.log('Connection lost detected');
|
|
644
|
-
|
|
645
|
-
// ログイン処理中は再接続をスキップ
|
|
646
|
-
if (this.isAuthing()) {
|
|
647
|
-
console.log('Skipping reconnection during authentication');
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// ★ 再接続処理を呼び出す(非同期で実行、エラーは握りつぶさない)
|
|
652
|
-
this.handleReconnection(info).catch(err => {
|
|
653
|
-
console.error('Reconnection handling failed:', err);
|
|
654
|
-
this.emit('reconnectFailed', err);
|
|
655
|
-
});
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
// iframe restart は既存のまま
|
|
659
|
-
this.signer.removeAllListeners?.('iframeRestart');
|
|
660
|
-
this.signer.on('iframeRestart', async () => {
|
|
661
|
-
const iframeUrl = info.iframeUrl +
|
|
662
|
-
(info.iframeUrl!.includes('?') ? '&' : '?') +
|
|
663
|
-
'pubkey=' + info.pubkey + '&rebind=' + localSigner.pubkey;
|
|
664
|
-
this.emit('iframeRestart', { pubkey: info.pubkey, iframeUrl });
|
|
665
|
-
});
|
|
666
|
-
|
|
667
|
-
// authUrl は既存のまま
|
|
668
|
-
this.signer.removeAllListeners?.('authUrl');
|
|
669
|
-
this.signer.on('authUrl', (url: string) => {
|
|
670
|
-
console.log('nostr login auth url', url);
|
|
671
|
-
this.emit('onAuthUrl', { url, iframeUrl: info.iframeUrl, eventToAddAccount });
|
|
672
|
-
});
|
|
600
|
+
// ★ イベントハンドラー登録
|
|
601
|
+
this.setupSignerEventHandlers(info);
|
|
673
602
|
|
|
674
603
|
// 認証フロー
|
|
675
604
|
if (listen) {
|
|
@@ -683,25 +612,66 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
683
612
|
info.pubkey = this.signer!.userPubkey;
|
|
684
613
|
info.signerPubkey = this.signer!.remotePubkey;
|
|
685
614
|
|
|
686
|
-
// ★
|
|
615
|
+
// ★ 接続情報を保持
|
|
687
616
|
this.currentInfo = info;
|
|
688
617
|
|
|
689
618
|
this.signerAbortController = undefined;
|
|
690
619
|
resolve();
|
|
691
620
|
}
|
|
692
621
|
|
|
693
|
-
|
|
694
|
-
|
|
622
|
+
|
|
623
|
+
// ★ 修正: イベントハンドラーを一度だけ登録
|
|
624
|
+
private setupSignerEventHandlers(info: Info) {
|
|
625
|
+
if (!this.signer) return;
|
|
626
|
+
|
|
627
|
+
// 既存のリスナーをすべて削除
|
|
628
|
+
this.signer.removeAllListeners('connectionLost');
|
|
629
|
+
this.signer.removeAllListeners('iframeRestart');
|
|
630
|
+
this.signer.removeAllListeners('authUrl');
|
|
631
|
+
|
|
632
|
+
// connectionLost: 一度だけ処理
|
|
633
|
+
this.signer.once('connectionLost', () => {
|
|
634
|
+
console.log('Connection lost detected');
|
|
635
|
+
this.scheduleReconnection(info);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
// iframeRestart
|
|
639
|
+
this.signer.on('iframeRestart', async () => {
|
|
640
|
+
const localSigner = this.signer?.['_rpc']?.['_signer'];
|
|
641
|
+
if (!localSigner) return;
|
|
642
|
+
|
|
643
|
+
const iframeUrl = info.iframeUrl +
|
|
644
|
+
(info.iframeUrl!.includes('?') ? '&' : '?') +
|
|
645
|
+
'pubkey=' + info.pubkey + '&rebind=' + localSigner.pubkey;
|
|
646
|
+
this.emit('iframeRestart', { pubkey: info.pubkey, iframeUrl });
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// authUrl
|
|
650
|
+
this.signer.on('authUrl', (url: string) => {
|
|
651
|
+
console.log('nostr login auth url', url);
|
|
652
|
+
this.emit('onAuthUrl', { url, iframeUrl: info.iframeUrl, eventToAddAccount: false });
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// ★ 新規追加: 再接続スケジューリング
|
|
657
|
+
private scheduleReconnection(info: Info) {
|
|
658
|
+
if (this.reconnectTimer) {
|
|
659
|
+
clearTimeout(this.reconnectTimer);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
// 認証中はスキップ
|
|
695
663
|
if (this.isAuthing()) {
|
|
696
664
|
console.log('Authentication in progress, skipping reconnection...');
|
|
697
665
|
return;
|
|
698
666
|
}
|
|
699
667
|
|
|
668
|
+
// 既に再接続中
|
|
700
669
|
if (this.isReconnecting) {
|
|
701
670
|
console.log('Already reconnecting, skipping...');
|
|
702
671
|
return;
|
|
703
672
|
}
|
|
704
673
|
|
|
674
|
+
// 最大試行回数超過
|
|
705
675
|
if (this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) {
|
|
706
676
|
console.error('Max reconnection attempts reached');
|
|
707
677
|
this.emit('reconnectFailed');
|
|
@@ -709,6 +679,24 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
709
679
|
return;
|
|
710
680
|
}
|
|
711
681
|
|
|
682
|
+
const delay = 2000 * (this.reconnectAttempts + 1); // 2秒, 4秒, 6秒
|
|
683
|
+
console.log(`Scheduling reconnection in ${delay}ms (attempt ${this.reconnectAttempts + 1}/${this.MAX_RECONNECT_ATTEMPTS})`);
|
|
684
|
+
|
|
685
|
+
this.reconnectTimer = setTimeout(() => {
|
|
686
|
+
this.handleReconnection(info).catch(err => {
|
|
687
|
+
console.error('Reconnection failed:', err);
|
|
688
|
+
// 失敗したら再スケジュール
|
|
689
|
+
this.scheduleReconnection(info);
|
|
690
|
+
});
|
|
691
|
+
}, delay);
|
|
692
|
+
}
|
|
693
|
+
// ★ 修正: 再接続処理の簡素化
|
|
694
|
+
private async handleReconnection(info: Info): Promise<void> {
|
|
695
|
+
if (this.isReconnecting) {
|
|
696
|
+
console.log('Already reconnecting, skipping...');
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
|
|
712
700
|
this.isReconnecting = true;
|
|
713
701
|
this.reconnectAttempts++;
|
|
714
702
|
|
|
@@ -736,12 +724,13 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
736
724
|
}
|
|
737
725
|
}
|
|
738
726
|
|
|
739
|
-
// 接続待機
|
|
740
727
|
await this.ndk.connect();
|
|
741
728
|
}
|
|
742
729
|
|
|
743
|
-
// 2. Signer
|
|
744
|
-
|
|
730
|
+
// 2. Signer再接続
|
|
731
|
+
if (this.signer) {
|
|
732
|
+
await this.signer.reconnect(info);
|
|
733
|
+
}
|
|
745
734
|
|
|
746
735
|
// 成功
|
|
747
736
|
this.reconnectAttempts = 0;
|
|
@@ -749,29 +738,18 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
749
738
|
console.log('Reconnection successful');
|
|
750
739
|
this.emit('reconnected');
|
|
751
740
|
|
|
741
|
+
// ★ イベントハンドラーを再登録
|
|
742
|
+
this.setupSignerEventHandlers(info);
|
|
743
|
+
|
|
752
744
|
} catch (error) {
|
|
753
745
|
console.error(`Reconnection attempt ${this.reconnectAttempts} failed:`, error);
|
|
754
746
|
this.isReconnecting = false;
|
|
755
|
-
|
|
756
|
-
// リトライ
|
|
757
|
-
if (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {
|
|
758
|
-
const delay = 2000 * this.reconnectAttempts; // 2秒, 4秒, 6秒
|
|
759
|
-
console.log(`Retrying in ${delay}ms...`);
|
|
760
|
-
|
|
761
|
-
setTimeout(() => {
|
|
762
|
-
this.handleReconnection(info).catch(err => {
|
|
763
|
-
console.error('Retry failed:', err);
|
|
764
|
-
});
|
|
765
|
-
}, delay);
|
|
766
|
-
} else {
|
|
767
|
-
this.emit('reconnectFailed');
|
|
768
|
-
this.reconnectAttempts = 0;
|
|
769
|
-
}
|
|
747
|
+
throw error; // ★ エラーを上位に伝播
|
|
770
748
|
}
|
|
771
749
|
}
|
|
772
750
|
|
|
773
751
|
|
|
774
|
-
// ★ 修正: ensureSigner -
|
|
752
|
+
// ★ 修正: ensureSigner - ping失敗時は警告のみ
|
|
775
753
|
private async ensureSigner() {
|
|
776
754
|
// signerがnullの場合のみ再初期化
|
|
777
755
|
if (!this.signer && this.currentInfo) {
|
|
@@ -784,37 +762,42 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
784
762
|
throw new Error('No signer available');
|
|
785
763
|
}
|
|
786
764
|
|
|
787
|
-
//
|
|
765
|
+
// リレー接続確認(切断時は警告のみ、署名処理は続行)
|
|
788
766
|
const stats = this.ndk.pool.stats();
|
|
789
767
|
if (stats.connected === 0 && this.currentInfo) {
|
|
790
|
-
console.
|
|
791
|
-
|
|
768
|
+
console.warn('NDK relays disconnected, attempting background reconnection');
|
|
769
|
+
this.scheduleReconnection(this.currentInfo);
|
|
770
|
+
// ★ エラーを投げずに続行(署名時にNIP-46が接続チェックする)
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// Signer接続確認(オプショナル)
|
|
774
|
+
const now = Date.now();
|
|
775
|
+
const signer = this.signer as any;
|
|
776
|
+
|
|
777
|
+
// キャッシュ期間内ならスキップ
|
|
778
|
+
if (signer.lastPingTime && now - signer.lastPingTime < signer.pingCacheDuration) {
|
|
792
779
|
return;
|
|
793
780
|
}
|
|
794
781
|
|
|
795
|
-
//
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
now - this.signer['lastPingTime'] < this.signer['pingCacheDuration']) {
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
782
|
+
// ログイン処理中はpingをスキップ
|
|
783
|
+
if (this.isAuthing()) {
|
|
784
|
+
console.log('Skipping ping during authentication');
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
803
787
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
this.signer
|
|
788
|
+
// ★ ping確認(失敗しても署名処理は続行)
|
|
789
|
+
if (this.signer.remotePubkey) {
|
|
790
|
+
try {
|
|
791
|
+
await signer._rpc.pingWithTimeout(this.signer.remotePubkey, 2000);
|
|
792
|
+
signer.lastPingTime = now;
|
|
793
|
+
} catch (error) {
|
|
794
|
+
console.warn('Ping failed in ensureSigner, but continuing with sign operation', error);
|
|
795
|
+
// ★ エラーを投げない:Nip46Signer.sign()内のensureConnection()で再チェックされる
|
|
808
796
|
}
|
|
809
|
-
} catch (error) {
|
|
810
|
-
// ping失敗 = 接続切断
|
|
811
|
-
console.log('Connection lost during ensureSigner, but ignoring during auth');
|
|
812
|
-
// ログイン処理中はエラーを無視
|
|
813
797
|
}
|
|
814
798
|
}
|
|
815
799
|
|
|
816
|
-
|
|
817
|
-
public async signEvent(event: any) {
|
|
800
|
+
public async signEvent(event: any) {
|
|
818
801
|
if (this.localSigner) {
|
|
819
802
|
event.pubkey = getPublicKey(this.localSigner.privateKey!);
|
|
820
803
|
event.id = getEventHash(event);
|
|
@@ -830,20 +813,67 @@ class AuthNostrService extends EventEmitter implements Signer {
|
|
|
830
813
|
return event;
|
|
831
814
|
}
|
|
832
815
|
|
|
816
|
+
// ★ 追加: クリーンアップ
|
|
817
|
+
public cleanup() {
|
|
818
|
+
if (this.reconnectTimer) {
|
|
819
|
+
clearTimeout(this.reconnectTimer);
|
|
820
|
+
this.reconnectTimer = undefined;
|
|
821
|
+
}
|
|
822
|
+
this.reconnectAttempts = 0;
|
|
823
|
+
this.isReconnecting = false;
|
|
824
|
+
|
|
825
|
+
// ★ 追加: signerのイベントリスナーもクリア
|
|
826
|
+
if (this.signer) {
|
|
827
|
+
this.signer.removeAllListeners('connectionLost');
|
|
828
|
+
this.signer.removeAllListeners('iframeRestart');
|
|
829
|
+
this.signer.removeAllListeners('authUrl');
|
|
830
|
+
}
|
|
831
|
+
}
|
|
833
832
|
|
|
833
|
+
private releaseSigner() {
|
|
834
|
+
this.cleanup(); // ★ 追加: クリーンアップ実行
|
|
835
|
+
|
|
836
|
+
this.signer = null;
|
|
837
|
+
this.signerErrCallback?.('cancelled');
|
|
838
|
+
this.localSigner = null;
|
|
834
839
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
this.
|
|
838
|
-
|
|
839
|
-
resolve(response.result);
|
|
840
|
-
} else {
|
|
841
|
-
reject(response.error);
|
|
842
|
-
}
|
|
843
|
-
});
|
|
844
|
-
});
|
|
840
|
+
// disconnect from signer relays
|
|
841
|
+
for (const r of this.ndk.pool.relays.keys()) {
|
|
842
|
+
this.ndk.pool.removeRelay(r);
|
|
843
|
+
}
|
|
845
844
|
}
|
|
846
845
|
|
|
846
|
+
public async logout(keepSigner = false) {
|
|
847
|
+
if (!keepSigner) this.releaseSigner();
|
|
848
|
+
else this.cleanup(); // ★ keepSigner時もクリーンアップ
|
|
849
|
+
|
|
850
|
+
localStorageRemoveCurrentAccount();
|
|
851
|
+
this.onAuth('logout');
|
|
852
|
+
this.emit('updateAccounts');
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
// ★ 修正: RPC呼び出し前に接続確認を追加
|
|
857
|
+
private async codec_call(method: string, pubkey: string, param: string) {
|
|
858
|
+
// Nip46Signerの接続確認を明示的に実行
|
|
859
|
+
if (this.signer) {
|
|
860
|
+
const signer = this.signer as any;
|
|
861
|
+
if (signer.ensureConnection) {
|
|
862
|
+
await signer.ensureConnection();
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return new Promise<string>((resolve, reject) => {
|
|
867
|
+
this.signer!.rpc.sendRequest(this.signer!.remotePubkey!, method, [pubkey, param], 24133, (response: NDKRpcResponse) => {
|
|
868
|
+
if (!response.error) {
|
|
869
|
+
resolve(response.result);
|
|
870
|
+
} else {
|
|
871
|
+
reject(response.error);
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
|
|
847
877
|
public async encrypt04(pubkey: string, plaintext: string) {
|
|
848
878
|
if (this.localSigner) {
|
|
849
879
|
return this.localSigner.encrypt(new NDKUser({ pubkey }), plaintext);
|
package/src/modules/Nip46.ts
CHANGED
|
@@ -380,19 +380,17 @@ export class ReadyListener {
|
|
|
380
380
|
return r;
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
|
-
|
|
384
383
|
export class Nip46Signer extends NDKNip46Signer {
|
|
385
384
|
private _userPubkey: string = '';
|
|
386
385
|
private _rpc: IframeNostrRpc;
|
|
387
386
|
private lastPingTime: number = 0;
|
|
388
|
-
private pingCacheDuration: number = 30000;
|
|
389
|
-
// ★ 追加: 再接続中フラグ
|
|
387
|
+
private pingCacheDuration: number = 30000;
|
|
390
388
|
private isReconnecting: boolean = false;
|
|
389
|
+
private connectionLostEmitted: boolean = false; // ★ 追加
|
|
391
390
|
|
|
392
391
|
constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
|
|
393
392
|
super(ndk, signerPubkey, localSigner);
|
|
394
393
|
|
|
395
|
-
// override with our own rpc implementation
|
|
396
394
|
this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
|
|
397
395
|
this._rpc.setUseNip44(true);
|
|
398
396
|
this._rpc.on('authUrl', (url: string) => {
|
|
@@ -406,10 +404,9 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
406
404
|
return this._userPubkey;
|
|
407
405
|
}
|
|
408
406
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
private async ensureConnection(): Promise<void> {
|
|
407
|
+
// ★ 修正: 接続確認のみ実施、再接続は行わない
|
|
408
|
+
// ★ public化して外部から呼び出し可能にする
|
|
409
|
+
public async ensureConnection(): Promise<void> {
|
|
413
410
|
if (!this.remotePubkey) return;
|
|
414
411
|
|
|
415
412
|
const now = Date.now();
|
|
@@ -422,17 +419,22 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
422
419
|
try {
|
|
423
420
|
await this._rpc.pingWithTimeout(this.remotePubkey, 2000);
|
|
424
421
|
this.lastPingTime = now;
|
|
422
|
+
this.connectionLostEmitted = false; // ★ リセット
|
|
425
423
|
console.log('Connection check OK');
|
|
426
424
|
} catch (error) {
|
|
427
425
|
console.error('Connection check failed', error);
|
|
428
|
-
|
|
429
|
-
|
|
426
|
+
|
|
427
|
+
// ★ 修正: 一度だけイベント発火
|
|
428
|
+
if (!this.connectionLostEmitted) {
|
|
429
|
+
this.connectionLostEmitted = true;
|
|
430
|
+
this.emit('connectionLost');
|
|
431
|
+
}
|
|
432
|
+
|
|
430
433
|
throw new Error('NIP-46 connection lost');
|
|
431
434
|
}
|
|
432
435
|
}
|
|
433
436
|
|
|
434
|
-
|
|
435
|
-
// ★ 新規追加: 再接続メソッド(外部から呼ばれる)
|
|
437
|
+
// ★ 修正: 再接続メソッド - イベント発火を抑制
|
|
436
438
|
public async reconnect(info: any): Promise<void> {
|
|
437
439
|
if (this.isReconnecting) {
|
|
438
440
|
console.log('Already reconnecting, skipping...');
|
|
@@ -444,12 +446,20 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
444
446
|
try {
|
|
445
447
|
console.log('Reconnecting signer...');
|
|
446
448
|
|
|
447
|
-
// リレー再接続は AuthNostrService 側で実施済みと仮定
|
|
448
|
-
// ここでは ping のみ実施
|
|
449
449
|
if (this.remotePubkey) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
450
|
+
// ★ 修正: 再接続中はイベント発火を抑制
|
|
451
|
+
const prevFlag = this.connectionLostEmitted;
|
|
452
|
+
this.connectionLostEmitted = true;
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
await this._rpc.pingWithTimeout(this.remotePubkey, 2000);
|
|
456
|
+
this.lastPingTime = Date.now();
|
|
457
|
+
this.connectionLostEmitted = false; // ★ 成功時のみリセット
|
|
458
|
+
console.log('Reconnection successful');
|
|
459
|
+
} catch (error) {
|
|
460
|
+
this.connectionLostEmitted = prevFlag; // ★ 失敗時は元に戻す
|
|
461
|
+
throw error;
|
|
462
|
+
}
|
|
453
463
|
}
|
|
454
464
|
|
|
455
465
|
this.isReconnecting = false;
|
|
@@ -462,15 +472,12 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
462
472
|
private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
|
|
463
473
|
console.log('setSignerPubkey', signerPubkey);
|
|
464
474
|
|
|
465
|
-
// ensure it's set
|
|
466
475
|
this.remotePubkey = signerPubkey;
|
|
467
476
|
|
|
468
|
-
// when we're sure it's known
|
|
469
477
|
this._rpc.on(`iframeRestart-${signerPubkey}`, () => {
|
|
470
478
|
this.emit('iframeRestart');
|
|
471
479
|
});
|
|
472
480
|
|
|
473
|
-
// now call getPublicKey and swap remotePubkey w/ that
|
|
474
481
|
await this.initUserPubkey(sameAsUser ? signerPubkey : '');
|
|
475
482
|
}
|
|
476
483
|
|
|
@@ -503,29 +510,26 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
503
510
|
public async listen(nostrConnectSecret: string) {
|
|
504
511
|
const signerPubkey = await (this.rpc as IframeNostrRpc).listen(nostrConnectSecret);
|
|
505
512
|
await this.setSignerPubkey(signerPubkey);
|
|
506
|
-
|
|
507
|
-
//
|
|
508
|
-
await this.ensureConnection();
|
|
513
|
+
// ★ 削除: ログイン直後の接続確認は不要
|
|
514
|
+
// await this.ensureConnection();
|
|
509
515
|
}
|
|
510
516
|
|
|
511
517
|
public async connect(token?: string, perms?: string) {
|
|
512
518
|
if (!this.remotePubkey) throw new Error('No signer pubkey');
|
|
513
519
|
await this._rpc.connectWithTimeout(this.remotePubkey, token, perms, NIP46_CONNECT_TIMEOUT);
|
|
514
520
|
await this.setSignerPubkey(this.remotePubkey);
|
|
515
|
-
|
|
516
|
-
//
|
|
517
|
-
await this.ensureConnection();
|
|
521
|
+
// ★ 削除: ログイン直後の接続確認は不要
|
|
522
|
+
// await this.ensureConnection();
|
|
518
523
|
}
|
|
519
524
|
|
|
520
525
|
public async setListenReply(reply: any, nostrConnectSecret: string) {
|
|
521
526
|
const signerPubkey = await this._rpc.parseNostrConnectReply(reply, nostrConnectSecret);
|
|
522
527
|
await this.setSignerPubkey(signerPubkey, true);
|
|
523
|
-
|
|
524
|
-
//
|
|
525
|
-
await this.ensureConnection();
|
|
528
|
+
// ★ 削除: ログイン直後の接続確認は不要
|
|
529
|
+
// await this.ensureConnection();
|
|
526
530
|
}
|
|
527
531
|
|
|
528
|
-
//
|
|
532
|
+
// ★ 修正: 署名前に接続確認
|
|
529
533
|
async sign(event: NostrEvent): Promise<string> {
|
|
530
534
|
await this.ensureConnection();
|
|
531
535
|
return super.sign(event);
|
|
@@ -541,17 +545,17 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
541
545
|
return super.decrypt(sender, value);
|
|
542
546
|
}
|
|
543
547
|
|
|
544
|
-
|
|
545
|
-
const params = [
|
|
546
|
-
name,
|
|
547
|
-
domain,
|
|
548
|
-
'', // email
|
|
549
|
-
perms,
|
|
550
|
-
];
|
|
548
|
+
public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
|
|
549
|
+
const params = [name, domain, '', perms];
|
|
551
550
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
551
|
+
// ★ 追加: 接続確認
|
|
552
|
+
if (this.remotePubkey === bunkerPubkey) {
|
|
553
|
+
await this.ensureConnection();
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const r = await new Promise<NDKRpcResponse>(ok => {
|
|
557
|
+
this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, ok);
|
|
558
|
+
});
|
|
555
559
|
|
|
556
560
|
console.log('create_account pubkey', r);
|
|
557
561
|
if (r.result === 'error') {
|
|
@@ -561,7 +565,6 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
561
565
|
return r.result;
|
|
562
566
|
}
|
|
563
567
|
|
|
564
|
-
// ★ 追加: removeAllListeners メソッド
|
|
565
568
|
public removeAllListeners = (event?: string | symbol): this => {
|
|
566
569
|
if (event) {
|
|
567
570
|
this._rpc.eventEmitter.removeAllListeners(event as string);
|
|
@@ -571,9 +574,6 @@ export class Nip46Signer extends NDKNip46Signer {
|
|
|
571
574
|
return this;
|
|
572
575
|
}
|
|
573
576
|
|
|
574
|
-
|
|
575
|
-
// EventEmitter互換メソッド
|
|
576
|
-
// ★ ここに once を追加 ★
|
|
577
577
|
public override on = <EventKey extends string | symbol = string>(
|
|
578
578
|
event: EventKey,
|
|
579
579
|
listener: (...args: any[]) => void
|