@konemono/nostr-login 1.7.29 → 1.7.31

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.7.29",
3
+ "version": "1.7.31",
4
4
  "description": "",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",
@@ -572,7 +572,8 @@ class AuthNostrService extends EventEmitter implements Signer {
572
572
  }
573
573
  }
574
574
 
575
- public async signEvent(event: any) {
575
+ public async signEvent(ev: any) {
576
+ const event = { ...ev };
576
577
  if (this.localSigner) {
577
578
  event.pubkey = getPublicKey(this.localSigner.privateKey!);
578
579
  event.id = getEventHash(event);
@@ -1,173 +1,579 @@
1
- // Nip46.ts
2
- import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NostrEvent } from '@nostr-dev-kit/ndk';
1
+ import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
3
2
  import { validateEvent, verifySignature } from 'nostr-tools';
4
3
  import { PrivateKeySigner } from './Signer';
5
4
  import { NIP46_TIMEOUT } from '../const';
6
5
  import ProcessManager from './ProcessManager';
7
6
 
8
- /* =========================
9
- * NostrRpc
10
- * ========================= */
11
7
  class NostrRpc extends NDKNostrRpc {
12
- protected processManager: ProcessManager;
8
+ protected processManager?: ProcessManager;
13
9
  protected _ndk: NDK;
14
10
  protected _signer: PrivateKeySigner;
15
11
  protected requests: Set<string> = new Set();
16
12
  protected requestTimeouts: Map<string, NodeJS.Timeout> = new Map();
17
13
  private sub?: NDKSubscription;
18
- protected _useNip44 = false;
14
+ protected _useNip44: boolean = false;
19
15
 
20
- constructor(ndk: NDK, signer: PrivateKeySigner, processManager: ProcessManager) {
16
+ public constructor(ndk: NDK, signer: PrivateKeySigner, processManager?: ProcessManager) {
21
17
  super(ndk, signer, ndk.debug.extend('nip46:signer:rpc'));
22
18
  this._ndk = ndk;
23
19
  this._signer = signer;
24
20
  this.processManager = processManager;
25
21
  }
26
22
 
27
- async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
23
+ public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
28
24
  filter.kinds = filter.kinds?.filter(k => k === 24133);
29
25
  this.sub = await super.subscribe(filter);
30
26
  return this.sub;
31
27
  }
32
28
 
33
- stop() {
34
- for (const t of this.requestTimeouts.values()) clearTimeout(t);
35
- this.requestTimeouts.clear();
29
+ public stop() {
30
+ this.clearAllTimeouts();
36
31
  if (this.sub) {
37
32
  this.sub.stop();
38
33
  this.sub = undefined;
39
34
  }
40
35
  }
41
36
 
37
+ private clearAllTimeouts() {
38
+ for (const timeout of this.requestTimeouts.values()) {
39
+ clearTimeout(timeout);
40
+ }
41
+ this.requestTimeouts.clear();
42
+ }
43
+
42
44
  protected clearTimeout(id: string) {
43
- const t = this.requestTimeouts.get(id);
44
- if (t) clearTimeout(t);
45
- this.requestTimeouts.delete(id);
45
+ const timeout = this.requestTimeouts.get(id);
46
+ if (timeout) {
47
+ clearTimeout(timeout);
48
+ this.requestTimeouts.delete(id);
49
+ }
46
50
  }
47
51
 
48
- setUseNip44(v: boolean) {
49
- this._useNip44 = v;
52
+ public setUseNip44(useNip44: boolean) {
53
+ this._useNip44 = useNip44;
50
54
  }
51
55
 
52
- async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
56
+ private isNip04(ciphertext: string) {
57
+ const l = ciphertext.length;
58
+ if (l < 28) return false;
59
+ return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
60
+ }
61
+
62
+ public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
53
63
  const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
54
64
  remoteUser.ndk = this._ndk;
65
+ const decrypt = this.isNip04(event.content) ? this._signer.decrypt : this._signer.decryptNip44;
66
+ const decryptedContent = await decrypt.call(this._signer, remoteUser, event.content);
67
+ const parsedContent = JSON.parse(decryptedContent);
68
+ const { id, method, params, result, error } = parsedContent;
69
+
70
+ if (method) {
71
+ return { id, pubkey: event.pubkey, method, params, event };
72
+ } else {
73
+ return { id, result, error, event };
74
+ }
75
+ }
55
76
 
56
- const decrypted = await this._signer.decrypt.call(this._signer, remoteUser, event.content);
77
+ public async parseNostrConnectReply(reply: any, secret: string) {
78
+ const event = new NDKEvent(this._ndk, reply);
79
+ const parsedEvent = await this.parseEvent(event);
80
+ console.log('nostr connect parsedEvent', parsedEvent);
81
+ if (!(parsedEvent as NDKRpcRequest).method) {
82
+ const response = parsedEvent as NDKRpcResponse;
83
+ if (response.result !== secret) throw new Error(response.error);
84
+ return event.pubkey;
85
+ } else {
86
+ throw new Error('Bad nostr connect reply');
87
+ }
88
+ }
57
89
 
58
- const { id, method, params, result, error } = JSON.parse(decrypted);
90
+ public async listen(nostrConnectSecret: string): Promise<string> {
91
+ const pubkey = this._signer.pubkey;
92
+ console.log('nostr-login listening for conn to', pubkey);
93
+ const sub = await this.subscribe({
94
+ 'kinds': [24133],
95
+ '#p': [pubkey],
96
+ });
97
+ return new Promise<string>((ok, err) => {
98
+ const timeoutId = setTimeout(() => {
99
+ this.stop();
100
+ err(new Error('NIP46 listen timeout'));
101
+ }, NIP46_TIMEOUT);
102
+
103
+ sub.on('event', async (event: NDKEvent) => {
104
+ try {
105
+ const parsedEvent = await this.parseEvent(event);
106
+ if (!(parsedEvent as NDKRpcRequest).method) {
107
+ const response = parsedEvent as NDKRpcResponse;
108
+
109
+ if (response.result === 'auth_url') return;
110
+
111
+ if (response.result === 'ack' || response.result === nostrConnectSecret) {
112
+ clearTimeout(timeoutId);
113
+ ok(event.pubkey);
114
+ } else {
115
+ clearTimeout(timeoutId);
116
+ err(response.error);
117
+ }
118
+ }
119
+ } catch (e) {
120
+ console.log('error parsing event', e, event.rawEvent());
121
+ }
122
+ this.stop();
123
+ });
124
+ });
125
+ }
59
126
 
60
- return method ? { id, pubkey: event.pubkey, method, params, event } : { id, result, error, event };
127
+ public async connect(pubkey: string, token?: string, perms?: string) {
128
+ return new Promise<void>((ok, err) => {
129
+ const connectParams = [pubkey!, token || '', perms || ''];
130
+
131
+ const timeoutId = setTimeout(() => {
132
+ err(new Error('NIP46 connect timeout'));
133
+ }, NIP46_TIMEOUT);
134
+
135
+ this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
136
+ clearTimeout(timeoutId);
137
+ if (response.result === 'ack') {
138
+ ok();
139
+ } else {
140
+ err(response.error);
141
+ }
142
+ });
143
+ });
61
144
  }
62
145
 
63
- protected getId() {
64
- return Math.random().toString(36).slice(2);
146
+ protected getId(): string {
147
+ return Math.random().toString(36).substring(7);
65
148
  }
66
149
 
67
- protected async ensureConnected() {
150
+ protected async ensureConnected(): Promise<void> {
68
151
  const relays = Array.from(this._ndk.pool.relays.values());
69
- if (relays.some(r => r.status === 1)) return;
152
+ console.log(
153
+ 'Checking relay connections:',
154
+ relays.map(r => ({ url: r.url, status: r.status })),
155
+ );
156
+
157
+ const connectedRelays = relays.filter(r => r.status === 1);
158
+
159
+ if (connectedRelays.length === 0) {
160
+ console.log('No connected relays, forcing reconnection...');
161
+
162
+ // 既存の接続を全てクリーンアップ
163
+ for (const relay of relays) {
164
+ try {
165
+ await relay.disconnect();
166
+ } catch (e) {
167
+ console.log('Error disconnecting relay:', relay.url, e);
168
+ }
169
+ }
70
170
 
71
- for (const r of relays) {
72
- try {
73
- await r.disconnect();
74
- } catch {}
171
+ // 再接続
172
+ await this._ndk.connect();
173
+
174
+ // 接続確立を待つ
175
+ await new Promise<void>((resolve, reject) => {
176
+ const timeout = setTimeout(() => {
177
+ const status = Array.from(this._ndk.pool.relays.values()).map(r => ({ url: r.url, status: r.status }));
178
+ console.error('Failed to reconnect to relays within 10s. Status:', status);
179
+ reject(new Error('Failed to reconnect to relays'));
180
+ }, 10000);
181
+
182
+ const checkConnection = () => {
183
+ const connected = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1);
184
+ if (connected.length > 0) {
185
+ clearTimeout(timeout);
186
+ console.log(
187
+ 'Successfully reconnected to',
188
+ connected.length,
189
+ 'relays:',
190
+ connected.map(r => r.url),
191
+ );
192
+ resolve();
193
+ } else {
194
+ setTimeout(checkConnection, 200);
195
+ }
196
+ };
197
+ checkConnection();
198
+ });
199
+ } else {
200
+ console.log(
201
+ 'Already connected to',
202
+ connectedRelays.length,
203
+ 'relays:',
204
+ connectedRelays.map(r => r.url),
205
+ );
75
206
  }
76
-
77
- await this._ndk.connect();
78
207
  }
79
208
 
80
- async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (r: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
81
- this.processManager.pause();
209
+ public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
210
+ console.log('sendRequest called:', method, 'to', remotePubkey);
211
+
82
212
  try {
213
+ this.processManager?.pause();
83
214
  await this.ensureConnected();
215
+ } catch (e) {
216
+ console.error('Failed to ensure connection:', e);
217
+ if (cb) {
218
+ cb({
219
+ id: '',
220
+ result: '',
221
+ error: 'Failed to connect to relays: ' + (e as Error).message,
222
+ event: undefined as any,
223
+ });
224
+ }
225
+ throw e;
84
226
  } finally {
85
- this.processManager.resume();
227
+ this.processManager?.resume();
86
228
  }
87
229
 
88
230
  const id = this.getId();
231
+
89
232
  this.setResponseHandler(id, cb);
90
233
 
91
234
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
92
- await event.publish();
235
+ console.log('sendRequest event created', { event, method, remotePubkey, params });
236
+
237
+ try {
238
+ await event.publish();
239
+ console.log('sendRequest event published successfully');
240
+ } catch (e) {
241
+ console.error('Failed to publish event:', e);
242
+ this.clearTimeout(id);
243
+ this.requests.delete(id);
244
+ if (cb) {
245
+ cb({
246
+ id,
247
+ result: '',
248
+ error: 'Failed to publish event: ' + (e as Error).message,
249
+ event: undefined as any,
250
+ });
251
+ }
252
+ throw e;
253
+ }
93
254
 
94
255
  // @ts-ignore
95
- return undefined;
256
+ return undefined as NDKRpcResponse;
96
257
  }
97
258
 
98
- protected setResponseHandler(id: string, cb?: (r: NDKRpcResponse) => void) {
99
- const to = setTimeout(() => {
100
- this.requests.delete(id);
101
- cb?.({ id, result: '', error: 'timeout', event: undefined as any });
259
+ protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
260
+ let authUrlSent = false;
261
+ const now = Date.now();
262
+
263
+ const timeoutId = setTimeout(() => {
264
+ if (this.requests.has(id)) {
265
+ this.requests.delete(id);
266
+ this.requestTimeouts.delete(id);
267
+ console.log('NIP46 request timeout for', id);
268
+ if (cb) {
269
+ cb({
270
+ id,
271
+ result: '',
272
+ error: 'NIP46 request timeout',
273
+ event: undefined as any,
274
+ });
275
+ }
276
+ }
102
277
  }, NIP46_TIMEOUT);
103
278
 
104
- this.requestTimeouts.set(id, to);
105
-
106
- this.once(`response-${id}`, r => {
107
- this.clearTimeout(id);
108
- this.requests.delete(id);
109
- cb?.(r);
279
+ this.requestTimeouts.set(id, timeoutId);
280
+
281
+ return new Promise<NDKRpcResponse>(() => {
282
+ const responseHandler = (response: NDKRpcResponse) => {
283
+ if (response.result === 'auth_url') {
284
+ this.once(`response-${id}`, responseHandler);
285
+ if (!authUrlSent) {
286
+ authUrlSent = true;
287
+ this.emit('authUrl', response.error);
288
+ }
289
+ } else if (cb) {
290
+ if (this.requests.has(id)) {
291
+ this.clearTimeout(id);
292
+ this.requests.delete(id);
293
+ console.log('nostr-login processed nip46 request in', Date.now() - now, 'ms');
294
+ cb(response);
295
+ }
296
+ }
297
+ };
298
+
299
+ this.once(`response-${id}`, responseHandler);
110
300
  });
111
301
  }
112
302
 
113
- protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[], kind: number) {
303
+ protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
114
304
  this.requests.add(id);
115
305
  const localUser = await this._signer.user();
116
306
  const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
307
+ const request = { id, method, params };
117
308
 
118
309
  const event = new NDKEvent(this._ndk, {
119
310
  kind,
120
- content: JSON.stringify({ id, method, params }),
311
+ content: JSON.stringify(request),
121
312
  tags: [['p', remotePubkey]],
122
313
  pubkey: localUser.pubkey,
123
314
  } as NostrEvent);
124
315
 
125
- event.content = await this._signer.encrypt.call(this._signer, remoteUser, event.content);
126
-
316
+ const useNip44 = this._useNip44 && method !== 'create_account';
317
+ const encrypt = useNip44 ? this._signer.encryptNip44 : this._signer.encrypt;
318
+ event.content = await encrypt.call(this._signer, remoteUser, event.content);
127
319
  await event.sign(this._signer);
320
+
128
321
  return event;
129
322
  }
130
323
  }
131
324
 
132
- /* =========================
133
- * IframeNostrRpc
134
- * ========================= */
135
325
  export class IframeNostrRpc extends NostrRpc {
326
+ private peerOrigin?: string;
136
327
  private iframePort?: MessagePort;
328
+ private iframeRequests = new Map<string, { id: string; pubkey: string }>();
329
+
330
+ public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
331
+ super(ndk, localSigner);
332
+ this._ndk = ndk;
333
+ this.peerOrigin = iframePeerOrigin;
334
+ }
137
335
 
138
- constructor(ndk: NDK, signer: PrivateKeySigner, processManager: ProcessManager) {
139
- super(ndk, signer, processManager);
336
+ public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
337
+ if (!this.peerOrigin) return super.subscribe(filter);
338
+ return new NDKSubscription(
339
+ this._ndk,
340
+ {},
341
+ {
342
+ closeOnEose: true,
343
+ cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
344
+ },
345
+ );
140
346
  }
141
347
 
142
- setWorkerIframePort(port: MessagePort) {
348
+ public setWorkerIframePort(port: MessagePort) {
349
+ if (!this.peerOrigin) throw new Error('Unexpected iframe port');
350
+
143
351
  this.iframePort = port;
144
352
 
353
+ setInterval(() => {
354
+ console.log('iframe-nip46 ping');
355
+ this.iframePort!.postMessage('ping');
356
+ }, 5000);
357
+
145
358
  port.onmessage = async ev => {
146
- const event = ev.data;
147
- if (!validateEvent(event) || !verifySignature(event)) return;
148
- const parsed = await this.parseEvent(new NDKEvent(this._ndk, event));
149
- if (!('method' in parsed)) {
150
- this.emit(`response-${parsed.id}`, parsed);
359
+ console.log('iframe-nip46 got response', ev.data);
360
+ if (typeof ev.data === 'string' && ev.data.startsWith('errorNoKey')) {
361
+ const event_id = ev.data.split(':')[1];
362
+ const { id = '', pubkey = '' } = this.iframeRequests.get(event_id) || {};
363
+ if (id && pubkey && this.requests.has(id)) this.emit(`iframeRestart-${pubkey}`);
364
+ return;
365
+ }
366
+
367
+ try {
368
+ const event = ev.data;
369
+
370
+ if (!validateEvent(event)) throw new Error('Invalid event from iframe');
371
+ if (!verifySignature(event)) throw new Error('Invalid event signature from iframe');
372
+ const nevent = new NDKEvent(this._ndk, event);
373
+ const parsedEvent = await this.parseEvent(nevent);
374
+ if (!(parsedEvent as NDKRpcRequest).method) {
375
+ console.log('parsed response', parsedEvent);
376
+ this.emit(`response-${parsedEvent.id}`, parsedEvent);
377
+ }
378
+ } catch (e) {
379
+ console.log('error parsing event', e, ev.data);
151
380
  }
152
381
  };
153
382
  }
383
+
384
+ public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
385
+ console.log('IframeNostrRpc.sendRequest called:', method, 'iframePort:', !!this.iframePort);
386
+
387
+ if (!this.iframePort) {
388
+ try {
389
+ this.processManager?.pause();
390
+ await this.ensureConnected();
391
+ } catch (e) {
392
+ console.error('Failed to ensure connection:', e);
393
+ if (cb) {
394
+ cb({
395
+ id: '',
396
+ result: '',
397
+ error: 'Failed to connect to relays: ' + (e as Error).message,
398
+ event: undefined as any,
399
+ });
400
+ }
401
+ throw e;
402
+ } finally {
403
+ this.processManager?.resume();
404
+ }
405
+ }
406
+
407
+ const id = this.getId();
408
+
409
+ const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
410
+
411
+ this.setResponseHandler(id, cb);
412
+
413
+ if (this.iframePort) {
414
+ this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
415
+
416
+ console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
417
+ this.iframePort.postMessage(event.rawEvent());
418
+ } else {
419
+ try {
420
+ await event.publish();
421
+ console.log('Request published to relays successfully');
422
+ } catch (e) {
423
+ console.error('Failed to publish event:', e);
424
+ this.clearTimeout(id);
425
+ this.requests.delete(id);
426
+ if (cb) {
427
+ cb({
428
+ id,
429
+ result: '',
430
+ error: 'Failed to publish event: ' + (e as Error).message,
431
+ event: undefined as any,
432
+ });
433
+ }
434
+ throw e;
435
+ }
436
+ }
437
+
438
+ // @ts-ignore
439
+ return undefined as NDKRpcResponse;
440
+ }
441
+ }
442
+
443
+ export class ReadyListener {
444
+ origin: string;
445
+ messages: string[];
446
+ promise: Promise<any>;
447
+
448
+ constructor(messages: string[], origin: string) {
449
+ this.origin = origin;
450
+ this.messages = messages;
451
+ this.promise = new Promise<any>(ok => {
452
+ console.log(new Date(), 'started listener for', this.messages);
453
+
454
+ const onReady = async (e: MessageEvent) => {
455
+ const originHostname = new URL(origin!).hostname;
456
+ const messageHostname = new URL(e.origin).hostname;
457
+ const validHost = messageHostname === originHostname || messageHostname.endsWith('.' + originHostname);
458
+ if (!validHost || !Array.isArray(e.data) || !e.data.length || !this.messages.includes(e.data[0])) {
459
+ return;
460
+ }
461
+
462
+ console.log(new Date(), 'got ready message from', e.origin, e.data);
463
+ window.removeEventListener('message', onReady);
464
+ ok(e.data);
465
+ };
466
+ window.addEventListener('message', onReady);
467
+ });
468
+ }
469
+
470
+ async wait(): Promise<any> {
471
+ console.log(new Date(), 'waiting for', this.messages);
472
+ const r = await this.promise;
473
+ console.log(new Date(), 'finished waiting for', this.messages, r);
474
+ return r;
475
+ }
154
476
  }
155
477
 
156
- /* =========================
157
- * Nip46Signer
158
- * ========================= */
159
478
  export class Nip46Signer extends NDKNip46Signer {
479
+ private _userPubkey: string = '';
160
480
  private _rpc: IframeNostrRpc;
161
481
 
162
- constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string) {
163
- const pm = new ProcessManager();
482
+ constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
164
483
  super(ndk, signerPubkey, localSigner);
165
484
 
166
- this._rpc = new IframeNostrRpc(ndk, localSigner, pm);
485
+ this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
486
+ this._rpc.setUseNip44(true);
487
+ this._rpc.on('authUrl', (url: string) => {
488
+ this.emit('authUrl', url);
489
+ });
490
+
167
491
  this.rpc = this._rpc;
492
+ }
168
493
 
169
- this._rpc.on('authUrl', url => {
170
- this.emit('authUrl', url);
494
+ get userPubkey() {
495
+ return this._userPubkey;
496
+ }
497
+
498
+ private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
499
+ console.log('setSignerPubkey', signerPubkey);
500
+
501
+ this.remotePubkey = signerPubkey;
502
+
503
+ this._rpc.on(`iframeRestart-${signerPubkey}`, () => {
504
+ this.emit('iframeRestart');
505
+ });
506
+
507
+ await this.initUserPubkey(sameAsUser ? signerPubkey : '');
508
+ }
509
+
510
+ public async initUserPubkey(hintPubkey?: string) {
511
+ if (this._userPubkey) throw new Error('Already called initUserPubkey');
512
+
513
+ if (hintPubkey) {
514
+ this._userPubkey = hintPubkey;
515
+ return;
516
+ }
517
+
518
+ this._userPubkey = await new Promise<string>((ok, err) => {
519
+ if (!this.remotePubkey) throw new Error('Signer pubkey not set');
520
+
521
+ console.log('get_public_key', this.remotePubkey);
522
+
523
+ const timeoutId = setTimeout(() => {
524
+ err(new Error('NIP46 get_public_key timeout'));
525
+ }, NIP46_TIMEOUT);
526
+
527
+ this._rpc.sendRequest(this.remotePubkey, 'get_public_key', [], 24133, (response: NDKRpcResponse) => {
528
+ clearTimeout(timeoutId);
529
+ if (response.error) {
530
+ err(new Error(response.error));
531
+ } else {
532
+ ok(response.result);
533
+ }
534
+ });
171
535
  });
172
536
  }
537
+
538
+ public async listen(nostrConnectSecret: string) {
539
+ const signerPubkey = await (this.rpc as IframeNostrRpc).listen(nostrConnectSecret);
540
+ await this.setSignerPubkey(signerPubkey);
541
+ }
542
+
543
+ public async connect(token?: string, perms?: string) {
544
+ if (!this.remotePubkey) throw new Error('No signer pubkey');
545
+ await this._rpc.connect(this.remotePubkey, token, perms);
546
+ await this.setSignerPubkey(this.remotePubkey);
547
+ }
548
+
549
+ public async setListenReply(reply: any, nostrConnectSecret: string) {
550
+ const signerPubkey = await this._rpc.parseNostrConnectReply(reply, nostrConnectSecret);
551
+ await this.setSignerPubkey(signerPubkey, true);
552
+ }
553
+
554
+ public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
555
+ const params = [name, domain, '', perms];
556
+
557
+ const r = await new Promise<NDKRpcResponse>((ok, err) => {
558
+ const timeoutId = setTimeout(() => {
559
+ err(new Error('NIP46 create_account timeout'));
560
+ }, NIP46_TIMEOUT);
561
+
562
+ this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, response => {
563
+ clearTimeout(timeoutId);
564
+ if (response.error) {
565
+ err(new Error(response.error));
566
+ } else {
567
+ ok(response);
568
+ }
569
+ });
570
+ });
571
+
572
+ console.log('create_account pubkey', r);
573
+ if (r.result === 'error') {
574
+ throw new Error(r.error);
575
+ }
576
+
577
+ return r.result;
578
+ }
173
579
  }
@@ -2,80 +2,94 @@ import { EventEmitter } from 'tseep';
2
2
  import { CALL_TIMEOUT } from '../const';
3
3
 
4
4
  class ProcessManager extends EventEmitter {
5
- private callCount = 0;
6
- private callTimer?: NodeJS.Timeout;
7
5
  private paused = false;
6
+ private callCount: number = 0;
7
+ private callTimer: NodeJS.Timeout | undefined;
8
+
9
+ constructor() {
10
+ super();
11
+ }
8
12
 
9
- /* 呼び出し元互換 */
10
13
  public onAuthUrl() {
14
+ console.log('ProcessManager.onAuthUrl called, resetting timer');
11
15
  this.resetTimer();
12
16
  }
13
17
 
14
- /* 呼び出し元互換 */
15
18
  public onIframeUrl() {
19
+ console.log('ProcessManager.onIframeUrl called, resetting timer');
16
20
  this.resetTimer();
17
21
  }
18
22
 
19
23
  private resetTimer() {
20
- if (this.paused) return;
21
-
22
- if (this.callTimer) clearTimeout(this.callTimer);
23
-
24
+ if (this.callTimer) {
25
+ clearTimeout(this.callTimer);
26
+ console.log('ProcessManager: timer reset');
27
+ }
24
28
  if (this.callCount > 0) {
25
29
  this.callTimer = setTimeout(() => {
30
+ console.log('ProcessManager: timeout reached, emitting onCallTimeout');
26
31
  this.emit('onCallTimeout');
27
32
  }, CALL_TIMEOUT);
33
+ console.log(`ProcessManager: new timer set for ${CALL_TIMEOUT} ms`);
28
34
  }
29
35
  }
30
36
 
31
- private startTimer() {
32
- if (this.paused) return;
33
- if (this.callTimer) return;
34
-
35
- this.callTimer = setTimeout(() => {
36
- this.emit('onCallTimeout');
37
- }, CALL_TIMEOUT);
38
- }
37
+ public async wait<T>(cb: () => Promise<T>): Promise<T> {
38
+ console.log('ProcessManager.wait called, callTimer exists:', !!this.callTimer, 'callCount:', this.callCount);
39
39
 
40
- private stopTimerIfIdle() {
41
- if (this.callCount === 0 && this.callTimer) {
42
- clearTimeout(this.callTimer);
43
- this.callTimer = undefined;
40
+ if (!this.callTimer) {
41
+ this.callTimer = setTimeout(() => {
42
+ console.log('ProcessManager: timeout reached, emitting onCallTimeout');
43
+ this.callTimer = undefined; // タイムアウト時にタイマーIDをクリア
44
+ this.emit('onCallTimeout');
45
+ }, CALL_TIMEOUT);
46
+ console.log(`Setting up timeout timer for ${CALL_TIMEOUT} ms`);
44
47
  }
45
- }
46
48
 
47
- public async wait<T>(cb: () => Promise<T>): Promise<T> {
48
- if (this.callCount === 0) {
49
+ if (!this.callCount) {
49
50
  this.emit('onCallStart');
50
51
  }
51
52
 
52
53
  this.callCount++;
53
54
 
55
+ let error;
56
+ let result;
57
+
54
58
  try {
55
- const p = Promise.resolve().then(cb);
56
- this.startTimer();
57
- return await p;
58
- } finally {
59
- this.callCount--;
60
- this.emit('onCallEnd');
61
- this.stopTimerIfIdle();
59
+ result = await cb();
60
+ } catch (e) {
61
+ error = e;
62
62
  }
63
- }
64
63
 
65
- /* 再接続などで timeout を止めるため */
66
- public pause() {
67
- this.paused = true;
64
+ this.callCount--;
65
+
66
+ this.emit('onCallEnd');
67
+
68
68
  if (this.callTimer) {
69
69
  clearTimeout(this.callTimer);
70
- this.callTimer = undefined;
71
70
  }
71
+
72
+ this.callTimer = undefined;
73
+
74
+ if (error) {
75
+ throw error;
76
+ }
77
+
78
+ // @ts-ignore
79
+ return result;
80
+ }
81
+ public pause() {
82
+ if (this.callTimer) clearTimeout(this.callTimer);
83
+ this.callTimer = undefined;
84
+ this.paused = true;
72
85
  }
73
86
 
74
- /* 再接続完了後に再開 */
75
87
  public resume() {
76
88
  this.paused = false;
77
89
  if (this.callCount > 0 && !this.callTimer) {
78
- this.startTimer();
90
+ this.callTimer = setTimeout(() => {
91
+ this.emit('onCallTimeout');
92
+ }, CALL_TIMEOUT);
79
93
  }
80
94
  }
81
95
  }