@konemono/nostr-login 1.7.36 → 1.7.37

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,59 +1,34 @@
1
1
  import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
2
2
  import { validateEvent, verifySignature } from 'nostr-tools';
3
3
  import { PrivateKeySigner } from './Signer';
4
- import { NIP46_TIMEOUT } from '../const';
5
- import ProcessManager from './ProcessManager';
6
4
 
7
5
  class NostrRpc extends NDKNostrRpc {
8
- protected processManager?: ProcessManager;
9
6
  protected _ndk: NDK;
10
7
  protected _signer: PrivateKeySigner;
11
8
  protected requests: Set<string> = new Set();
12
- protected requestTimeouts: Map<string, NodeJS.Timeout> = new Map();
13
9
  private sub?: NDKSubscription;
14
10
  protected _useNip44: boolean = false;
15
11
 
16
- public cleanupTimersAndRequests() {
17
- this.clearAllTimeouts(); // private/protected なメソッドを呼び出す
18
- this.requests.clear(); // requests Setをクリア
19
- }
20
-
21
- public constructor(ndk: NDK, signer: PrivateKeySigner, processManager?: ProcessManager) {
12
+ public constructor(ndk: NDK, signer: PrivateKeySigner) {
22
13
  super(ndk, signer, ndk.debug.extend('nip46:signer:rpc'));
23
14
  this._ndk = ndk;
24
15
  this._signer = signer;
25
- this.processManager = processManager;
26
16
  }
27
17
 
28
18
  public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
19
+ // NOTE: fixing ndk
29
20
  filter.kinds = filter.kinds?.filter(k => k === 24133);
30
21
  this.sub = await super.subscribe(filter);
31
22
  return this.sub;
32
23
  }
33
24
 
34
25
  public stop() {
35
- this.clearAllTimeouts();
36
26
  if (this.sub) {
37
27
  this.sub.stop();
38
28
  this.sub = undefined;
39
29
  }
40
30
  }
41
31
 
42
- protected clearAllTimeouts() {
43
- for (const timeout of this.requestTimeouts.values()) {
44
- clearTimeout(timeout);
45
- }
46
- this.requestTimeouts.clear();
47
- }
48
-
49
- protected clearTimeout(id: string) {
50
- const timeout = this.requestTimeouts.get(id);
51
- if (timeout) {
52
- clearTimeout(timeout);
53
- this.requestTimeouts.delete(id);
54
- }
55
- }
56
-
57
32
  public setUseNip44(useNip44: boolean) {
58
33
  this._useNip44 = useNip44;
59
34
  }
@@ -64,6 +39,7 @@ class NostrRpc extends NDKNostrRpc {
64
39
  return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
65
40
  }
66
41
 
42
+ // override to auto-decrypt nip04/nip44
67
43
  public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
68
44
  const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
69
45
  remoteUser.ndk = this._ndk;
@@ -92,6 +68,9 @@ class NostrRpc extends NDKNostrRpc {
92
68
  }
93
69
  }
94
70
 
71
+ // ndk doesn't support nostrconnect:
72
+ // we just listed to an unsolicited reply to
73
+ // our pubkey and if it's ack/secret - we're fine
95
74
  public async listen(nostrConnectSecret: string): Promise<string> {
96
75
  const pubkey = this._signer.pubkey;
97
76
  console.log('nostr-login listening for conn to', pubkey);
@@ -100,45 +79,40 @@ class NostrRpc extends NDKNostrRpc {
100
79
  '#p': [pubkey],
101
80
  });
102
81
  return new Promise<string>((ok, err) => {
103
- const timeoutId = setTimeout(() => {
104
- this.stop();
105
- err(new Error('NIP46 listen timeout'));
106
- }, NIP46_TIMEOUT);
107
-
108
82
  sub.on('event', async (event: NDKEvent) => {
109
83
  try {
110
84
  const parsedEvent = await this.parseEvent(event);
85
+ // console.log('ack parsedEvent', parsedEvent);
111
86
  if (!(parsedEvent as NDKRpcRequest).method) {
112
87
  const response = parsedEvent as NDKRpcResponse;
113
88
 
89
+ // ignore
114
90
  if (response.result === 'auth_url') return;
115
91
 
92
+ // FIXME for now accept 'ack' replies, later on only
93
+ // accept secrets
116
94
  if (response.result === 'ack' || response.result === nostrConnectSecret) {
117
- clearTimeout(timeoutId);
118
95
  ok(event.pubkey);
119
96
  } else {
120
- clearTimeout(timeoutId);
121
97
  err(response.error);
122
98
  }
123
99
  }
124
100
  } catch (e) {
125
101
  console.log('error parsing event', e, event.rawEvent());
126
102
  }
103
+ // done
127
104
  this.stop();
128
105
  });
129
106
  });
130
107
  }
131
108
 
109
+ // since ndk doesn't yet support perms param
110
+ // we reimplement the 'connect' call here
111
+ // instead of await signer.blockUntilReady();
132
112
  public async connect(pubkey: string, token?: string, perms?: string) {
133
113
  return new Promise<void>((ok, err) => {
134
114
  const connectParams = [pubkey!, token || '', perms || ''];
135
-
136
- const timeoutId = setTimeout(() => {
137
- err(new Error('NIP46 connect timeout'));
138
- }, NIP46_TIMEOUT);
139
-
140
115
  this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
141
- clearTimeout(timeoutId);
142
116
  if (response.result === 'ack') {
143
117
  ok();
144
118
  } else {
@@ -152,113 +126,24 @@ class NostrRpc extends NDKNostrRpc {
152
126
  return Math.random().toString(36).substring(7);
153
127
  }
154
128
 
155
- protected async ensureConnected(): Promise<void> {
156
- const relays = Array.from(this._ndk.pool.relays.values());
157
- console.log(
158
- 'Checking relay connections:',
159
- relays.map(r => ({ url: r.url, status: r.status })),
160
- );
161
-
162
- const connectedRelays = relays.filter(r => r.status === 1);
163
- console.log('connected relays', connectedRelays);
164
-
165
- if (connectedRelays.length === 0) {
166
- console.log('No connected relays, forcing reconnection...');
167
-
168
- // 既存の接続を全てクリーンアップ
169
- for (const relay of relays) {
170
- try {
171
- await relay.disconnect();
172
- } catch (e) {
173
- console.log('Error disconnecting relay:', relay.url, e);
174
- }
175
- }
176
-
177
- // 再接続
178
- await this._ndk.connect();
179
-
180
- // 接続確立を待つ
181
- await new Promise<void>((resolve, reject) => {
182
- const timeout = setTimeout(() => {
183
- const status = Array.from(this._ndk.pool.relays.values()).map(r => ({ url: r.url, status: r.status }));
184
- console.error('Failed to reconnect to relays within 10s. Status:', status);
185
- reject(new Error('Failed to reconnect to relays'));
186
- }, 10000);
187
-
188
- const checkConnection = () => {
189
- const connected = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1);
190
- if (connected.length > 0) {
191
- clearTimeout(timeout);
192
- console.log(
193
- 'Successfully reconnected to',
194
- connected.length,
195
- 'relays:',
196
- connected.map(r => r.url),
197
- );
198
- resolve();
199
- } else {
200
- setTimeout(checkConnection, 200);
201
- }
202
- };
203
- checkConnection();
204
- });
205
- } else {
206
- console.log(
207
- 'Already connected to',
208
- connectedRelays.length,
209
- 'relays:',
210
- connectedRelays.map(r => r.url),
211
- );
212
- }
213
- }
214
-
215
129
  public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
216
- console.log('sendRequest called:', method, 'to', remotePubkey);
217
-
218
- try {
219
- this.processManager?.pause();
220
- await this.ensureConnected();
221
- this.processManager?.resetTimer();
222
- } catch (e) {
223
- console.error('Failed to ensure connection:', e);
224
- if (cb) {
225
- cb({
226
- id: '',
227
- result: '',
228
- error: 'Failed to connect to relays: ' + (e as Error).message,
229
- event: undefined as any,
230
- });
231
- }
232
- throw e;
233
- } finally {
234
- this.processManager?.resume();
235
- }
236
-
237
130
  const id = this.getId();
238
131
 
132
+ // response handler will deduplicate auth urls and responses
239
133
  this.setResponseHandler(id, cb);
240
134
 
135
+ // create and sign request
241
136
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
242
- console.log('sendRequest event created', { event, method, remotePubkey, params });
137
+ console.log("sendRequest", { event, method, remotePubkey, params });
243
138
 
244
- try {
245
- await event.publish();
246
- console.log('sendRequest event published successfully');
247
- } catch (e) {
248
- console.error('Failed to publish event:', e);
249
- this.clearTimeout(id);
250
- this.requests.delete(id);
251
- if (cb) {
252
- cb({
253
- id,
254
- result: '',
255
- error: 'Failed to publish event: ' + (e as Error).message,
256
- event: undefined as any,
257
- });
258
- }
259
- throw e;
260
- }
139
+ // send to relays
140
+ await event.publish();
261
141
 
142
+ // NOTE: ndk returns a promise that never resolves and
143
+ // in fact REQUIRES cb to be provided (otherwise no way
144
+ // to consume the result), we've already stepped on the bug
145
+ // of waiting for this unresolvable result, so now we return
146
+ // undefined to make sure waiters fail, not hang.
262
147
  // @ts-ignore
263
148
  return undefined as NDKRpcResponse;
264
149
  }
@@ -266,26 +151,6 @@ class NostrRpc extends NDKNostrRpc {
266
151
  protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
267
152
  let authUrlSent = false;
268
153
  const now = Date.now();
269
-
270
- const timeoutId = setTimeout(() => {
271
- if (this.requests.has(id)) {
272
- clearTimeout(timeoutId);
273
- this.requests.delete(id);
274
- this.requestTimeouts.delete(id);
275
- console.log('NIP46 request timeout for', id);
276
- if (cb) {
277
- cb({
278
- id,
279
- result: '',
280
- error: 'NIP46 request timeout',
281
- event: undefined as any,
282
- });
283
- }
284
- }
285
- }, NIP46_TIMEOUT);
286
-
287
- this.requestTimeouts.set(id, timeoutId);
288
-
289
154
  return new Promise<NDKRpcResponse>(() => {
290
155
  const responseHandler = (response: NDKRpcResponse) => {
291
156
  if (response.result === 'auth_url') {
@@ -296,7 +161,6 @@ class NostrRpc extends NDKNostrRpc {
296
161
  }
297
162
  } else if (cb) {
298
163
  if (this.requests.has(id)) {
299
- this.clearTimeout(id);
300
164
  this.requests.delete(id);
301
165
  console.log('nostr-login processed nip46 request in', Date.now() - now, 'ms');
302
166
  cb(response);
@@ -311,14 +175,6 @@ class NostrRpc extends NDKNostrRpc {
311
175
  protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
312
176
  this.requests.add(id);
313
177
  const localUser = await this._signer.user();
314
-
315
- if (!localUser.pubkey) {
316
- throw new Error('CORRUPTION: Missing local pubkey. Signer state compromised.');
317
- }
318
- if (!remotePubkey) {
319
- throw new Error('CORRUPTION: Missing remote pubkey. Signer state compromised.');
320
- }
321
-
322
178
  const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
323
179
  const request = { id, method, params };
324
180
 
@@ -355,6 +211,7 @@ export class IframeNostrRpc extends NostrRpc {
355
211
  this._ndk,
356
212
  {},
357
213
  {
214
+ // don't send to relay
358
215
  closeOnEose: true,
359
216
  cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
360
217
  },
@@ -366,6 +223,7 @@ export class IframeNostrRpc extends NostrRpc {
366
223
 
367
224
  this.iframePort = port;
368
225
 
226
+ // to make sure Chrome doesn't terminate the channel
369
227
  setInterval(() => {
370
228
  console.log('iframe-nip46 ping');
371
229
  this.iframePort!.postMessage('ping');
@@ -380,6 +238,7 @@ export class IframeNostrRpc extends NostrRpc {
380
238
  return;
381
239
  }
382
240
 
241
+ // a copy-paste from rpc.subscribe
383
242
  try {
384
243
  const event = ev.data;
385
244
 
@@ -387,6 +246,7 @@ export class IframeNostrRpc extends NostrRpc {
387
246
  if (!verifySignature(event)) throw new Error('Invalid event signature from iframe');
388
247
  const nevent = new NDKEvent(this._ndk, event);
389
248
  const parsedEvent = await this.parseEvent(nevent);
249
+ // we're only implementing client-side rpc
390
250
  if (!(parsedEvent as NDKRpcRequest).method) {
391
251
  console.log('parsed response', parsedEvent);
392
252
  this.emit(`response-${parsedEvent.id}`, parsedEvent);
@@ -398,60 +258,31 @@ export class IframeNostrRpc extends NostrRpc {
398
258
  }
399
259
 
400
260
  public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
401
- console.log('IframeNostrRpc.sendRequest called:', method, 'iframePort:', !!this.iframePort);
402
-
403
- if (!this.iframePort) {
404
- try {
405
- console.log(this.processManager);
406
- this.processManager?.pause();
407
- await this.ensureConnected();
408
- } catch (e) {
409
- console.error('Failed to ensure connection:', e);
410
- if (cb) {
411
- cb({
412
- id: '',
413
- result: '',
414
- error: 'Failed to connect to relays: ' + (e as Error).message,
415
- event: undefined as any,
416
- });
417
- }
418
- throw e;
419
- } finally {
420
- this.processManager?.resume();
421
- }
422
- }
423
-
424
261
  const id = this.getId();
425
262
 
263
+ // create and sign request event
426
264
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
427
265
 
266
+ // set response handler, it will dedup auth urls,
267
+ // and also dedup response handlers - we're sending
268
+ // to relays and to iframe
428
269
  this.setResponseHandler(id, cb);
429
270
 
430
271
  if (this.iframePort) {
272
+ // map request event id to request id, if iframe
273
+ // has no key it will reply with error:event_id (it can't
274
+ // decrypt the request id without keys)
431
275
  this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
432
276
 
277
+ // send to iframe
433
278
  console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
434
279
  this.iframePort.postMessage(event.rawEvent());
435
280
  } else {
436
- try {
437
- await event.publish();
438
- console.log('Request published to relays successfully');
439
- } catch (e) {
440
- console.error('Failed to publish event:', e);
441
- this.clearTimeout(id);
442
- this.requests.delete(id);
443
- if (cb) {
444
- cb({
445
- id,
446
- result: '',
447
- error: 'Failed to publish event: ' + (e as Error).message,
448
- event: undefined as any,
449
- });
450
- }
451
- throw e;
452
- }
281
+ // send to relays
282
+ await event.publish();
453
283
  }
454
284
 
285
+ // see notes in 'super'
455
286
  // @ts-ignore
456
287
  return undefined as NDKRpcResponse;
457
288
  }
@@ -468,11 +299,14 @@ export class ReadyListener {
468
299
  this.promise = new Promise<any>(ok => {
469
300
  console.log(new Date(), 'started listener for', this.messages);
470
301
 
302
+ // ready message handler
471
303
  const onReady = async (e: MessageEvent) => {
472
304
  const originHostname = new URL(origin!).hostname;
473
305
  const messageHostname = new URL(e.origin).hostname;
306
+ // same host or subdomain
474
307
  const validHost = messageHostname === originHostname || messageHostname.endsWith('.' + originHostname);
475
308
  if (!validHost || !Array.isArray(e.data) || !e.data.length || !this.messages.includes(e.data[0])) {
309
+ // console.log(new Date(), 'got invalid ready message', e.origin, e.data);
476
310
  return;
477
311
  }
478
312
 
@@ -487,6 +321,17 @@ export class ReadyListener {
487
321
  async wait(): Promise<any> {
488
322
  console.log(new Date(), 'waiting for', this.messages);
489
323
  const r = await this.promise;
324
+ // NOTE: timer here doesn't help bcs it must be activated when
325
+ // user "confirms", but that's happening on a different
326
+ // origin and we can't really know.
327
+ // await new Promise<any>((ok, err) => {
328
+ // // 10 sec should be more than enough
329
+ // setTimeout(() => err(new Date() + ' timeout for ' + this.message), 10000);
330
+
331
+ // // if promise already resolved or will resolve in the future
332
+ // this.promise.then(ok);
333
+ // });
334
+
490
335
  console.log(new Date(), 'finished waiting for', this.messages, r);
491
336
  return r;
492
337
  }
@@ -499,8 +344,9 @@ export class Nip46Signer extends NDKNip46Signer {
499
344
  constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
500
345
  super(ndk, signerPubkey, localSigner);
501
346
 
347
+ // override with our own rpc implementation
502
348
  this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
503
- this._rpc.setUseNip44(true);
349
+ this._rpc.setUseNip44(true); // !!this.params.optionsModal.dev);
504
350
  this._rpc.on('authUrl', (url: string) => {
505
351
  this.emit('authUrl', url);
506
352
  });
@@ -513,29 +359,20 @@ export class Nip46Signer extends NDKNip46Signer {
513
359
  }
514
360
 
515
361
  private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
516
- console.log('setSignerPubkey', signerPubkey);
362
+ console.log("setSignerPubkey", signerPubkey);
517
363
 
364
+ // ensure it's set
518
365
  this.remotePubkey = signerPubkey;
519
366
 
367
+ // when we're sure it's known
520
368
  this._rpc.on(`iframeRestart-${signerPubkey}`, () => {
521
369
  this.emit('iframeRestart');
522
370
  });
523
371
 
372
+ // now call getPublicKey and swap remotePubkey w/ that
524
373
  await this.initUserPubkey(sameAsUser ? signerPubkey : '');
525
374
  }
526
375
 
527
- public forceResetState() {
528
- console.log('Nip46Signer state reset due to connection failure or timeout.');
529
-
530
- // 1. ユーザー公開鍵をクリア
531
- this._userPubkey = '';
532
-
533
- // 2. リモート公開鍵をクリア
534
- this.remotePubkey = ''; // 親クラスのプロパティをクリア
535
-
536
- // 3. RPC側のタイマーとリクエストを、公開メソッド経由でクリア
537
- (this._rpc as NostrRpc).cleanupTimersAndRequests();
538
- }
539
376
  public async initUserPubkey(hintPubkey?: string) {
540
377
  if (this._userPubkey) throw new Error('Already called initUserPubkey');
541
378
 
@@ -547,19 +384,9 @@ export class Nip46Signer extends NDKNip46Signer {
547
384
  this._userPubkey = await new Promise<string>((ok, err) => {
548
385
  if (!this.remotePubkey) throw new Error('Signer pubkey not set');
549
386
 
550
- console.log('get_public_key', this.remotePubkey);
551
-
552
- const timeoutId = setTimeout(() => {
553
- err(new Error('NIP46 get_public_key timeout'));
554
- }, NIP46_TIMEOUT);
555
-
387
+ console.log("get_public_key", this.remotePubkey);
556
388
  this._rpc.sendRequest(this.remotePubkey, 'get_public_key', [], 24133, (response: NDKRpcResponse) => {
557
- clearTimeout(timeoutId);
558
- if (response.error) {
559
- err(new Error(response.error));
560
- } else {
561
- ok(response.result);
562
- }
389
+ ok(response.result);
563
390
  });
564
391
  });
565
392
  }
@@ -581,21 +408,15 @@ export class Nip46Signer extends NDKNip46Signer {
581
408
  }
582
409
 
583
410
  public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
584
- const params = [name, domain, '', perms];
585
-
586
- const r = await new Promise<NDKRpcResponse>((ok, err) => {
587
- const timeoutId = setTimeout(() => {
588
- err(new Error('NIP46 create_account timeout'));
589
- }, NIP46_TIMEOUT);
590
-
591
- this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, response => {
592
- clearTimeout(timeoutId);
593
- if (response.error) {
594
- err(new Error(response.error));
595
- } else {
596
- ok(response);
597
- }
598
- });
411
+ const params = [
412
+ name,
413
+ domain,
414
+ '', // email
415
+ perms,
416
+ ];
417
+
418
+ const r = await new Promise<NDKRpcResponse>(ok => {
419
+ this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, ok);
599
420
  });
600
421
 
601
422
  console.log('create_account pubkey', r);
@@ -1,4 +1,4 @@
1
- import { Info } from 'nostr-login-components';
1
+ import { Info } from 'nostr-login-components/dist/types/types';
2
2
 
3
3
  export interface Signer {
4
4
  signEvent: (event: any) => Promise<any>;
@@ -1,4 +1,4 @@
1
- import { Info } from 'nostr-login-components';
1
+ import { Info } from 'nostr-login-components/dist/types/types';
2
2
  import { NostrLoginOptions } from '../types';
3
3
 
4
4
  class NostrParams {
@@ -2,7 +2,6 @@ import { EventEmitter } from 'tseep';
2
2
  import { CALL_TIMEOUT } from '../const';
3
3
 
4
4
  class ProcessManager extends EventEmitter {
5
- private paused = false;
6
5
  private callCount: number = 0;
7
6
  private callTimer: NodeJS.Timeout | undefined;
8
7
 
@@ -11,41 +10,22 @@ class ProcessManager extends EventEmitter {
11
10
  }
12
11
 
13
12
  public onAuthUrl() {
14
- console.log('ProcessManager.onAuthUrl called, resetting timer');
15
- this.resetTimer();
13
+ if (Boolean(this.callTimer)) {
14
+ clearTimeout(this.callTimer);
15
+ }
16
16
  }
17
17
 
18
18
  public onIframeUrl() {
19
- console.log('ProcessManager.onIframeUrl called, resetting timer');
20
- this.resetTimer();
21
- }
22
-
23
- public resetTimer() {
24
- // 既存のタイマーがあればクリア
25
- if (this.callTimer) {
19
+ if (Boolean(this.callTimer)) {
26
20
  clearTimeout(this.callTimer);
27
- this.callTimer = undefined; // IDをクリア
28
- console.log('ProcessManager: timer reset');
29
- }
30
-
31
- // 監視対象が残っていて、かつ一時停止中でなければ、新しいタイマーを設定
32
- if (this.callCount > 0 && !this.paused) {
33
- this.callTimer = setTimeout(() => {
34
- console.log('ProcessManager: timeout reached, emitting onCallTimeout');
35
- this.callTimer = undefined; // タイムアウト時にIDをクリア
36
- this.emit('onCallTimeout');
37
- }, CALL_TIMEOUT);
38
- console.log(`ProcessManager: new timer set for ${CALL_TIMEOUT} ms`);
39
21
  }
40
22
  }
41
23
 
42
24
  public async wait<T>(cb: () => Promise<T>): Promise<T> {
43
- console.log('ProcessManager.wait called, callTimer exists:', !!this.callTimer, 'callCount:', this.callCount);
25
+ // FIXME only allow 1 parallel req
44
26
 
45
- // 修正点: タイマーの初期設定を resetTimer に任せる
46
- if (!this.callTimer && this.callCount === 0) {
47
- // waitの最初の呼び出し時のみ、タイマーを初期設定
48
- this.resetTimer();
27
+ if (!this.callTimer) {
28
+ this.callTimer = setTimeout(() => this.emit('onCallTimeout'), CALL_TIMEOUT);
49
29
  }
50
30
 
51
31
  if (!this.callCount) {
@@ -54,52 +34,33 @@ class ProcessManager extends EventEmitter {
54
34
 
55
35
  this.callCount++;
56
36
 
57
- let error: any;
58
- let result: any;
37
+ let error;
38
+ let result;
59
39
 
60
- // 非同期処理の実行
61
40
  try {
62
41
  result = await cb();
63
42
  } catch (e) {
64
43
  error = e;
65
44
  }
66
45
 
67
- // ★ 修正点: クリーンアップロジックを resetTimer に置き換え
68
- // ProcessManagerの呼び出しカウントをデクリメント
69
46
  this.callCount--;
70
- this.emit('onCallEnd');
71
-
72
- // リクエスト完了後、タイマーをリセットし、callCountに応じて再設定
73
- this.resetTimer();
74
47
 
75
- // エラーがあればスローし、呼び出し元に伝播させる
76
- if (error) {
77
- throw error;
78
- }
79
-
80
- // @ts-ignore
81
- return result as T;
82
- }
48
+ this.emit('onCallEnd');
83
49
 
84
- public pause() {
85
- console.log('ProcessManager: PAUSING timer...');
86
50
  if (this.callTimer) {
87
51
  clearTimeout(this.callTimer);
88
52
  }
89
- this.callTimer = undefined; // タイマーをクリア
90
- this.paused = true;
91
- this.emit('onCallPause');
92
- }
93
53
 
94
- public resume() {
95
- console.log('ProcessManager: RESUMING timer...');
96
- this.paused = false;
54
+ this.callTimer = undefined;
97
55
 
98
- // ★ 修正点: pause解除後、監視対象が残っていればタイマーを再設定 (resetTimerを呼び出す)
99
- if (this.callCount > 0 && !this.callTimer) {
100
- this.resetTimer();
56
+ if (error) {
57
+ throw error;
101
58
  }
102
- this.emit('onCallResume');
59
+
60
+ // we can't return undefined bcs an exception is
61
+ // thrown above on error
62
+ // @ts-ignore
63
+ return result;
103
64
  }
104
65
  }
105
66