@konemono/nostr-login 1.10.15 → 1.11.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.
@@ -1,8 +1,14 @@
1
- import { localStorageAddAccount, bunkerUrlToInfo, isBunkerUrl, fetchProfile, getBunkerUrl, localStorageRemoveCurrentAccount, createProfile, getIcon } from '../utils';
1
+ import { localStorageAddAccount, bunkerUrlToInfo, isBunkerUrl, fetchProfile, getBunkerUrl, localStorageRemoveCurrentAccount, createProfile, getIcon, bytesToHex, hexToBytes } from '../utils';
2
2
  import { ConnectionString, Info } from 'nostr-login-components/dist/types/types';
3
- import { generatePrivateKey, getEventHash, getPublicKey, nip19 } from 'nostr-tools';
3
+ import { createRxNostr, RxNostr } from 'rx-nostr';
4
+ import { generateSecretKey, getEventHash, getPublicKey, nip19 } from 'nostr-tools';
4
5
  import { NostrLoginAuthOptions, Response } from '../types';
5
- import NDK, { NDKEvent, NDKNip46Signer, NDKRpcResponse, NDKUser, NostrEvent } from '@nostr-dev-kit/ndk';
6
+
7
+
8
+
9
+ // Placeholder types for SimplePool transition
10
+ // private pool: SimplePool;
11
+ // private outboxPool: SimplePool;
6
12
  import { NostrParams } from './';
7
13
  import { EventEmitter } from 'tseep';
8
14
  import { Signer } from './Nostr';
@@ -33,8 +39,7 @@ const NOSTRCONNECT_APPS: ConnectionString[] = [
33
39
  ];
34
40
 
35
41
  class AuthNostrService extends EventEmitter implements Signer {
36
- private ndk: NDK;
37
- private profileNdk: NDK;
42
+ private rxNostr: RxNostr;
38
43
  private signer: Nip46Signer | null = null;
39
44
  private localSigner: PrivateKeySigner | null = null;
40
45
  private params: NostrParams;
@@ -61,15 +66,11 @@ class AuthNostrService extends EventEmitter implements Signer {
61
66
  constructor(params: NostrParams) {
62
67
  super();
63
68
  this.params = params;
64
- this.ndk = new NDK({
65
- enableOutboxModel: false,
66
- });
67
69
 
68
- this.profileNdk = new NDK({
69
- enableOutboxModel: true,
70
- explicitRelayUrls: OUTBOX_RELAYS,
71
- });
72
- this.profileNdk.connect();
70
+ this.rxNostr = createRxNostr({} as any);
71
+ // Default relays for initial connection if needed, though they are usually set per-action
72
+ // @ts-ignore
73
+ this.rxNostr.switchRelays(DEFAULT_NIP46_RELAYS);
73
74
 
74
75
  this.nip04 = {
75
76
  encrypt: this.encrypt04.bind(this),
@@ -81,30 +82,30 @@ class AuthNostrService extends EventEmitter implements Signer {
81
82
  };
82
83
  }
83
84
 
84
- public isIframe() {
85
+ public isIframe(): boolean {
85
86
  return !!this.iframe;
86
87
  }
87
88
 
88
- public async waitReady() {
89
+ public async waitReady(): Promise<void> {
89
90
  if (this.signerPromise) {
90
91
  try {
91
92
  await this.signerPromise;
92
- } catch {}
93
+ } catch { }
93
94
  }
94
95
 
95
96
  if (this.readyPromise) {
96
97
  try {
97
98
  await this.readyPromise;
98
- } catch {}
99
+ } catch { }
99
100
  }
100
101
  }
101
102
 
102
- public cancelNostrConnect() {
103
+ public cancelNostrConnect(): void {
103
104
  this.releaseSigner();
104
105
  this.resetAuth();
105
106
  }
106
107
 
107
- public cancelSignerInit() {
108
+ public cancelSignerInit(): void {
108
109
  if (this.signerAbortController) {
109
110
  this.signerAbortController.abort();
110
111
  this.signerAbortController = undefined;
@@ -133,7 +134,7 @@ class AuthNostrService extends EventEmitter implements Signer {
133
134
  iframeUrl?: string;
134
135
  customRelays?: string[];
135
136
  } = {},
136
- ) {
137
+ ): Promise<Info> {
137
138
  // カスタムリレーが指定されていれば使用、そうでなければ単一リレーまたはデフォルト
138
139
  const relays = customRelays && customRelays.length > 0 ? customRelays : relay ? [relay] : DEFAULT_NIP46_RELAYS;
139
140
 
@@ -168,11 +169,13 @@ class AuthNostrService extends EventEmitter implements Signer {
168
169
  return info;
169
170
  }
170
171
 
171
- public async createNostrConnect() {
172
- this.nostrConnectKey = generatePrivateKey();
172
+ public async createNostrConnect(): Promise<string> {
173
+ const skBytes = generateSecretKey();
174
+ // @ts-ignore
175
+ this.nostrConnectKey = bytesToHex(skBytes);
173
176
  this.nostrConnectSecret = Math.random().toString(36).substring(7);
174
177
 
175
- const pubkey = getPublicKey(this.nostrConnectKey);
178
+ const pubkey = getPublicKey(skBytes);
176
179
  const meta = {
177
180
  name: encodeURIComponent(document.location.host),
178
181
  url: encodeURIComponent(document.location.origin),
@@ -229,10 +232,14 @@ class AuthNostrService extends EventEmitter implements Signer {
229
232
  return [nostrconnect, apps];
230
233
  }
231
234
 
232
- public async localSignup(name: string, sk?: string) {
235
+ public async localSignup(name: string, sk?: string): Promise<void> {
233
236
  const signup = !sk;
234
- sk = sk || generatePrivateKey();
235
- const pubkey = getPublicKey(sk);
237
+ if (!sk) {
238
+ const skBytes = generateSecretKey();
239
+ sk = bytesToHex(skBytes);
240
+ }
241
+ // @ts-ignore
242
+ const pubkey = getPublicKey(hexToBytes(sk!));
236
243
  const info: Info = {
237
244
  pubkey,
238
245
  sk,
@@ -243,25 +250,118 @@ class AuthNostrService extends EventEmitter implements Signer {
243
250
  await this.setLocal(info, signup);
244
251
  }
245
252
 
246
- public async setLocal(info: Info, signup?: boolean) {
253
+ public async setLocal(info: Info, signup?: boolean): Promise<void> {
247
254
  this.releaseSigner();
248
- this.localSigner = new PrivateKeySigner(info.sk!);
255
+ this.localSigner = new PrivateKeySigner(hexToBytes(info.sk!));
249
256
 
250
- if (signup) await createProfile(info, this.profileNdk, this.localSigner, this.params.optionsModal.signupRelays, this.params.optionsModal.outboxRelays);
257
+ // TODO: nostr-toolsを使用するようにcreateProfileをリファクタリングする
258
+ if (signup) {
259
+ await createProfile(info, this.rxNostr, this.localSigner, this.params.optionsModal.signupRelays, this.params.optionsModal.outboxRelays);
260
+ }
251
261
 
252
262
  this.onAuth(signup ? 'signup' : 'login', info);
253
263
  }
254
264
 
255
- public prepareImportUrl(url: string) {
265
+ public prepareImportUrl(url: string): string {
256
266
  // for OTP we choose interactive import
257
267
  if (this.params.userInfo?.authMethod === 'otp') return url + '&import=true';
258
268
 
259
269
  // for local we export our existing key
260
270
  if (!this.localSigner || this.params.userInfo?.authMethod !== 'local') throw new Error('Most be local keys');
261
- return url + '#import=' + nip19.nsecEncode(this.localSigner.privateKey!);
271
+ // Ensure privateKey is Uint8Array for nsecEncode if needed, but nip19.nsecEncode usually takes Uint8Array
272
+ const skBytes = this.localSigner.privateKey!;
273
+ return url + '#import=' + nip19.nsecEncode(skBytes);
274
+ }
275
+
276
+ // リレー管理メソッド
277
+ public getRelayStatus(): Record<string, string> {
278
+ const status = this.rxNostr.getAllRelayStatus();
279
+ const result: Record<string, string> = {};
280
+ for (const [url, state] of Object.entries(status)) {
281
+ result[url] = String(state);
282
+ }
283
+ return result;
284
+ }
285
+
286
+ public getRelayStats(): {
287
+ total: number;
288
+ connected: number;
289
+ connecting: number;
290
+ disconnected: number;
291
+ error: number;
292
+ } {
293
+ const status = this.rxNostr.getAllRelayStatus();
294
+ const stats = {
295
+ total: 0,
296
+ connected: 0,
297
+ connecting: 0,
298
+ disconnected: 0,
299
+ error: 0,
300
+ };
301
+
302
+ for (const state of Object.values(status)) {
303
+ stats.total++;
304
+ const stateStr = String(state).toLowerCase();
305
+ if (stateStr === 'connected') stats.connected++;
306
+ else if (stateStr === 'connecting') stats.connecting++;
307
+ else if (stateStr === 'error') stats.error++;
308
+ else stats.disconnected++;
309
+ }
310
+
311
+ return stats;
312
+ }
313
+
314
+ public async connectToRelay(relayUrl: string): Promise<void> {
315
+ try {
316
+ this.rxNostr.reconnect(relayUrl);
317
+ console.log(`Connecting to relay: ${relayUrl}`);
318
+ } catch (error) {
319
+ console.error(`Failed to connect to relay ${relayUrl}:`, error);
320
+ throw error;
321
+ }
322
+ }
323
+
324
+ public async disconnectFromRelay(relayUrl: string): Promise<void> {
325
+ try {
326
+ // rx-nostrにはdisconnect APIがないため、リレーリストから除外する方法を使用
327
+ const currentRelays = Object.keys(this.rxNostr.getAllRelayStatus());
328
+ const newRelays = currentRelays.filter(url => url !== relayUrl);
329
+ if (newRelays.length > 0) {
330
+ // @ts-ignore
331
+ this.rxNostr.switchRelays(newRelays);
332
+ }
333
+ console.log(`Disconnected from relay: ${relayUrl}`);
334
+ } catch (error) {
335
+ console.error(`Failed to disconnect from relay ${relayUrl}:`, error);
336
+ throw error;
337
+ }
338
+ }
339
+
340
+ public async reconnectAllRelays(): Promise<void> {
341
+ try {
342
+ const relays = Object.keys(this.rxNostr.getAllRelayStatus());
343
+ console.log(`Reconnecting to ${relays.length} relays...`);
344
+
345
+ for (const relay of relays) {
346
+ try {
347
+ this.rxNostr.reconnect(relay);
348
+ } catch (error) {
349
+ console.warn(`Failed to reconnect to ${relay}:`, error);
350
+ }
351
+ }
352
+ } catch (error) {
353
+ console.error('Failed to reconnect all relays:', error);
354
+ throw error;
355
+ }
262
356
  }
263
357
 
264
- public async importAndConnect(cs: ConnectionString) {
358
+ public async checkRelayHealth(): Promise<boolean> {
359
+ const stats = this.getRelayStats();
360
+ // 少なくとも1つのリレーが接続されていればOK
361
+ return stats.connected > 0;
362
+ }
363
+
364
+ public async importAndConnect(cs: ConnectionString): Promise<void> {
265
365
  const { relay, domain, link, iframeUrl } = cs;
266
366
  if (!domain) throw new Error('Domain required');
267
367
 
@@ -278,22 +378,22 @@ class AuthNostrService extends EventEmitter implements Signer {
278
378
  this.onAuth('login', info);
279
379
  }
280
380
 
281
- public setReadOnly(pubkey: string) {
381
+ public setReadOnly(pubkey: string): void {
282
382
  const info: Info = { pubkey, authMethod: 'readOnly' };
283
383
  this.onAuth('login', info);
284
384
  }
285
385
 
286
- public setExtension(pubkey: string) {
386
+ public setExtension(pubkey: string): void {
287
387
  const info: Info = { pubkey, authMethod: 'extension' };
288
388
  this.onAuth('login', info);
289
389
  }
290
390
 
291
- public setOTP(pubkey: string, data: string) {
391
+ public setOTP(pubkey: string, data: string): void {
292
392
  const info: Info = { pubkey, authMethod: 'otp', otpData: data };
293
393
  this.onAuth('login', info);
294
394
  }
295
395
 
296
- public async setConnect(info: Info) {
396
+ public async setConnect(info: Info): Promise<void> {
297
397
  this.releaseSigner();
298
398
  await this.startAuth();
299
399
  await this.initSigner(info);
@@ -301,7 +401,7 @@ class AuthNostrService extends EventEmitter implements Signer {
301
401
  await this.endAuth();
302
402
  }
303
403
 
304
- public async createAccount(nip05: string) {
404
+ public async createAccount(nip05: string): Promise<{ bunkerUrl: string; sk: string | undefined }> {
305
405
  const [name, domain] = nip05.split('@');
306
406
 
307
407
  // bunker's own url
@@ -325,18 +425,19 @@ class AuthNostrService extends EventEmitter implements Signer {
325
425
  };
326
426
  }
327
427
 
328
- private releaseSigner() {
428
+ private releaseSigner(): void {
329
429
  this.signer = null;
330
430
  this.signerErrCallback?.('cancelled');
331
431
  this.localSigner = null;
332
432
 
333
433
  // disconnect from signer relays
334
- for (const r of this.ndk.pool.relays.keys()) {
335
- this.ndk.pool.removeRelay(r);
336
- }
434
+ // rx-nostr manages relays, we can switch to empty or default if needed
435
+ // or just leave them be as they might be reused?
436
+ // For now, let's just clear the signer instance.
437
+ // this.rxNostr.switchRelays([]); // Should we disconnect? Maybe better to keep connection for next signer?
337
438
  }
338
439
 
339
- public async logout(keepSigner = false) {
440
+ public async logout(keepSigner = false): Promise<void> {
340
441
  if (!keepSigner) this.releaseSigner();
341
442
 
342
443
  // move current to recent
@@ -348,7 +449,7 @@ class AuthNostrService extends EventEmitter implements Signer {
348
449
  this.emit('updateAccounts');
349
450
  }
350
451
 
351
- private setUserInfo(userInfo: Info | null) {
452
+ private setUserInfo(userInfo: Info | null): void {
352
453
  this.params.userInfo = userInfo;
353
454
  this.emit('onUserInfo', userInfo);
354
455
 
@@ -358,13 +459,14 @@ class AuthNostrService extends EventEmitter implements Signer {
358
459
  }
359
460
  }
360
461
 
361
- public exportKeys() {
462
+ public exportKeys(): string {
362
463
  if (!this.params.userInfo) return '';
363
464
  if (this.params.userInfo.authMethod !== 'local') return '';
364
- return nip19.nsecEncode(this.params.userInfo.sk!);
465
+ const skBytes = hexToBytes(this.params.userInfo.sk!);
466
+ return nip19.nsecEncode(skBytes);
365
467
  }
366
468
 
367
- private onAuth(type: 'login' | 'signup' | 'logout', info: Info | null = null) {
469
+ private onAuth(type: 'login' | 'signup' | 'logout', info: Info | null = null): void {
368
470
  if (type !== 'logout' && !info) throw new Error('No user info in onAuth');
369
471
 
370
472
  // make sure we emulate logout first
@@ -378,7 +480,8 @@ class AuthNostrService extends EventEmitter implements Signer {
378
480
 
379
481
  if (info) {
380
482
  // async profile fetch
381
- fetchProfile(info, this.profileNdk).then(p => {
483
+ // TODO: nostr-toolsを使用するようにfetchProfileをリファクタリングする
484
+ fetchProfile(info, this.rxNostr as any).then(p => {
382
485
  if (this.params.userInfo !== info) return;
383
486
 
384
487
  const userInfo = {
@@ -411,7 +514,7 @@ class AuthNostrService extends EventEmitter implements Signer {
411
514
  options.name = info!.name;
412
515
 
413
516
  if (info!.sk) {
414
- options.localNsec = nip19.nsecEncode(info!.sk);
517
+ options.localNsec = nip19.nsecEncode(hexToBytes(info!.sk));
415
518
  }
416
519
 
417
520
  if (info!.relays) {
@@ -437,7 +540,7 @@ class AuthNostrService extends EventEmitter implements Signer {
437
540
  }
438
541
  }
439
542
 
440
- private async createIframe(iframeUrl?: string) {
543
+ private async createIframe(iframeUrl?: string): Promise<{ iframe: HTMLIFrameElement; port: MessagePort } | undefined> {
441
544
  if (!iframeUrl) return undefined;
442
545
 
443
546
  // ensure iframe
@@ -496,18 +599,18 @@ class AuthNostrService extends EventEmitter implements Signer {
496
599
  // }
497
600
  // }
498
601
 
499
- public async sendNeedAuth() {
602
+ public async sendNeedAuth(): Promise<void> {
500
603
  const [nostrconnect] = await this.getNostrConnectServices();
501
604
  const event = new CustomEvent('nlNeedAuth', { detail: { nostrconnect } });
502
605
  console.log('nostr-login need auth', nostrconnect);
503
606
  document.dispatchEvent(event);
504
607
  }
505
608
 
506
- public isAuthing() {
609
+ public isAuthing(): boolean {
507
610
  return !!this.readyCallback;
508
611
  }
509
612
 
510
- public async startAuth() {
613
+ public async startAuth(): Promise<void> {
511
614
  console.log('startAuth');
512
615
  if (this.readyCallback) throw new Error('Already started');
513
616
 
@@ -515,7 +618,7 @@ class AuthNostrService extends EventEmitter implements Signer {
515
618
  this.readyPromise = new Promise<void>(ok => (this.readyCallback = ok));
516
619
  }
517
620
 
518
- public async endAuth() {
621
+ public async endAuth(): Promise<void> {
519
622
  console.log('endAuth', this.params.userInfo);
520
623
  if (this.params.userInfo && this.params.userInfo.iframeUrl) {
521
624
  // create iframe
@@ -531,28 +634,28 @@ class AuthNostrService extends EventEmitter implements Signer {
531
634
  this.readyCallback = undefined;
532
635
  }
533
636
 
534
- public resetAuth() {
637
+ public resetAuth(): void {
535
638
  if (this.readyCallback) this.readyCallback();
536
639
  this.readyCallback = undefined;
537
640
  }
538
641
 
539
- private async listen(info: Info) {
642
+ private async listen(info: Info): Promise<void> {
540
643
  if (!info.iframeUrl) return this.signer!.listen(this.nostrConnectSecret);
541
644
  const r = await this.starterReady!.wait();
542
645
  if (r[0] === 'starterError') throw new Error(r[1]);
543
646
  return this.signer!.setListenReply(r[1], this.nostrConnectSecret);
544
647
  }
545
648
 
546
- public async connect(info: Info, perms?: string) {
649
+ public async connect(info: Info, perms?: string): Promise<void> {
547
650
  return this.signer!.connect(info.token, perms);
548
651
  }
549
652
 
550
- public async initSigner(info: Info, { listen = false, connect = false, eventToAddAccount = false } = {}) {
653
+ public async initSigner(info: Info, { listen = false, connect = false, eventToAddAccount = false } = {}): Promise<void> {
551
654
  // mutex
552
655
  if (this.signerPromise) {
553
656
  try {
554
657
  await this.signerPromise;
555
- } catch {}
658
+ } catch { }
556
659
  }
557
660
 
558
661
  // we remove support for iframe from nip05 and bunker-url methods,
@@ -593,22 +696,28 @@ class AuthNostrService extends EventEmitter implements Signer {
593
696
  return this.signerPromise;
594
697
  }
595
698
 
596
- private async initSignerInternal(info: Info, listen: boolean, connect: boolean, eventToAddAccount: boolean, resolve: () => void) {
699
+ private async initSignerInternal(info: Info, listen: boolean, connect: boolean, eventToAddAccount: boolean, resolve: () => void): Promise<void> {
597
700
  // pre-connect if we're creating the connection (listen|connect) or
598
701
  // not iframe mode
599
702
  if (info.relays && !info.iframeUrl) {
600
- for (const r of info.relays) {
601
- this.ndk.addExplicitRelay(r, undefined);
602
- }
703
+ // @ts-ignore
704
+ this.rxNostr.switchRelays(info.relays);
603
705
  }
604
706
 
605
- // wait until we connect, otherwise
606
- // signer won't start properly
607
- await this.ndk.connect();
707
+ // rx-nostr connects automatically
608
708
 
609
709
  // create and prepare the signer
610
- const localSigner = new PrivateKeySigner(info.sk!);
611
- this.signer = new Nip46Signer(this.ndk, localSigner, info.signerPubkey!, info.iframeUrl ? new URL(info.iframeUrl!).origin : undefined);
710
+ const localSigner = new PrivateKeySigner(hexToBytes(info.sk!));
711
+
712
+ // TODO: 元のNDKロジック:
713
+ // This initialized the Nip46Signer with NDK instance.
714
+ //
715
+ // New Implementation Plan:
716
+ // 1. Pass the SimplePool instance or relay list to Nip46Signer.
717
+ // 2. Nip46Signer should manage its own connection or use the shared pool to publish/subscribe.
718
+
719
+ // TODO: ndk引数を削除する
720
+ this.signer = new Nip46Signer(this.rxNostr, localSigner, info.signerPubkey!, info.iframeUrl ? new URL(info.iframeUrl!).origin : undefined);
612
721
 
613
722
  // we should notify the banner the same way as
614
723
  // the onAuthUrl does
@@ -660,7 +769,7 @@ class AuthNostrService extends EventEmitter implements Signer {
660
769
  iframeUrl = '',
661
770
  customRelays,
662
771
  }: { name: string; bunkerUrl: string; sk?: string; domain?: string; iframeUrl?: string; customRelays?: string[] },
663
- ) {
772
+ ): Promise<void> {
664
773
  try {
665
774
  const info = bunkerUrlToInfo(bunkerUrl, sk);
666
775
  if (isBunkerUrl(name)) info.bunkerUrl = name;
@@ -697,8 +806,9 @@ class AuthNostrService extends EventEmitter implements Signer {
697
806
  }
698
807
  }
699
808
 
700
- public async signEvent(event: any) {
809
+ public async signEvent(event: any): Promise<any> {
701
810
  if (this.localSigner) {
811
+ // this.localSigner.privateKey is Uint8Array
702
812
  event.pubkey = getPublicKey(this.localSigner.privateKey!);
703
813
  event.id = getEventHash(event);
704
814
  event.sig = await this.localSigner.sign(event);
@@ -713,11 +823,12 @@ class AuthNostrService extends EventEmitter implements Signer {
713
823
  return event;
714
824
  }
715
825
 
716
- private async ensureSigner() {
826
+ private async ensureSigner(): Promise<void> {
717
827
  // signerがキャンセル等で破棄されている場合は再初期化
718
828
  if (!this.signer && this.params.userInfo) {
719
829
  console.log('Signer was destroyed, reinitializing...');
720
830
  await this.initSigner(this.params.userInfo);
831
+ return; // initSignerで接続も行われるので終了
721
832
  }
722
833
 
723
834
  if (!this.signer) {
@@ -725,87 +836,76 @@ class AuthNostrService extends EventEmitter implements Signer {
725
836
  }
726
837
 
727
838
  // リレー接続を確認・再接続
728
- const stats = this.ndk.pool.stats();
729
- console.log('NDK pool stats:', stats);
730
-
731
- if (stats.connected === 0) {
732
- console.log('NDK relays disconnected, forcing reconnection...');
733
-
734
- // 既存のリレーを一度切断してから再接続
735
- for (const relay of this.ndk.pool.relays.values()) {
736
- try {
737
- relay.disconnect();
738
- } catch (e) {
739
- console.log('Error disconnecting relay:', e);
740
- }
741
- }
742
-
743
- // 少し待ってから再接続
744
- await new Promise(resolve => setTimeout(resolve, 100));
745
- await this.ndk.connect();
746
-
747
- // 接続を待つ
748
- await new Promise(resolve => setTimeout(resolve, 500));
749
-
750
- const newStats = this.ndk.pool.stats();
751
- console.log('NDK pool stats after reconnect:', newStats);
752
-
753
- if (newStats.connected === 0) {
754
- throw new Error('Failed to reconnect to relays');
839
+ // TODO: 元のNDKロジック:
840
+ // Checks ndk.pool.stats() for connectivity.
841
+ //
842
+ // New Implementation Plan:
843
+ // 1. Iterate through SimplePool.relays or listConnectionStatus().
844
+ // 2. If no relays are connected, modify code to reconnect.
845
+
846
+ // TODO: rx-nostrの統計を確認する
847
+ // const stats = this.pool.listConnectionStatus();
848
+ // const stats = this.pool.listConnectionStatus();
849
+ // const stats = this.ndk.pool.stats();
850
+ // console.log('NDK pool stats:', stats);
851
+ const relayStates = this.rxNostr.getAllRelayStatus();
852
+ const connectedCount = Object.values(relayStates).filter(s => String(s) === 'connected').length;
853
+
854
+ if (connectedCount === 0) {
855
+ console.log('RxNostr relays disconnected, reinitializing signer...');
856
+
857
+ // リレーが完全に切断されている場合、signerも再初期化する必要がある
858
+ // (RPCサブスクリプションも切断されているため)
859
+ if (this.params.userInfo) {
860
+ // 古いsignerを破棄
861
+ this.signer = null;
862
+
863
+ // 既存のリレーを一度切断 (rx-nostr reconnects automatically, but we might want to force it)
864
+ this.rxNostr.reconnect(Object.keys(relayStates)[0]);
865
+
866
+ // signerを再初期化(リレー接続も含む)
867
+ await this.initSigner(this.params.userInfo);
868
+ } else {
869
+ throw new Error('Cannot reconnect: no user info');
755
870
  }
756
871
  }
757
872
  }
758
873
 
759
- private async codec_call(method: string, pubkey: string, param: string) {
760
- return new Promise<string>((resolve, reject) => {
761
- this.signer!.rpc.sendRequest(this.signer!.remotePubkey!, method, [pubkey, param], 24133, (response: NDKRpcResponse) => {
762
- if (!response.error) {
763
- resolve(response.result);
764
- } else {
765
- reject(response.error);
766
- }
767
- });
768
- });
769
- }
770
874
 
771
- public async encrypt04(pubkey: string, plaintext: string) {
875
+
876
+ public async encrypt04(pubkey: string, plaintext: string): Promise<string> {
772
877
  if (this.localSigner) {
773
- return this.localSigner.encrypt(new NDKUser({ pubkey }), plaintext);
878
+ return this.localSigner.encrypt(pubkey as any, plaintext);
774
879
  } else {
775
880
  await this.ensureSigner();
776
- return this.signer!.encrypt(new NDKUser({ pubkey }), plaintext);
881
+ return this.signer!.encrypt(pubkey, plaintext);
777
882
  }
778
883
  }
779
884
 
780
- public async decrypt04(pubkey: string, ciphertext: string) {
885
+ public async decrypt04(pubkey: string, ciphertext: string): Promise<string> {
781
886
  if (this.localSigner) {
782
- return this.localSigner.decrypt(new NDKUser({ pubkey }), ciphertext);
887
+ return this.localSigner.decrypt(pubkey as any, ciphertext);
783
888
  } else {
784
- // decrypt is broken in ndk v2.3.1, and latest
785
- // ndk v2.8.1 doesn't allow to override connect easily,
786
- // so we reimplement and fix decrypt here as a temporary fix
787
889
  await this.ensureSigner();
788
- return this.codec_call('nip04_decrypt', pubkey, ciphertext);
890
+ return this.signer!.decrypt(pubkey, ciphertext);
789
891
  }
790
892
  }
791
893
 
792
- public async encrypt44(pubkey: string, plaintext: string) {
894
+ public async encrypt44(pubkey: string, plaintext: string): Promise<string> {
793
895
  if (this.localSigner) {
794
896
  return this.nip44Codec.encrypt(this.localSigner.privateKey!, pubkey, plaintext);
795
897
  } else {
796
- // no support of nip44 in ndk yet
797
898
  await this.ensureSigner();
798
- return this.codec_call('nip44_encrypt', pubkey, plaintext);
899
+ return this.signer!.encryptNip44(pubkey, plaintext);
799
900
  }
800
901
  }
801
902
 
802
- public async decrypt44(pubkey: string, ciphertext: string) {
903
+ public async decrypt44(pubkey: string, ciphertext: string): Promise<string> {
803
904
  if (this.localSigner) {
804
905
  return this.nip44Codec.decrypt(this.localSigner.privateKey!, pubkey, ciphertext);
805
906
  } else {
806
- // no support of nip44 in ndk yet
807
907
  await this.ensureSigner();
808
- return this.codec_call('nip44_decrypt', pubkey, ciphertext);
908
+ return this.signer!.decryptNip44(pubkey, ciphertext);
809
909
  }
810
910
  }
811
911
  }
@@ -1,5 +1,5 @@
1
1
  import { NostrLoginOptions, StartScreens, TypeModal } from '../types';
2
- import { checkNip05, getBunkerUrl, getDarkMode, localStorageRemoveRecent, localStorageSetItem, prepareSignupRelays } from '../utils';
2
+ import { checkNip05, getBunkerUrl, getDarkMode, localStorageRemoveRecent, localStorageSetItem, prepareSignupRelays, bytesToHex } from '../utils';
3
3
  import { AuthNostrService, NostrExtensionService, NostrParams } from '.';
4
4
  import { EventEmitter } from 'tseep';
5
5
  import { ConnectionString, Info, RecentType } from 'nostr-login-components/dist/types/types';
@@ -28,7 +28,7 @@ class ModalManager extends EventEmitter {
28
28
  if (this.launcherPromise) {
29
29
  try {
30
30
  await this.launcherPromise;
31
- } catch {}
31
+ } catch { }
32
32
  this.launcherPromise = undefined;
33
33
  }
34
34
  }
@@ -281,7 +281,7 @@ class ModalManager extends EventEmitter {
281
281
  throw new Error('Bad nsec value');
282
282
  }
283
283
  if (decoded.type !== 'nsec') throw new Error('Bad bech32 type');
284
- await this.authNostrService.localSignup('', decoded.data);
284
+ await this.authNostrService.localSignup('', bytesToHex(decoded.data));
285
285
  ok();
286
286
  } else if (nsecOrBunker.startsWith('bunker:')) {
287
287
  await this.authNostrService.authNip46('login', { name: '', bunkerUrl: nsecOrBunker });