@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konemono/nostr-login",
3
- "version": "1.11.21",
3
+ "version": "1.11.23",
4
4
  "description": "",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",
@@ -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
- if (!this.readyCallback) await this.startAuth();
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
- private releaseSigner() {
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
- this.signerPromise = new Promise<void>(async (ok, err) => {
606
- this.signerErrCallback = err;
607
- try {
608
- // タイムアウトとキャンセルの両方に対応
609
- await Promise.race([this.initSignerInternal(info, listen, connect, eventToAddAccount, ok), abortPromise]);
610
- } catch (e) {
611
- console.log('initSigner failure', e);
612
- // make sure signer isn't set
613
- this.signer = null;
614
- this.signerAbortController = undefined;
615
- err(e);
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
- // ★ 修正: connectionLost イベントハンドリングを統一
641
- this.signer.removeAllListeners?.('connectionLost');
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
- private async handleReconnection(info: Info): Promise<void> {
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 ping確認はensureSignerで別途行う
744
- // await this.signer.reconnect(info);
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.log('NDK relays disconnected, attempting reconnection...');
791
- await this.handleReconnection(this.currentInfo);
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
- // Signer接続確認(失敗したら再接続を同期的に実行)
796
- try {
797
- // キャッシュ期間内ならスキップ
798
- const now = Date.now();
799
- if (this.signer['lastPingTime'] &&
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
- // ping確認
805
- if (this.signer.remotePubkey) {
806
- await this.signer['_rpc'].pingWithTimeout(this.signer.remotePubkey, 2000);
807
- this.signer['lastPingTime'] = now;
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
- private async codec_call(method: string, pubkey: string, param: string) {
836
- return new Promise<string>((resolve, reject) => {
837
- this.signer!.rpc.sendRequest(this.signer!.remotePubkey!, method, [pubkey, param], 24133, (response: NDKRpcResponse) => {
838
- if (!response.error) {
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);
@@ -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; // 30秒
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
- this.emit('connectionLost');
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
- await this._rpc.pingWithTimeout(this.remotePubkey, 2000);
451
- this.lastPingTime = Date.now();
452
- console.log('Reconnection successful');
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
- public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
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
- const r = await new Promise<NDKRpcResponse>(ok => {
553
- this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, ok);
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