@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,60 +1,138 @@
1
- import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
2
- import { validateEvent, verifySignature } from 'nostr-tools';
1
+ import { createRxForwardReq, createRxNostr, RxNostr } from 'rx-nostr';
2
+ import { EventEmitter } from 'tseep';
3
+ import { validateEvent, verifyEvent, type Event as NostrEventSDK } from 'nostr-tools';
3
4
  import { PrivateKeySigner } from './Signer';
4
5
  import { NIP46_REQUEST_TIMEOUT, NIP46_CONNECT_TIMEOUT } from '../const';
5
6
 
6
- // タイムアウト付きPromiseラッパー
7
7
  function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage: string): Promise<T> {
8
8
  return Promise.race([promise, new Promise<T>((_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs))]);
9
9
  }
10
+ // nostr-toolsに基づく厳格な型定義
11
+ type NostrFilter = {
12
+ ids?: string[];
13
+ kinds?: number[];
14
+ authors?: string[];
15
+ since?: number;
16
+ until?: number;
17
+ limit?: number;
18
+ search?: string;
19
+ [key: `#${string}`]: string[] | undefined; // タグフィルター (#e, #p, etc.)
20
+ };
21
+
22
+ type NostrEvent = {
23
+ id?: string;
24
+ kind: number;
25
+ pubkey: string;
26
+ content: string;
27
+ tags: string[][];
28
+ created_at: number;
29
+ sig?: string;
30
+ };
31
+
32
+ type NostrSubscription = {
33
+ on: (event: string, cb: any) => void;
34
+ stop: () => void;
35
+ };
36
+
37
+ type NDKRpcResponse = {
38
+ id: string;
39
+ pubkey: string;
40
+ content: string;
41
+ result?: string;
42
+ error?: string;
43
+ [key: string]: any;
44
+ };
45
+
46
+ type NDKRpcRequest = {
47
+ id: string;
48
+ pubkey: string;
49
+ method: string;
50
+ params: any[];
51
+ [key: string]: any;
52
+ };
53
+
54
+ // Helper to wrap rx-nostr subscription
55
+ class RxReqAdapter extends EventEmitter {
56
+ private sub: any;
57
+ constructor(rxNostr: RxNostr, filters: NostrFilter[], relays?: string[]) {
58
+ super();
59
+ const req = createRxForwardReq();
60
+ this.sub = rxNostr.use(req).subscribe((packet) => {
61
+ this.emit('event', packet.event);
62
+ });
63
+ // If relays are provided, we might want to ensure they are used?
64
+ // rx-nostr manages relays globally usually, but we can set default relays in the instance.
65
+ req.emit(filters as any);
66
+ }
67
+ stop() {
68
+ this.sub.unsubscribe();
69
+ }
70
+ }
10
71
 
11
- class NostrRpc extends NDKNostrRpc {
12
- protected _ndk: NDK;
72
+ // TODO: NDKの継承を削除する
73
+ class NostrRpc extends EventEmitter {
74
+ public rxNostr: RxNostr;
13
75
  protected _signer: PrivateKeySigner;
14
76
  protected requests: Set<string> = new Set();
15
- private sub?: NDKSubscription;
77
+ // private sub?: NDKSubscription;
78
+ private sub?: RxReqAdapter;
16
79
  protected _useNip44: boolean = false;
17
80
  private reconnectAttempts: number = 0;
18
81
  private maxReconnectAttempts: number = 3;
19
82
  private reconnectDelay: number = 2000;
20
83
 
21
- public constructor(ndk: NDK, signer: PrivateKeySigner) {
22
- super(ndk, signer, ndk.debug.extend('nip46:signer:rpc'));
23
- this._ndk = ndk;
84
+ public constructor(rxNostr: RxNostr, signer: PrivateKeySigner) {
85
+ super();
86
+ this.rxNostr = rxNostr;
24
87
  this._signer = signer;
25
88
  this.setupConnectionMonitoring();
26
89
  }
27
90
 
28
- public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
29
- // NOTE: fixing ndk
30
- filter.kinds = filter.kinds?.filter(k => k === 24133);
31
- this.sub = await super.subscribe(filter);
91
+ public async subscribe(filter: NostrFilter): Promise<NostrSubscription> {
92
+ const f: any = { ...filter };
93
+ if (f.kinds) {
94
+ f.kinds = f.kinds.filter((k: number) => k === 24133);
95
+ }
96
+
97
+ this.sub = new RxReqAdapter(this.rxNostr, [f]);
32
98
  return this.sub;
33
99
  }
34
100
 
35
- public stop() {
101
+ public stop(): void {
36
102
  if (this.sub) {
37
103
  this.sub.stop();
38
104
  this.sub = undefined;
39
105
  }
40
106
  }
41
107
 
42
- public setUseNip44(useNip44: boolean) {
108
+ public setUseNip44(useNip44: boolean): void {
43
109
  this._useNip44 = useNip44;
44
110
  }
45
111
 
46
- private isNip04(ciphertext: string) {
112
+ private isNip04(ciphertext: string): boolean {
47
113
  const l = ciphertext.length;
48
114
  if (l < 28) return false;
49
115
  return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
50
116
  }
51
117
 
52
118
  // override to auto-decrypt nip04/nip44
53
- public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
54
- const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
55
- remoteUser.ndk = this._ndk;
119
+ public async parseEvent(event: NostrEvent): Promise<any> {
120
+ // TODO: 元のNDKロジック:
121
+ // 1. NDKUser.from(event.pubkey) to get remote user.
122
+ // 2. Check content for NIP-04 pattern (?iv=...).
123
+ // 3. Call signer.decrypt(remoteUser, content) or signer.decryptNip44(remoteUser, content).
124
+ //
125
+ // New Implementation Plan:
126
+ // 1. Use direct NIP-04/NIP-44 decryption functions from local signer.
127
+ // 2. Pass event.pubkey (string) instead of NDKUser object.
128
+
129
+ // const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
130
+ // remoteUser.ndk = this._ndk;
131
+
56
132
  const decrypt = this.isNip04(event.content) ? this._signer.decrypt : this._signer.decryptNip44;
57
- const decryptedContent = await decrypt.call(this._signer, remoteUser, event.content);
133
+
134
+ // NOTE: We need to adjust _signer.decrypt signature to accept string pubkey
135
+ const decryptedContent = await decrypt.call(this._signer, event.pubkey as any, event.content);
58
136
  const parsedContent = JSON.parse(decryptedContent);
59
137
  const { id, method, params, result, error } = parsedContent;
60
138
 
@@ -65,8 +143,8 @@ class NostrRpc extends NDKNostrRpc {
65
143
  }
66
144
  }
67
145
 
68
- public async parseNostrConnectReply(reply: any, secret: string) {
69
- const event = new NDKEvent(this._ndk, reply);
146
+ public async parseNostrConnectReply(reply: any, secret: string): Promise<string> {
147
+ const event = reply as NostrEvent;
70
148
  const parsedEvent = await this.parseEvent(event);
71
149
  console.log('nostr connect parsedEvent', parsedEvent);
72
150
  if (!(parsedEvent as NDKRpcRequest).method) {
@@ -82,14 +160,15 @@ class NostrRpc extends NDKNostrRpc {
82
160
  // we just listed to an unsolicited reply to
83
161
  // our pubkey and if it's ack/secret - we're fine
84
162
  public async listen(nostrConnectSecret: string): Promise<string> {
163
+ // TODO: rx-nostrを使用して実装する
85
164
  const pubkey = this._signer.pubkey;
86
165
  console.log('nostr-login listening for conn to', pubkey);
87
166
  const sub = await this.subscribe({
88
167
  'kinds': [24133],
89
168
  '#p': [pubkey],
90
- });
169
+ } as any); // Cast to any because RxReqAdapter expects NostrFilter but we pass explicit strict filters that might differ slightly in type def
91
170
  return new Promise<string>((ok, err) => {
92
- sub.on('event', async (event: NDKEvent) => {
171
+ sub.on('event', async (event: NostrEvent) => {
93
172
  try {
94
173
  const parsedEvent = await this.parseEvent(event);
95
174
  // console.log('ack parsedEvent', parsedEvent);
@@ -108,7 +187,7 @@ class NostrRpc extends NDKNostrRpc {
108
187
  }
109
188
  }
110
189
  } catch (e) {
111
- console.log('error parsing event', e, event.rawEvent());
190
+ console.log('error parsing event', e, (event as any).rawEvent?.());
112
191
  }
113
192
  // done
114
193
  this.stop();
@@ -119,7 +198,8 @@ class NostrRpc extends NDKNostrRpc {
119
198
  // since ndk doesn't yet support perms param
120
199
  // we reimplement the 'connect' call here
121
200
  // instead of await signer.blockUntilReady();
122
- public async connect(pubkey: string, token?: string, perms?: string) {
201
+ public async connect(pubkey: string, token?: string, perms?: string): Promise<void> {
202
+ // TODO: nostr-toolsを使用して実装する
123
203
  return new Promise<void>((ok, err) => {
124
204
  const connectParams = [pubkey!, token || '', perms || ''];
125
205
  this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
@@ -161,7 +241,7 @@ class NostrRpc extends NDKNostrRpc {
161
241
  }
162
242
 
163
243
  // 接続監視のセットアップ
164
- private setupConnectionMonitoring() {
244
+ private setupConnectionMonitoring(): void {
165
245
  // アプリがフォアグラウンドに戻ったときの処理
166
246
  if (typeof document !== 'undefined') {
167
247
  document.addEventListener('visibilitychange', async () => {
@@ -179,11 +259,22 @@ class NostrRpc extends NDKNostrRpc {
179
259
  await this.reconnect();
180
260
  });
181
261
  }
262
+
263
+ // 定期的な接続状態チェック(オプション)
264
+ setInterval(() => {
265
+ const stats = this.rxNostr.getAllRelayStatus();
266
+ const connected = Object.values(stats).filter(s => String(s) === 'connected').length;
267
+ if (connected === 0) {
268
+ console.warn('No relays connected, triggering reconnection...');
269
+ this.ensureConnected();
270
+ }
271
+ }, 60000); // 60秒ごとにチェック
182
272
  }
183
273
 
184
274
  // 接続を確認して必要なら再接続
185
275
  private async ensureConnected(): Promise<void> {
186
- const connectedRelays = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1); // 1 = CONNECTED
276
+ const states = this.rxNostr.getAllRelayStatus();
277
+ const connectedRelays = Object.values(states).filter(s => String(s) === 'connected');
187
278
 
188
279
  if (connectedRelays.length === 0) {
189
280
  console.log('No connected relays, attempting reconnection...');
@@ -191,7 +282,7 @@ class NostrRpc extends NDKNostrRpc {
191
282
  }
192
283
  }
193
284
 
194
- // 再接続処理
285
+ // 再接続処理(指数バックオフ付き)
195
286
  protected async reconnect(): Promise<void> {
196
287
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
197
288
  console.error('Max reconnection attempts reached');
@@ -208,25 +299,42 @@ class NostrRpc extends NDKNostrRpc {
208
299
  this.sub.stop();
209
300
  }
210
301
 
211
- // 少し待ってから再接続
212
- await new Promise(resolve => setTimeout(resolve, this.reconnectDelay));
213
-
214
- // 再接続
215
- await this._ndk.connect();
216
-
217
- // サブスクリプションを再開
218
- if (this.sub) {
219
- await this.sub.start();
302
+ // 指数バックオフ: 2^attempt * baseDelay (最大30秒)
303
+ const backoffDelay = Math.min(
304
+ this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
305
+ 30000
306
+ );
307
+ console.log(`Waiting ${backoffDelay}ms before reconnection...`);
308
+ await new Promise(resolve => setTimeout(resolve, backoffDelay));
309
+
310
+ // 全リレーに再接続を試みる
311
+ const relays = Object.keys(this.rxNostr.getAllRelayStatus());
312
+ if (relays.length > 0) {
313
+ for (const relay of relays) {
314
+ try {
315
+ this.rxNostr.reconnect(relay);
316
+ console.log(`Reconnected to ${relay}`);
317
+ } catch (error) {
318
+ console.warn(`Failed to reconnect to ${relay}:`, error);
319
+ }
320
+ }
220
321
  }
221
322
 
222
- this.reconnectAttempts = 0;
223
- this.emit('reconnected');
224
- console.log('Successfully reconnected to relays');
225
- } catch (e) {
226
- console.error('Reconnection failed:', e);
227
-
228
- // リトライ
229
- setTimeout(() => this.reconnect(), this.reconnectDelay * this.reconnectAttempts);
323
+ // 再接続成功後、カウンターをリセット
324
+ setTimeout(() => {
325
+ const states = this.rxNostr.getAllRelayStatus();
326
+ const connected = Object.values(states).filter(s => String(s) === 'connected').length;
327
+ if (connected > 0) {
328
+ console.log('Reconnection successful, resetting attempt counter');
329
+ this.reconnectAttempts = 0;
330
+ this.emit('reconnected');
331
+ }
332
+ }, 2000);
333
+ } catch (error) {
334
+ console.error('Reconnection error:', error);
335
+ this.emit('reconnectError', error);
336
+ // 次の再接続を試みる
337
+ setTimeout(() => this.reconnect(), this.reconnectDelay);
230
338
  }
231
339
  }
232
340
 
@@ -245,7 +353,11 @@ class NostrRpc extends NDKNostrRpc {
245
353
  console.log('sendRequest', { event, method, remotePubkey, params });
246
354
 
247
355
  // send to relays
248
- await event.publish();
356
+ // await event.publish();
357
+ this.rxNostr.send(event).subscribe({
358
+ error: (err) => console.error('Nip46 publish error', err),
359
+ complete: () => console.log('Nip46 publish complete')
360
+ });
249
361
 
250
362
  // NOTE: ndk returns a promise that never resolves and
251
363
  // in fact REQUIRES cb to be provided (otherwise no way
@@ -256,7 +368,8 @@ class NostrRpc extends NDKNostrRpc {
256
368
  return undefined as NDKRpcResponse;
257
369
  }
258
370
 
259
- protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
371
+ protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
372
+ // TODO: 再実装する
260
373
  let authUrlSent = false;
261
374
  const now = Date.now();
262
375
  return new Promise<NDKRpcResponse>(() => {
@@ -280,22 +393,43 @@ class NostrRpc extends NDKNostrRpc {
280
393
  });
281
394
  }
282
395
 
283
- protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
396
+ protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133): Promise<NostrEvent> {
397
+ // TODO: 元のNDKロジック:
398
+ // const event = new NDKEvent(this._ndk, { ... });
399
+ // await event.sign(this._signer);
400
+ //
401
+ // New Implementation Plan:
402
+ // 1. Create a plain event object:
403
+ // { kind, content, tags, pubkey, created_at: Math.floor(Date.now()/1000) }
404
+ // 2. Encrypt content:
405
+ // if (useNip44) await signer.encryptNip44(remotePubkey, content)
406
+ // else await signer.encrypt(remotePubkey, content)
407
+ // 3. Sign:
408
+ // event.id = getEventHash(event)
409
+ // event.sig = await signer.signEvent(event)
410
+ // 4. Return the signed event object.
411
+
284
412
  this.requests.add(id);
285
- const localUser = await this._signer.user();
286
- const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
413
+ const localUser = await this._signer.user(); // TODO: signerプロパティから取得する
414
+ // const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
287
415
  const request = { id, method, params };
288
416
 
289
- const event = new NDKEvent(this._ndk, {
417
+ // Placeholder event construction
418
+ const event = {
290
419
  kind,
291
420
  content: JSON.stringify(request),
292
421
  tags: [['p', remotePubkey]],
293
422
  pubkey: localUser.pubkey,
294
- } as NostrEvent);
423
+ created_at: Math.floor(Date.now() / 1000),
424
+ } as any;
295
425
 
296
426
  const useNip44 = this._useNip44 && method !== 'create_account';
297
427
  const encrypt = useNip44 ? this._signer.encryptNip44 : this._signer.encrypt;
298
- event.content = await encrypt.call(this._signer, remoteUser, event.content);
428
+
429
+ // NOTE: Adjust encrypt to accept string pubkey
430
+ event.content = await encrypt.call(this._signer, remotePubkey as any, event.content);
431
+
432
+ // NOTE: This assumes _signer has a sign() method compatible with the event
299
433
  await event.sign(this._signer);
300
434
 
301
435
  return event;
@@ -310,27 +444,29 @@ export class IframeNostrRpc extends NostrRpc {
310
444
  private lastResponseTime: number = Date.now();
311
445
  private heartbeatTimeoutMs: number = 30000; // 30秒応答がなければ再接続
312
446
 
313
- public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
314
- super(ndk, localSigner);
315
- this._ndk = ndk;
447
+ public constructor(rxNostr: RxNostr, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
448
+ super(rxNostr, localSigner);
316
449
  this.peerOrigin = iframePeerOrigin;
317
450
  }
318
451
 
319
- public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
452
+ public async subscribe(filter: NostrFilter): Promise<NostrSubscription> {
320
453
  if (!this.peerOrigin) return super.subscribe(filter);
454
+ return super.subscribe(filter);
455
+ /*
321
456
  return new NDKSubscription(
322
457
  this._ndk,
323
458
  {},
324
459
  {
325
460
  // don't send to relay
326
461
  closeOnEose: true,
327
- cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
462
+ cacheUsage: 'ONLY_CACHE',
328
463
  },
329
464
  );
465
+ */
330
466
  }
331
467
 
332
468
  // ハートビート開始
333
- private startHeartbeat() {
469
+ private startHeartbeat(): void {
334
470
  this.stopHeartbeat();
335
471
 
336
472
  this.heartbeatInterval = window.setInterval(async () => {
@@ -343,14 +479,14 @@ export class IframeNostrRpc extends NostrRpc {
343
479
  }, 10000); // 10秒ごとにチェック
344
480
  }
345
481
 
346
- private stopHeartbeat() {
482
+ private stopHeartbeat(): void {
347
483
  if (this.heartbeatInterval) {
348
484
  clearInterval(this.heartbeatInterval);
349
485
  this.heartbeatInterval = undefined;
350
486
  }
351
487
  }
352
488
 
353
- public setWorkerIframePort(port: MessagePort) {
489
+ public setWorkerIframePort(port: MessagePort): void {
354
490
  if (!this.peerOrigin) throw new Error('Unexpected iframe port');
355
491
 
356
492
  this.iframePort = port;
@@ -376,8 +512,8 @@ export class IframeNostrRpc extends NostrRpc {
376
512
  const event = ev.data;
377
513
 
378
514
  if (!validateEvent(event)) throw new Error('Invalid event from iframe');
379
- if (!verifySignature(event)) throw new Error('Invalid event signature from iframe');
380
- const nevent = new NDKEvent(this._ndk, event);
515
+ if (!verifyEvent(event)) throw new Error('Invalid event signature from iframe');
516
+ const nevent = event as NostrEvent;
381
517
  const parsedEvent = await this.parseEvent(nevent);
382
518
  // レスポンス受信時にタイムスタンプを更新
383
519
  this.lastResponseTime = Date.now();
@@ -398,23 +534,22 @@ export class IframeNostrRpc extends NostrRpc {
398
534
  // create and sign request event
399
535
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
400
536
 
401
- // set response handler, it will dedup auth urls,
402
- // and also dedup response handlers - we're sending
403
- // to relays and to iframe
537
+ // set response handler
404
538
  this.setResponseHandler(id, cb);
405
539
 
406
540
  if (this.iframePort) {
407
- // map request event id to request id, if iframe
408
- // has no key it will reply with error:event_id (it can't
409
- // decrypt the request id without keys)
410
- this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
411
-
412
- // send to iframe
413
- console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
414
- this.iframePort.postMessage(event.rawEvent());
541
+ this.iframeRequests.set(event.id!, { id, pubkey: remotePubkey });
542
+ console.log('iframe-nip46 sending request to', this.peerOrigin, event);
543
+ this.iframePort.postMessage(event);
415
544
  } else {
416
- // send to relays
417
- await event.publish();
545
+ // send to relays using rx-nostr
546
+ this.rxNostr.send(event).subscribe({
547
+ next: (packet) => {
548
+ console.log('Nip46 request published to', packet.from);
549
+ },
550
+ error: (err) => console.error('Nip46 publish error', err),
551
+ complete: () => console.log('Nip46 publish complete')
552
+ });
418
553
  }
419
554
 
420
555
  // see notes in 'super'
@@ -472,15 +607,20 @@ export class ReadyListener {
472
607
  }
473
608
  }
474
609
 
475
- export class Nip46Signer extends NDKNip46Signer {
610
+ // TODO: NDKの継承を削除する
611
+ export class Nip46Signer extends EventEmitter {
612
+ public remotePubkey: string = '';
613
+ // @ts-ignore
614
+ public rpc: NostrRpc;
476
615
  private _userPubkey: string = '';
477
616
  private _rpc: IframeNostrRpc;
478
617
 
479
- constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
480
- super(ndk, signerPubkey, localSigner);
618
+ constructor(rxNostr: RxNostr, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
619
+ super();
620
+ // super(ndk, signerPubkey, localSigner);
481
621
 
482
622
  // override with our own rpc implementation
483
- this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
623
+ this._rpc = new IframeNostrRpc(rxNostr, localSigner, iframeOrigin);
484
624
  this._rpc.setUseNip44(true); // !!this.params.optionsModal.dev);
485
625
  this._rpc.on('authUrl', (url: string) => {
486
626
  this.emit('authUrl', url);
@@ -489,11 +629,11 @@ export class Nip46Signer extends NDKNip46Signer {
489
629
  this.rpc = this._rpc;
490
630
  }
491
631
 
492
- get userPubkey() {
632
+ get userPubkey(): string {
493
633
  return this._userPubkey;
494
634
  }
495
635
 
496
- private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
636
+ private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false): Promise<void> {
497
637
  console.log('setSignerPubkey', signerPubkey);
498
638
 
499
639
  // ensure it's set
@@ -508,7 +648,7 @@ export class Nip46Signer extends NDKNip46Signer {
508
648
  await this.initUserPubkey(sameAsUser ? signerPubkey : '');
509
649
  }
510
650
 
511
- public async initUserPubkey(hintPubkey?: string) {
651
+ public async initUserPubkey(hintPubkey?: string): Promise<void> {
512
652
  if (this._userPubkey) throw new Error('Already called initUserPubkey');
513
653
 
514
654
  if (hintPubkey) {
@@ -525,7 +665,7 @@ export class Nip46Signer extends NDKNip46Signer {
525
665
  if (response.error) {
526
666
  err(new Error(response.error));
527
667
  } else {
528
- ok(response.result);
668
+ ok(response.result || '');
529
669
  }
530
670
  });
531
671
  }),
@@ -534,23 +674,23 @@ export class Nip46Signer extends NDKNip46Signer {
534
674
  );
535
675
  }
536
676
 
537
- public async listen(nostrConnectSecret: string) {
677
+ public async listen(nostrConnectSecret: string): Promise<void> {
538
678
  const signerPubkey = await (this.rpc as IframeNostrRpc).listen(nostrConnectSecret);
539
679
  await this.setSignerPubkey(signerPubkey);
540
680
  }
541
681
 
542
- public async connect(token?: string, perms?: string) {
682
+ public async connect(token?: string, perms?: string): Promise<void> {
543
683
  if (!this.remotePubkey) throw new Error('No signer pubkey');
544
684
  await (this._rpc as any).connectWithTimeout(this.remotePubkey, token, perms, NIP46_CONNECT_TIMEOUT);
545
685
  await this.setSignerPubkey(this.remotePubkey);
546
686
  }
547
687
 
548
- public async setListenReply(reply: any, nostrConnectSecret: string) {
688
+ public async setListenReply(reply: any, nostrConnectSecret: string): Promise<void> {
549
689
  const signerPubkey = await this._rpc.parseNostrConnectReply(reply, nostrConnectSecret);
550
690
  await this.setSignerPubkey(signerPubkey, true);
551
691
  }
552
692
 
553
- public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
693
+ public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }): Promise<string> {
554
694
  const params = [
555
695
  name,
556
696
  domain,
@@ -563,10 +703,52 @@ export class Nip46Signer extends NDKNip46Signer {
563
703
  });
564
704
 
565
705
  console.log('create_account pubkey', r);
566
- if (r.result === 'error') {
567
- throw new Error(r.error);
706
+ if (r.result === 'error' || !r.result) {
707
+ throw new Error(r.error || 'Unknown error');
568
708
  }
569
709
 
570
710
  return r.result;
571
711
  }
712
+ // TODO: 必要であればNDKNip46Signerから不足しているメソッドを実装する
713
+ public async encrypt(recipient: any, value: string): Promise<string> {
714
+ const recipientPubkey = typeof recipient === 'string' ? recipient : recipient.pubkey;
715
+ return await this.rpcSend('nip04_encrypt', [recipientPubkey, value]);
716
+ }
717
+
718
+ public async decrypt(sender: any, value: string): Promise<string> {
719
+ const senderPubkey = typeof sender === 'string' ? sender : sender.pubkey;
720
+ return await this.rpcSend('nip04_decrypt', [senderPubkey, value]);
721
+ }
722
+
723
+ public async encryptNip44(recipient: any, value: string): Promise<string> {
724
+ const recipientPubkey = typeof recipient === 'string' ? recipient : recipient.pubkey;
725
+ return await this.rpcSend('nip44_encrypt', [recipientPubkey, value]);
726
+ }
727
+
728
+ public async decryptNip44(sender: any, value: string): Promise<string> {
729
+ const senderPubkey = typeof sender === 'string' ? sender : sender.pubkey;
730
+ return await this.rpcSend('nip44_decrypt', [senderPubkey, value]);
731
+ }
732
+
733
+ public async sign(event: any): Promise<string> {
734
+ const eventString = JSON.stringify(event);
735
+ const res = await this.rpcSend('sign_event', [eventString]);
736
+ // The result matches NIP-46 sign_event response which is the signed event (stringified json)
737
+ const signedEvent = JSON.parse(res);
738
+ event.sig = signedEvent.sig;
739
+ return signedEvent.sig;
740
+ }
741
+
742
+ public async user(): Promise<any> {
743
+ return { pubkey: this.userPubkey };
744
+ }
745
+
746
+ private async rpcSend(method: string, params: any[]): Promise<string> {
747
+ return new Promise<string>((resolve, reject) => {
748
+ this.rpc.sendRequest(this.remotePubkey, method, params, 24133, (response: any) => {
749
+ if (response.error) reject(new Error(response.error));
750
+ else resolve(response.result);
751
+ });
752
+ });
753
+ }
572
754
  }
@@ -60,6 +60,7 @@ class NostrExtensionService extends EventEmitter {
60
60
  }
61
61
 
62
62
  private async setExtensionReadPubkey(expectedPubkey?: string) {
63
+ // @ts-ignore
63
64
  window.nostr = this.nostrExtension;
64
65
  // @ts-ignore
65
66
  const pubkey = await window.nostr.getPublicKey();
@@ -81,6 +82,7 @@ class NostrExtensionService extends EventEmitter {
81
82
  }
82
83
 
83
84
  public unsetExtension(nostr: Nostr) {
85
+ // @ts-ignore
84
86
  if (window.nostr === this.nostrExtension) {
85
87
  // @ts-ignore
86
88
  window.nostr = nostr;