@konemono/nostr-login 1.11.0 → 1.11.1

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,138 +1,136 @@
1
- import { createRxForwardReq, createRxNostr, RxNostr } from 'rx-nostr';
2
- import { EventEmitter } from 'tseep';
3
- import { validateEvent, verifyEvent, type Event as NostrEventSDK } from 'nostr-tools';
1
+ import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
2
+ import { validateEvent } from 'nostr-tools';
4
3
  import { PrivateKeySigner } from './Signer';
5
4
  import { NIP46_REQUEST_TIMEOUT, NIP46_CONNECT_TIMEOUT } from '../const';
6
5
 
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);
10
+
11
+ /**
12
+ * NDKのリレー接続を待機する
13
+ */
14
+ export function waitForConnection(ndk: NDK, timeout: number): Promise<void> {
15
+ return new Promise((resolve, reject) => {
16
+ const start = Date.now();
17
+
18
+ const check = () => {
19
+ // 接続済みリレーがあるか確認
20
+ const connected = Array.from(ndk.pool.relays.values()).some(relay => relay.status === 1); // 1 = CONNECTED
21
+
22
+ if (connected) {
23
+ resolve();
24
+ } else if (Date.now() - start > timeout) {
25
+ reject(new Error(`接続タイムアウト: ${timeout}ms`));
26
+ } else {
27
+ setTimeout(check, 100);
28
+ }
29
+ };
30
+
31
+ check();
32
+ });
33
+ }
34
+
35
+ /**
36
+ * NDKの接続を強制的に確立する
37
+ */
38
+ export async function ensureNDKConnection(ndk: NDK, timeout: number): Promise<void> {
39
+ // connect()は冪等なので安全
40
+ await ndk.connect();
41
+ await waitForConnection(ndk, timeout);
42
+ }
43
+
44
+ /**
45
+ * NIP-46専用リレーも含めて接続を確立する
46
+ */
47
+ export async function ensureNIP46Connection(ndk: NDK, timeout: number): Promise<void> {
48
+ try {
49
+ await ensureNDKConnection(ndk, timeout);
50
+ } catch (e) {
51
+ console.warn('Initial connection attempt failed, retrying with force disconnect...', e);
52
+ // 接続数0なら明示的に全切断して再試行
53
+ if (Array.from(ndk.pool.relays.values()).filter(r => r.status === 1).length === 0) {
54
+ ndk.pool.relays.forEach(relay => {
55
+ try {
56
+ relay.disconnect();
57
+ } catch (err) {
58
+ console.error('Error disconnecting relay:', err);
59
+ }
60
+ });
61
+ // タイムアウトを延長して再試行
62
+ await ensureNDKConnection(ndk, timeout * 2);
63
+ } else {
64
+ throw e;
65
+ }
66
+ }
67
+
68
+ // NIP-46用リレーが個別に定義されている場合の接続確認
69
+ // @ts-ignore
70
+ const signerRelays = ndk.signer?.relayUrls;
71
+ if (signerRelays && Array.isArray(signerRelays)) {
72
+ for (const url of signerRelays) {
73
+ const relay = ndk.pool.getRelay(url);
74
+ if (relay && relay.status !== 1) {
75
+ await relay.connect();
76
+ }
77
+ }
66
78
  }
67
- stop() {
68
- this.sub.unsubscribe();
79
+
80
+ // 最終チェック
81
+ const connectedRelays = Array.from(ndk.pool.relays.values()).filter(r => r.status === 1);
82
+ if (connectedRelays.length === 0) {
83
+ throw new Error('リレー接続に失敗しました');
69
84
  }
70
85
  }
71
86
 
72
- // TODO: NDKの継承を削除する
73
- class NostrRpc extends EventEmitter {
74
- public rxNostr: RxNostr;
87
+ class NostrRpc extends NDKNostrRpc {
88
+ protected _ndk: NDK;
75
89
  protected _signer: PrivateKeySigner;
76
90
  protected requests: Set<string> = new Set();
77
- // private sub?: NDKSubscription;
78
- private sub?: RxReqAdapter;
91
+ private sub?: NDKSubscription;
79
92
  protected _useNip44: boolean = false;
80
93
  private reconnectAttempts: number = 0;
81
94
  private maxReconnectAttempts: number = 3;
82
95
  private reconnectDelay: number = 2000;
83
96
 
84
- public constructor(rxNostr: RxNostr, signer: PrivateKeySigner) {
85
- super();
86
- this.rxNostr = rxNostr;
97
+ public constructor(ndk: NDK, signer: PrivateKeySigner) {
98
+ super(ndk, signer, ndk.debug.extend('nip46:signer:rpc'));
99
+ this._ndk = ndk;
87
100
  this._signer = signer;
88
101
  this.setupConnectionMonitoring();
89
102
  }
90
103
 
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]);
104
+ public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
105
+ // NOTE: fixing ndk
106
+ filter.kinds = filter.kinds?.filter(k => k === 24133);
107
+ this.sub = await super.subscribe(filter);
98
108
  return this.sub;
99
109
  }
100
110
 
101
- public stop(): void {
111
+ public stop() {
102
112
  if (this.sub) {
103
113
  this.sub.stop();
104
114
  this.sub = undefined;
105
115
  }
106
116
  }
107
117
 
108
- public setUseNip44(useNip44: boolean): void {
118
+ public setUseNip44(useNip44: boolean) {
109
119
  this._useNip44 = useNip44;
110
120
  }
111
121
 
112
- private isNip04(ciphertext: string): boolean {
122
+ private isNip04(ciphertext: string) {
113
123
  const l = ciphertext.length;
114
124
  if (l < 28) return false;
115
125
  return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
116
126
  }
117
127
 
118
128
  // override to auto-decrypt nip04/nip44
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
-
129
+ public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
130
+ const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
131
+ remoteUser.ndk = this._ndk;
132
132
  const decrypt = this.isNip04(event.content) ? this._signer.decrypt : this._signer.decryptNip44;
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);
133
+ const decryptedContent = await decrypt.call(this._signer, remoteUser, event.content);
136
134
  const parsedContent = JSON.parse(decryptedContent);
137
135
  const { id, method, params, result, error } = parsedContent;
138
136
 
@@ -143,8 +141,8 @@ class NostrRpc extends EventEmitter {
143
141
  }
144
142
  }
145
143
 
146
- public async parseNostrConnectReply(reply: any, secret: string): Promise<string> {
147
- const event = reply as NostrEvent;
144
+ public async parseNostrConnectReply(reply: any, secret: string) {
145
+ const event = new NDKEvent(this._ndk, reply);
148
146
  const parsedEvent = await this.parseEvent(event);
149
147
  console.log('nostr connect parsedEvent', parsedEvent);
150
148
  if (!(parsedEvent as NDKRpcRequest).method) {
@@ -160,15 +158,14 @@ class NostrRpc extends EventEmitter {
160
158
  // we just listed to an unsolicited reply to
161
159
  // our pubkey and if it's ack/secret - we're fine
162
160
  public async listen(nostrConnectSecret: string): Promise<string> {
163
- // TODO: rx-nostrを使用して実装する
164
161
  const pubkey = this._signer.pubkey;
165
162
  console.log('nostr-login listening for conn to', pubkey);
166
163
  const sub = await this.subscribe({
167
164
  'kinds': [24133],
168
165
  '#p': [pubkey],
169
- } as any); // Cast to any because RxReqAdapter expects NostrFilter but we pass explicit strict filters that might differ slightly in type def
166
+ });
170
167
  return new Promise<string>((ok, err) => {
171
- sub.on('event', async (event: NostrEvent) => {
168
+ sub.on('event', async (event: NDKEvent) => {
172
169
  try {
173
170
  const parsedEvent = await this.parseEvent(event);
174
171
  // console.log('ack parsedEvent', parsedEvent);
@@ -187,7 +184,7 @@ class NostrRpc extends EventEmitter {
187
184
  }
188
185
  }
189
186
  } catch (e) {
190
- console.log('error parsing event', e, (event as any).rawEvent?.());
187
+ console.log('error parsing event', e, event.rawEvent());
191
188
  }
192
189
  // done
193
190
  this.stop();
@@ -198,8 +195,7 @@ class NostrRpc extends EventEmitter {
198
195
  // since ndk doesn't yet support perms param
199
196
  // we reimplement the 'connect' call here
200
197
  // instead of await signer.blockUntilReady();
201
- public async connect(pubkey: string, token?: string, perms?: string): Promise<void> {
202
- // TODO: nostr-toolsを使用して実装する
198
+ public async connect(pubkey: string, token?: string, perms?: string) {
203
199
  return new Promise<void>((ok, err) => {
204
200
  const connectParams = [pubkey!, token || '', perms || ''];
205
201
  this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
@@ -241,7 +237,7 @@ class NostrRpc extends EventEmitter {
241
237
  }
242
238
 
243
239
  // 接続監視のセットアップ
244
- private setupConnectionMonitoring(): void {
240
+ private setupConnectionMonitoring() {
245
241
  // アプリがフォアグラウンドに戻ったときの処理
246
242
  if (typeof document !== 'undefined') {
247
243
  document.addEventListener('visibilitychange', async () => {
@@ -259,22 +255,11 @@ class NostrRpc extends EventEmitter {
259
255
  await this.reconnect();
260
256
  });
261
257
  }
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秒ごとにチェック
272
258
  }
273
259
 
274
260
  // 接続を確認して必要なら再接続
275
261
  private async ensureConnected(): Promise<void> {
276
- const states = this.rxNostr.getAllRelayStatus();
277
- const connectedRelays = Object.values(states).filter(s => String(s) === 'connected');
262
+ const connectedRelays = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1); // 1 = CONNECTED
278
263
 
279
264
  if (connectedRelays.length === 0) {
280
265
  console.log('No connected relays, attempting reconnection...');
@@ -282,7 +267,7 @@ class NostrRpc extends EventEmitter {
282
267
  }
283
268
  }
284
269
 
285
- // 再接続処理(指数バックオフ付き)
270
+ // 再接続処理
286
271
  protected async reconnect(): Promise<void> {
287
272
  if (this.reconnectAttempts >= this.maxReconnectAttempts) {
288
273
  console.error('Max reconnection attempts reached');
@@ -299,42 +284,25 @@ class NostrRpc extends EventEmitter {
299
284
  this.sub.stop();
300
285
  }
301
286
 
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
- }
287
+ // 少し待ってから再接続
288
+ await new Promise(resolve => setTimeout(resolve, this.reconnectDelay));
289
+
290
+ // 再接続
291
+ await this._ndk.connect();
292
+
293
+ // サブスクリプションを再開
294
+ if (this.sub) {
295
+ await this.sub.start();
321
296
  }
322
297
 
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);
298
+ this.reconnectAttempts = 0;
299
+ this.emit('reconnected');
300
+ console.log('Successfully reconnected to relays');
301
+ } catch (e) {
302
+ console.error('Reconnection failed:', e);
303
+
304
+ // リトライ
305
+ setTimeout(() => this.reconnect(), this.reconnectDelay * this.reconnectAttempts);
338
306
  }
339
307
  }
340
308
 
@@ -343,6 +311,9 @@ class NostrRpc extends EventEmitter {
343
311
  }
344
312
 
345
313
  public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
314
+ // リクエスト送信前に接続を確認
315
+ await ensureNIP46Connection(this._ndk, 5000);
316
+
346
317
  const id = this.getId();
347
318
 
348
319
  // response handler will deduplicate auth urls and responses
@@ -353,11 +324,7 @@ class NostrRpc extends EventEmitter {
353
324
  console.log('sendRequest', { event, method, remotePubkey, params });
354
325
 
355
326
  // send to relays
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
- });
327
+ await event.publish();
361
328
 
362
329
  // NOTE: ndk returns a promise that never resolves and
363
330
  // in fact REQUIRES cb to be provided (otherwise no way
@@ -368,8 +335,7 @@ class NostrRpc extends EventEmitter {
368
335
  return undefined as NDKRpcResponse;
369
336
  }
370
337
 
371
- protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
372
- // TODO: 再実装する
338
+ protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
373
339
  let authUrlSent = false;
374
340
  const now = Date.now();
375
341
  return new Promise<NDKRpcResponse>(() => {
@@ -393,43 +359,22 @@ class NostrRpc extends EventEmitter {
393
359
  });
394
360
  }
395
361
 
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
-
362
+ protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
412
363
  this.requests.add(id);
413
- const localUser = await this._signer.user(); // TODO: signerプロパティから取得する
414
- // const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
364
+ const localUser = await this._signer.user();
365
+ const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
415
366
  const request = { id, method, params };
416
367
 
417
- // Placeholder event construction
418
- const event = {
368
+ const event = new NDKEvent(this._ndk, {
419
369
  kind,
420
370
  content: JSON.stringify(request),
421
371
  tags: [['p', remotePubkey]],
422
372
  pubkey: localUser.pubkey,
423
- created_at: Math.floor(Date.now() / 1000),
424
- } as any;
373
+ } as NostrEvent);
425
374
 
426
375
  const useNip44 = this._useNip44 && method !== 'create_account';
427
376
  const encrypt = useNip44 ? this._signer.encryptNip44 : this._signer.encrypt;
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
377
+ event.content = await encrypt.call(this._signer, remoteUser, event.content);
433
378
  await event.sign(this._signer);
434
379
 
435
380
  return event;
@@ -444,29 +389,27 @@ export class IframeNostrRpc extends NostrRpc {
444
389
  private lastResponseTime: number = Date.now();
445
390
  private heartbeatTimeoutMs: number = 30000; // 30秒応答がなければ再接続
446
391
 
447
- public constructor(rxNostr: RxNostr, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
448
- super(rxNostr, localSigner);
392
+ public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
393
+ super(ndk, localSigner);
394
+ this._ndk = ndk;
449
395
  this.peerOrigin = iframePeerOrigin;
450
396
  }
451
397
 
452
- public async subscribe(filter: NostrFilter): Promise<NostrSubscription> {
398
+ public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
453
399
  if (!this.peerOrigin) return super.subscribe(filter);
454
- return super.subscribe(filter);
455
- /*
456
400
  return new NDKSubscription(
457
401
  this._ndk,
458
402
  {},
459
403
  {
460
404
  // don't send to relay
461
405
  closeOnEose: true,
462
- cacheUsage: 'ONLY_CACHE',
406
+ cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
463
407
  },
464
408
  );
465
- */
466
409
  }
467
410
 
468
411
  // ハートビート開始
469
- private startHeartbeat(): void {
412
+ private startHeartbeat() {
470
413
  this.stopHeartbeat();
471
414
 
472
415
  this.heartbeatInterval = window.setInterval(async () => {
@@ -479,14 +422,14 @@ export class IframeNostrRpc extends NostrRpc {
479
422
  }, 10000); // 10秒ごとにチェック
480
423
  }
481
424
 
482
- private stopHeartbeat(): void {
425
+ private stopHeartbeat() {
483
426
  if (this.heartbeatInterval) {
484
427
  clearInterval(this.heartbeatInterval);
485
428
  this.heartbeatInterval = undefined;
486
429
  }
487
430
  }
488
431
 
489
- public setWorkerIframePort(port: MessagePort): void {
432
+ public setWorkerIframePort(port: MessagePort) {
490
433
  if (!this.peerOrigin) throw new Error('Unexpected iframe port');
491
434
 
492
435
  this.iframePort = port;
@@ -512,8 +455,7 @@ export class IframeNostrRpc extends NostrRpc {
512
455
  const event = ev.data;
513
456
 
514
457
  if (!validateEvent(event)) throw new Error('Invalid event from iframe');
515
- if (!verifyEvent(event)) throw new Error('Invalid event signature from iframe');
516
- const nevent = event as NostrEvent;
458
+ const nevent = new NDKEvent(this._ndk, event);
517
459
  const parsedEvent = await this.parseEvent(nevent);
518
460
  // レスポンス受信時にタイムスタンプを更新
519
461
  this.lastResponseTime = Date.now();
@@ -529,27 +471,33 @@ export class IframeNostrRpc extends NostrRpc {
529
471
  }
530
472
 
531
473
  public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
474
+ // リクエスト送信前に接続を確認 (relayを使用する場合に備えて)
475
+ if (!this.iframePort) {
476
+ await ensureNIP46Connection(this._ndk, 5000);
477
+ }
478
+
532
479
  const id = this.getId();
533
480
 
534
481
  // create and sign request event
535
482
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
536
483
 
537
- // set response handler
484
+ // set response handler, it will dedup auth urls,
485
+ // and also dedup response handlers - we're sending
486
+ // to relays and to iframe
538
487
  this.setResponseHandler(id, cb);
539
488
 
540
489
  if (this.iframePort) {
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);
490
+ // map request event id to request id, if iframe
491
+ // has no key it will reply with error:event_id (it can't
492
+ // decrypt the request id without keys)
493
+ this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
494
+
495
+ // send to iframe
496
+ console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
497
+ this.iframePort.postMessage(event.rawEvent());
544
498
  } else {
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
- });
499
+ // send to relays
500
+ await event.publish();
553
501
  }
554
502
 
555
503
  // see notes in 'super'
@@ -607,20 +555,15 @@ export class ReadyListener {
607
555
  }
608
556
  }
609
557
 
610
- // TODO: NDKの継承を削除する
611
- export class Nip46Signer extends EventEmitter {
612
- public remotePubkey: string = '';
613
- // @ts-ignore
614
- public rpc: NostrRpc;
558
+ export class Nip46Signer extends NDKNip46Signer {
615
559
  private _userPubkey: string = '';
616
560
  private _rpc: IframeNostrRpc;
617
561
 
618
- constructor(rxNostr: RxNostr, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
619
- super();
620
- // super(ndk, signerPubkey, localSigner);
562
+ constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
563
+ super(ndk, signerPubkey, localSigner);
621
564
 
622
565
  // override with our own rpc implementation
623
- this._rpc = new IframeNostrRpc(rxNostr, localSigner, iframeOrigin);
566
+ this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
624
567
  this._rpc.setUseNip44(true); // !!this.params.optionsModal.dev);
625
568
  this._rpc.on('authUrl', (url: string) => {
626
569
  this.emit('authUrl', url);
@@ -629,11 +572,11 @@ export class Nip46Signer extends EventEmitter {
629
572
  this.rpc = this._rpc;
630
573
  }
631
574
 
632
- get userPubkey(): string {
575
+ get userPubkey() {
633
576
  return this._userPubkey;
634
577
  }
635
578
 
636
- private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false): Promise<void> {
579
+ private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
637
580
  console.log('setSignerPubkey', signerPubkey);
638
581
 
639
582
  // ensure it's set
@@ -648,7 +591,7 @@ export class Nip46Signer extends EventEmitter {
648
591
  await this.initUserPubkey(sameAsUser ? signerPubkey : '');
649
592
  }
650
593
 
651
- public async initUserPubkey(hintPubkey?: string): Promise<void> {
594
+ public async initUserPubkey(hintPubkey?: string) {
652
595
  if (this._userPubkey) throw new Error('Already called initUserPubkey');
653
596
 
654
597
  if (hintPubkey) {
@@ -665,7 +608,7 @@ export class Nip46Signer extends EventEmitter {
665
608
  if (response.error) {
666
609
  err(new Error(response.error));
667
610
  } else {
668
- ok(response.result || '');
611
+ ok(response.result);
669
612
  }
670
613
  });
671
614
  }),
@@ -674,23 +617,23 @@ export class Nip46Signer extends EventEmitter {
674
617
  );
675
618
  }
676
619
 
677
- public async listen(nostrConnectSecret: string): Promise<void> {
620
+ public async listen(nostrConnectSecret: string) {
678
621
  const signerPubkey = await (this.rpc as IframeNostrRpc).listen(nostrConnectSecret);
679
622
  await this.setSignerPubkey(signerPubkey);
680
623
  }
681
624
 
682
- public async connect(token?: string, perms?: string): Promise<void> {
625
+ public async connect(token?: string, perms?: string) {
683
626
  if (!this.remotePubkey) throw new Error('No signer pubkey');
684
627
  await (this._rpc as any).connectWithTimeout(this.remotePubkey, token, perms, NIP46_CONNECT_TIMEOUT);
685
628
  await this.setSignerPubkey(this.remotePubkey);
686
629
  }
687
630
 
688
- public async setListenReply(reply: any, nostrConnectSecret: string): Promise<void> {
631
+ public async setListenReply(reply: any, nostrConnectSecret: string) {
689
632
  const signerPubkey = await this._rpc.parseNostrConnectReply(reply, nostrConnectSecret);
690
633
  await this.setSignerPubkey(signerPubkey, true);
691
634
  }
692
635
 
693
- public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }): Promise<string> {
636
+ public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
694
637
  const params = [
695
638
  name,
696
639
  domain,
@@ -703,52 +646,10 @@ export class Nip46Signer extends EventEmitter {
703
646
  });
704
647
 
705
648
  console.log('create_account pubkey', r);
706
- if (r.result === 'error' || !r.result) {
707
- throw new Error(r.error || 'Unknown error');
649
+ if (r.result === 'error') {
650
+ throw new Error(r.error);
708
651
  }
709
652
 
710
653
  return r.result;
711
654
  }
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
- }
754
655
  }