@konemono/nostr-login 1.7.21 → 1.7.23

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,11 +1,13 @@
1
1
  import { NostrLoginOptions, StartScreens, TypeModal } from '../types';
2
- import { checkNip05, getBunkerUrl, getDarkMode, localStorageRemoveRecent, localStorageSetItem, prepareSignupRelays } from '../utils';
2
+ import { checkNip05, getBunkerUrl, getDarkMode, localStorageRemoveRecent, localStorageSetItem, prepareSignupRelays, localStorageGetItem } from '../utils';
3
3
  import { AuthNostrService, NostrExtensionService, NostrParams } from '.';
4
4
  import { EventEmitter } from 'tseep';
5
- import { ConnectionString, Info, RecentType } from 'nostr-login-components';
5
+ import { ConnectionString, Info, RecentType } from 'nostr-login-components/dist/types/types';
6
6
  import { nip19 } from 'nostr-tools';
7
7
  import { setDarkMode } from '..';
8
8
 
9
+ const RELAY_STORAGE_KEY = '__nostrlogin_user_relays';
10
+
9
11
  class ModalManager extends EventEmitter {
10
12
  private modal: TypeModal | null = null;
11
13
  private params: NostrParams;
@@ -23,6 +25,18 @@ class ModalManager extends EventEmitter {
23
25
  this.authNostrService = authNostrService;
24
26
  }
25
27
 
28
+ private getUserRelays(): string[] {
29
+ const stored = localStorageGetItem(RELAY_STORAGE_KEY);
30
+ if (stored && Array.isArray(stored)) {
31
+ return stored;
32
+ }
33
+ return [];
34
+ }
35
+
36
+ private saveUserRelays(relays: string[]) {
37
+ localStorageSetItem(RELAY_STORAGE_KEY, JSON.stringify(relays));
38
+ }
39
+
26
40
  public async waitReady() {
27
41
  if (this.launcherPromise) {
28
42
  try {
@@ -34,10 +48,8 @@ class ModalManager extends EventEmitter {
34
48
 
35
49
  public async launch(opt: NostrLoginOptions) {
36
50
  console.log('nostr-login launch', opt);
37
- // mutex
38
51
  if (this.launcherPromise) await this.waitReady();
39
52
 
40
- // hmm?!
41
53
  if (this.authNostrService.isAuthing()) this.authNostrService.resetAuth();
42
54
 
43
55
  this.opt = opt;
@@ -61,7 +73,6 @@ class ModalManager extends EventEmitter {
61
73
  this.modal.setAttribute('bunkers', opt.bunkers);
62
74
  } else {
63
75
  let bunkers = 'nsec.app,highlighter.com';
64
- // if (opt.dev) bunkers += ',new.nsec.app';
65
76
  this.modal.setAttribute('bunkers', bunkers);
66
77
  }
67
78
 
@@ -91,6 +102,11 @@ class ModalManager extends EventEmitter {
91
102
  this.modal.isLoadingExtension = false;
92
103
  this.modal.isLoading = false;
93
104
 
105
+ const userRelays = this.getUserRelays();
106
+ if (userRelays.length > 0) {
107
+ this.params.optionsModal.connectRelays = userRelays;
108
+ }
109
+
94
110
  [this.modal.connectionString, this.modal.connectionStringServices] = await this.authNostrService.getNostrConnectServices();
95
111
 
96
112
  dialog.appendChild(this.modal);
@@ -100,21 +116,11 @@ class ModalManager extends EventEmitter {
100
116
 
101
117
  this.launcherPromise = new Promise<void>((ok, err) => {
102
118
  dialog.addEventListener('close', () => {
103
- // noop if already resolved
104
119
  err(new Error('Closed'));
105
120
 
106
121
  this.authNostrService.resetAuth();
107
122
 
108
123
  if (this.modal) {
109
- // it's reset on modal creation
110
- // // reset state
111
- // this.modal.isLoading = false;
112
- // this.modal.authUrl = '';
113
- // this.modal.iframeUrl = '';
114
- // this.modal.error = '';
115
- // this.modal.isLoadingExtension = false;
116
-
117
- // drop it
118
124
  // @ts-ignore
119
125
  document.body.removeChild(this.modal.parentNode);
120
126
  this.modal = null;
@@ -157,20 +163,16 @@ class ModalManager extends EventEmitter {
157
163
 
158
164
  const login = async (name: string, domain?: string) => {
159
165
  await exec(async () => {
160
- // convert name to bunker url
161
166
  const bunkerUrl = await getBunkerUrl(name, this.params.optionsModal);
162
167
 
163
- // connect to bunker by url
164
168
  await this.authNostrService.authNip46('login', { name, bunkerUrl, domain });
165
169
  });
166
170
  };
167
171
 
168
172
  const signup = async (name: string) => {
169
173
  await exec(async () => {
170
- // create acc on service and get bunker url
171
174
  const { bunkerUrl, sk } = await this.authNostrService.createAccount(name);
172
175
 
173
- // connect to bunker by url
174
176
  await this.authNostrService.authNip46('signup', { name, bunkerUrl, sk });
175
177
  });
176
178
  };
@@ -190,7 +192,6 @@ class ModalManager extends EventEmitter {
190
192
  cs.link = this.authNostrService.prepareImportUrl(cs.link);
191
193
 
192
194
  if (this.modal && iframeUrl) {
193
- // we pass the link down to iframe so it could open it
194
195
  this.modal.authUrl = cs.link;
195
196
  this.modal.iframeUrl = iframeUrl;
196
197
  this.modal.isLoading = false;
@@ -208,7 +209,6 @@ class ModalManager extends EventEmitter {
208
209
 
209
210
  if (this.modal) {
210
211
  if (iframeUrl) {
211
- // we pass the link down to iframe so it could open it
212
212
  this.modal.authUrl = link;
213
213
  this.modal.iframeUrl = iframeUrl;
214
214
  this.modal.isLoading = false;
@@ -239,8 +239,6 @@ class ModalManager extends EventEmitter {
239
239
  .charAt(0)
240
240
  .toUpperCase() + self.hostname.slice(1);
241
241
  const relays = prepareSignupRelays(this.params.optionsModal.signupRelays);
242
- // const url = `https://start.njump.me/?an=${name}&at=popup&ac=${window.location.href}&s=${this.opt!.followNpubs || ''}&arr=${relays}&awr=${relays}`;
243
- // console.log('njump url', url);
244
242
 
245
243
  this.modal!.njumpIframe = `
246
244
  <html><body>
@@ -248,18 +246,15 @@ class ModalManager extends EventEmitter {
248
246
  <script>
249
247
  new NstartModal({
250
248
  baseUrl: 'https://start.njump.me',
251
- // Required parameters
252
249
  an: '${name}',
253
- // Optional parameters
254
250
  s: [${this.opt!.followNpubs ? `'${this.opt!.followNpubs}'` : ''}],
255
- afb: false, // forceBunker
256
- asb: false, // skipBunker
257
- aan: false, // avoidNsec
258
- aac: true, // avoidNcryptsec
259
- ahc: true, // hide close button
260
- arr: ${JSON.stringify(relays)}, //readRelays
261
- awr: ${JSON.stringify(relays)}, //writeRelays
262
- // Callbacks
251
+ afb: false,
252
+ asb: false,
253
+ aan: false,
254
+ aac: true,
255
+ ahc: true,
256
+ arr: ${JSON.stringify(relays)},
257
+ awr: ${JSON.stringify(relays)},
263
258
  onComplete: (result) => {
264
259
  console.log('Login token:', result.nostrLogin);
265
260
  window.parent.location.href='${window.location.href}#nostr-login='+result.nostrLogin;
@@ -270,11 +265,10 @@ class ModalManager extends EventEmitter {
270
265
  }).open();
271
266
  </script>
272
267
  </body></html>
273
- `.replaceAll('&', '&amp;'); // needed?
268
+ `.replaceAll('&', '&amp;');
274
269
 
275
270
  return new Promise((ok, err) => {
276
271
  const process = async (nsecOrBunker: string) => {
277
- // process the returned value
278
272
  console.log('nsecOrBunker', nsecOrBunker);
279
273
  if (nsecOrBunker.startsWith('nsec1')) {
280
274
  let decoded;
@@ -300,7 +294,6 @@ class ModalManager extends EventEmitter {
300
294
  if (window.location.hash.startsWith('#nostr-login=')) {
301
295
  const nsecOrBunker = window.location.hash.split('#nostr-login=')[1];
302
296
 
303
- // clear hash from history
304
297
  const url = new URL(window.location.toString());
305
298
  url.hash = '';
306
299
  window.history.replaceState({}, '', url.toString());
@@ -309,9 +302,6 @@ class ModalManager extends EventEmitter {
309
302
  }
310
303
  };
311
304
 
312
- // // use random 'target' to make sure window.opener is
313
- // // accessible to the popup
314
- // window.open(url, '' + Date.now(), 'popup=true,width=600,height=950');
315
305
  window.addEventListener('hashchange', onOpen);
316
306
  });
317
307
  });
@@ -359,7 +349,6 @@ class ModalManager extends EventEmitter {
359
349
  });
360
350
 
361
351
  this.modal.addEventListener('nlNostrConnectDefault', () => {
362
- // dedup the calls
363
352
  if (!this.authNostrService.isAuthing()) nostrConnect();
364
353
  });
365
354
 
@@ -368,18 +357,30 @@ class ModalManager extends EventEmitter {
368
357
  this.authNostrService.cancelNostrConnect();
369
358
  });
370
359
 
360
+ this.modal.addEventListener('nlSaveUserRelays', (event: any) => {
361
+ const relays = event.detail as string[];
362
+ console.log('nlSaveUserRelays', relays);
363
+ this.saveUserRelays(relays);
364
+ this.params.optionsModal.connectRelays = relays;
365
+ });
366
+
367
+ this.modal.addEventListener('nlGetUserRelays', () => {
368
+ const relays = this.getUserRelays();
369
+ console.log('nlGetUserRelays', relays);
370
+ if (this.modal) {
371
+ this.modal.dispatchEvent(
372
+ new CustomEvent('nlUserRelaysReply', {
373
+ detail: relays,
374
+ }),
375
+ );
376
+ }
377
+ });
378
+
371
379
  this.modal.addEventListener('nlSwitchAccount', (event: any) => {
372
380
  const eventInfo: Info = event.detail as Info;
373
381
 
374
382
  this.emit('onSwitchAccount', eventInfo);
375
383
 
376
- // wait a bit, if dialog closes before
377
- // switching finishes then launched promise rejects
378
-
379
- // FIXME this calls resetAuth which then prevents
380
- // endAuth from getting properly called. 300 is not
381
- // enough to init iframe, so there should be a
382
- // feedback from switchAccount here
383
384
  setTimeout(() => dialog.close(), 300);
384
385
  });
385
386
 
@@ -426,7 +427,7 @@ class ModalManager extends EventEmitter {
426
427
  else throw new Error('Bad npub');
427
428
  } else if (nameNpub.trim().length === 64) {
428
429
  pubkey = nameNpub.trim();
429
- nip19.npubEncode(pubkey); // check
430
+ nip19.npubEncode(pubkey);
430
431
  }
431
432
  return pubkey;
432
433
  };
@@ -466,13 +467,10 @@ class ModalManager extends EventEmitter {
466
467
  throw new Error('Failed to send DM');
467
468
  }
468
469
 
469
- // switch to 'enter code' mode
470
470
  this.modal.isOTP = true;
471
471
 
472
- // remember for code handler below
473
472
  otpPubkey = pubkey;
474
473
 
475
- // spinner off
476
474
  this.modal.isLoading = false;
477
475
  },
478
476
  { start: true },
@@ -529,8 +527,6 @@ class ModalManager extends EventEmitter {
529
527
  this.modal.isLoading = false;
530
528
  }
531
529
 
532
- // this.authNostrService.cancelListenNostrConnect();
533
-
534
530
  dialog.close();
535
531
  err(new Error('Cancelled'));
536
532
  };
@@ -555,9 +551,6 @@ class ModalManager extends EventEmitter {
555
551
  }
556
552
 
557
553
  public async showIframeUrl(url: string) {
558
- // make sure we consume the previous promise,
559
- // otherwise launch will start await-ing
560
- // before modal is created and setting iframeUrl will fail
561
554
  await this.waitReady();
562
555
 
563
556
  this.launch({
@@ -1,11 +1,13 @@
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';
4
5
 
5
6
  class NostrRpc extends NDKNostrRpc {
6
7
  protected _ndk: NDK;
7
8
  protected _signer: PrivateKeySigner;
8
9
  protected requests: Set<string> = new Set();
10
+ protected requestTimeouts: Map<string, NodeJS.Timeout> = new Map();
9
11
  private sub?: NDKSubscription;
10
12
  protected _useNip44: boolean = false;
11
13
 
@@ -16,19 +18,34 @@ class NostrRpc extends NDKNostrRpc {
16
18
  }
17
19
 
18
20
  public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
19
- // NOTE: fixing ndk
20
21
  filter.kinds = filter.kinds?.filter(k => k === 24133);
21
22
  this.sub = await super.subscribe(filter);
22
23
  return this.sub;
23
24
  }
24
25
 
25
26
  public stop() {
27
+ this.clearAllTimeouts();
26
28
  if (this.sub) {
27
29
  this.sub.stop();
28
30
  this.sub = undefined;
29
31
  }
30
32
  }
31
33
 
34
+ private clearAllTimeouts() {
35
+ for (const timeout of this.requestTimeouts.values()) {
36
+ clearTimeout(timeout);
37
+ }
38
+ this.requestTimeouts.clear();
39
+ }
40
+
41
+ private clearTimeout(id: string) {
42
+ const timeout = this.requestTimeouts.get(id);
43
+ if (timeout) {
44
+ clearTimeout(timeout);
45
+ this.requestTimeouts.delete(id);
46
+ }
47
+ }
48
+
32
49
  public setUseNip44(useNip44: boolean) {
33
50
  this._useNip44 = useNip44;
34
51
  }
@@ -39,7 +56,6 @@ class NostrRpc extends NDKNostrRpc {
39
56
  return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
40
57
  }
41
58
 
42
- // override to auto-decrypt nip04/nip44
43
59
  public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
44
60
  const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
45
61
  remoteUser.ndk = this._ndk;
@@ -68,9 +84,6 @@ class NostrRpc extends NDKNostrRpc {
68
84
  }
69
85
  }
70
86
 
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
74
87
  public async listen(nostrConnectSecret: string): Promise<string> {
75
88
  const pubkey = this._signer.pubkey;
76
89
  console.log('nostr-login listening for conn to', pubkey);
@@ -78,61 +91,46 @@ class NostrRpc extends NDKNostrRpc {
78
91
  'kinds': [24133],
79
92
  '#p': [pubkey],
80
93
  });
81
-
82
94
  return new Promise<string>((ok, err) => {
83
- let finished = false;
84
-
85
- // 30 sec timeout
86
95
  const timeoutId = setTimeout(() => {
87
- if (!finished) {
88
- finished = true;
89
- this.stop();
90
- console.log('nostr-login: listen timeout, emitting timeout event');
91
- this.emit('timeout');
92
- err('Listen timed out');
93
- }
94
- }, 30000);
95
-
96
+ this.stop();
97
+ err(new Error('NIP46 listen timeout'));
98
+ }, NIP46_TIMEOUT);
99
+
96
100
  sub.on('event', async (event: NDKEvent) => {
97
- if (finished) return;
98
-
99
101
  try {
100
102
  const parsedEvent = await this.parseEvent(event);
101
- // console.log('ack parsedEvent', parsedEvent);
102
103
  if (!(parsedEvent as NDKRpcRequest).method) {
103
104
  const response = parsedEvent as NDKRpcResponse;
104
105
 
105
- // ignore
106
106
  if (response.result === 'auth_url') return;
107
107
 
108
- // FIXME for now accept 'ack' replies, later on only
109
- // accept secrets
110
108
  if (response.result === 'ack' || response.result === nostrConnectSecret) {
111
- finished = true;
112
109
  clearTimeout(timeoutId);
113
- this.stop();
114
110
  ok(event.pubkey);
115
111
  } else {
116
- finished = true;
117
112
  clearTimeout(timeoutId);
118
- this.stop();
119
113
  err(response.error);
120
114
  }
121
115
  }
122
116
  } catch (e) {
123
117
  console.log('error parsing event', e, event.rawEvent());
124
118
  }
119
+ this.stop();
125
120
  });
126
121
  });
127
122
  }
128
123
 
129
- // since ndk doesn't yet support perms param
130
- // we reimplement the 'connect' call here
131
- // instead of await signer.blockUntilReady();
132
124
  public async connect(pubkey: string, token?: string, perms?: string) {
133
125
  return new Promise<void>((ok, err) => {
134
126
  const connectParams = [pubkey!, token || '', perms || ''];
127
+
128
+ const timeoutId = setTimeout(() => {
129
+ err(new Error('NIP46 connect timeout'));
130
+ }, NIP46_TIMEOUT);
131
+
135
132
  this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
133
+ clearTimeout(timeoutId);
136
134
  if (response.result === 'ack') {
137
135
  ok();
138
136
  } else {
@@ -149,22 +147,13 @@ class NostrRpc extends NDKNostrRpc {
149
147
  public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
150
148
  const id = this.getId();
151
149
 
152
- // response handler will deduplicate auth urls and responses
153
150
  this.setResponseHandler(id, cb);
154
151
 
155
- // create and sign request
156
152
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
157
- console.log("sendRequest", { event, method, remotePubkey, params });
153
+ console.log('sendRequest', { event, method, remotePubkey, params });
158
154
 
159
- // send to relays
160
- const relays = await event.publish();
161
- if (relays.size === 0) throw new Error('Failed to publish to relays');
155
+ await event.publish();
162
156
 
163
- // NOTE: ndk returns a promise that never resolves and
164
- // in fact REQUIRES cb to be provided (otherwise no way
165
- // to consume the result), we've already stepped on the bug
166
- // of waiting for this unresolvable result, so now we return
167
- // undefined to make sure waiters fail, not hang.
168
157
  // @ts-ignore
169
158
  return undefined as NDKRpcResponse;
170
159
  }
@@ -172,7 +161,25 @@ class NostrRpc extends NDKNostrRpc {
172
161
  protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
173
162
  let authUrlSent = false;
174
163
  const now = Date.now();
175
- return new Promise<NDKRpcResponse>((resolve, reject) => {
164
+
165
+ const timeoutId = setTimeout(() => {
166
+ if (this.requests.has(id)) {
167
+ this.requests.delete(id);
168
+ this.requestTimeouts.delete(id);
169
+ if (cb) {
170
+ cb({
171
+ id,
172
+ result: '',
173
+ error: 'NIP46 request timeout',
174
+ event: undefined as any,
175
+ });
176
+ }
177
+ }
178
+ }, NIP46_TIMEOUT);
179
+
180
+ this.requestTimeouts.set(id, timeoutId);
181
+
182
+ return new Promise<NDKRpcResponse>(() => {
176
183
  const responseHandler = (response: NDKRpcResponse) => {
177
184
  if (response.result === 'auth_url') {
178
185
  this.once(`response-${id}`, responseHandler);
@@ -182,30 +189,18 @@ class NostrRpc extends NDKNostrRpc {
182
189
  }
183
190
  } else if (cb) {
184
191
  if (this.requests.has(id)) {
192
+ this.clearTimeout(id);
185
193
  this.requests.delete(id);
186
194
  console.log('nostr-login processed nip46 request in', Date.now() - now, 'ms');
187
195
  cb(response);
188
- resolve(response);
189
196
  }
190
197
  }
191
198
  };
192
199
 
193
200
  this.once(`response-${id}`, responseHandler);
194
-
195
- // timeout
196
- setTimeout(() => {
197
- if (this.requests.has(id)) {
198
- this.requests.delete(id);
199
- this.removeListener(`response-${id}`, responseHandler);
200
- console.log('nostr-login: NIP-46 request timeout, emitting timeout event');
201
- this.emit('timeout');
202
- reject('[Nip46] Request timed out');
203
- }
204
- }, 30000);
205
201
  });
206
202
  }
207
203
 
208
-
209
204
  protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
210
205
  this.requests.add(id);
211
206
  const localUser = await this._signer.user();
@@ -232,7 +227,6 @@ export class IframeNostrRpc extends NostrRpc {
232
227
  private peerOrigin?: string;
233
228
  private iframePort?: MessagePort;
234
229
  private iframeRequests = new Map<string, { id: string; pubkey: string }>();
235
- private iframePingInterval?: NodeJS.Timeout;
236
230
 
237
231
  public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
238
232
  super(ndk, localSigner);
@@ -246,7 +240,6 @@ export class IframeNostrRpc extends NostrRpc {
246
240
  this._ndk,
247
241
  {},
248
242
  {
249
- // don't send to relay
250
243
  closeOnEose: true,
251
244
  cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
252
245
  },
@@ -258,8 +251,7 @@ export class IframeNostrRpc extends NostrRpc {
258
251
 
259
252
  this.iframePort = port;
260
253
 
261
- // to make sure Chrome doesn't terminate the channel
262
- this.iframePingInterval = setInterval(() => {
254
+ setInterval(() => {
263
255
  console.log('iframe-nip46 ping');
264
256
  this.iframePort!.postMessage('ping');
265
257
  }, 5000);
@@ -273,7 +265,6 @@ export class IframeNostrRpc extends NostrRpc {
273
265
  return;
274
266
  }
275
267
 
276
- // a copy-paste from rpc.subscribe
277
268
  try {
278
269
  const event = ev.data;
279
270
 
@@ -281,7 +272,6 @@ export class IframeNostrRpc extends NostrRpc {
281
272
  if (!verifySignature(event)) throw new Error('Invalid event signature from iframe');
282
273
  const nevent = new NDKEvent(this._ndk, event);
283
274
  const parsedEvent = await this.parseEvent(nevent);
284
- // we're only implementing client-side rpc
285
275
  if (!(parsedEvent as NDKRpcRequest).method) {
286
276
  console.log('parsed response', parsedEvent);
287
277
  this.emit(`response-${parsedEvent.id}`, parsedEvent);
@@ -292,39 +282,22 @@ export class IframeNostrRpc extends NostrRpc {
292
282
  };
293
283
  }
294
284
 
295
- public cleanup() {
296
- if (this.iframePingInterval) {
297
- clearInterval(this.iframePingInterval);
298
- this.iframePingInterval = undefined;
299
- }
300
- }
301
-
302
285
  public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
303
286
  const id = this.getId();
304
287
 
305
- // create and sign request event
306
288
  const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
307
289
 
308
- // set response handler, it will dedup auth urls,
309
- // and also dedup response handlers - we're sending
310
- // to relays and to iframe
311
290
  this.setResponseHandler(id, cb);
312
291
 
313
292
  if (this.iframePort) {
314
- // map request event id to request id, if iframe
315
- // has no key it will reply with error:event_id (it can't
316
- // decrypt the request id without keys)
317
293
  this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
318
294
 
319
- // send to iframe
320
295
  console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
321
296
  this.iframePort.postMessage(event.rawEvent());
322
297
  } else {
323
- // send to relays
324
298
  await event.publish();
325
299
  }
326
300
 
327
- // see notes in 'super'
328
301
  // @ts-ignore
329
302
  return undefined as NDKRpcResponse;
330
303
  }
@@ -341,14 +314,11 @@ export class ReadyListener {
341
314
  this.promise = new Promise<any>(ok => {
342
315
  console.log(new Date(), 'started listener for', this.messages);
343
316
 
344
- // ready message handler
345
317
  const onReady = async (e: MessageEvent) => {
346
318
  const originHostname = new URL(origin!).hostname;
347
319
  const messageHostname = new URL(e.origin).hostname;
348
- // same host or subdomain
349
320
  const validHost = messageHostname === originHostname || messageHostname.endsWith('.' + originHostname);
350
321
  if (!validHost || !Array.isArray(e.data) || !e.data.length || !this.messages.includes(e.data[0])) {
351
- // console.log(new Date(), 'got invalid ready message', e.origin, e.data);
352
322
  return;
353
323
  }
354
324
 
@@ -362,14 +332,7 @@ export class ReadyListener {
362
332
 
363
333
  async wait(): Promise<any> {
364
334
  console.log(new Date(), 'waiting for', this.messages);
365
-
366
- const r = await new Promise<any>((ok, err) => {
367
- // 30 sec timeout for iframe ready
368
- setTimeout(() => err(new Date() + ' timeout for ' + this.messages), 30000);
369
-
370
- this.promise.then(ok).catch(err);
371
- });
372
-
335
+ const r = await this.promise;
373
336
  console.log(new Date(), 'finished waiting for', this.messages, r);
374
337
  return r;
375
338
  }
@@ -382,15 +345,11 @@ export class Nip46Signer extends NDKNip46Signer {
382
345
  constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
383
346
  super(ndk, signerPubkey, localSigner);
384
347
 
385
- // override with our own rpc implementation
386
348
  this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
387
- this._rpc.setUseNip44(true); // !!this.params.optionsModal.dev);
349
+ this._rpc.setUseNip44(true);
388
350
  this._rpc.on('authUrl', (url: string) => {
389
351
  this.emit('authUrl', url);
390
352
  });
391
- this._rpc.on('timeout', () => {
392
- this.emit('timeout');
393
- });
394
353
 
395
354
  this.rpc = this._rpc;
396
355
  }
@@ -400,17 +359,14 @@ export class Nip46Signer extends NDKNip46Signer {
400
359
  }
401
360
 
402
361
  private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
403
- console.log("setSignerPubkey", signerPubkey);
362
+ console.log('setSignerPubkey', signerPubkey);
404
363
 
405
- // ensure it's set
406
364
  this.remotePubkey = signerPubkey;
407
365
 
408
- // when we're sure it's known
409
366
  this._rpc.on(`iframeRestart-${signerPubkey}`, () => {
410
367
  this.emit('iframeRestart');
411
368
  });
412
369
 
413
- // now call getPublicKey and swap remotePubkey w/ that
414
370
  await this.initUserPubkey(sameAsUser ? signerPubkey : '');
415
371
  }
416
372
 
@@ -425,9 +381,19 @@ export class Nip46Signer extends NDKNip46Signer {
425
381
  this._userPubkey = await new Promise<string>((ok, err) => {
426
382
  if (!this.remotePubkey) throw new Error('Signer pubkey not set');
427
383
 
428
- console.log("get_public_key", this.remotePubkey);
384
+ console.log('get_public_key', this.remotePubkey);
385
+
386
+ const timeoutId = setTimeout(() => {
387
+ err(new Error('NIP46 get_public_key timeout'));
388
+ }, NIP46_TIMEOUT);
389
+
429
390
  this._rpc.sendRequest(this.remotePubkey, 'get_public_key', [], 24133, (response: NDKRpcResponse) => {
430
- ok(response.result);
391
+ clearTimeout(timeoutId);
392
+ if (response.error) {
393
+ err(new Error(response.error));
394
+ } else {
395
+ ok(response.result);
396
+ }
431
397
  });
432
398
  });
433
399
  }
@@ -449,15 +415,21 @@ export class Nip46Signer extends NDKNip46Signer {
449
415
  }
450
416
 
451
417
  public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
452
- const params = [
453
- name,
454
- domain,
455
- '', // email
456
- perms,
457
- ];
458
-
459
- const r = await new Promise<NDKRpcResponse>(ok => {
460
- this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, ok);
418
+ const params = [name, domain, '', perms];
419
+
420
+ const r = await new Promise<NDKRpcResponse>((ok, err) => {
421
+ const timeoutId = setTimeout(() => {
422
+ err(new Error('NIP46 create_account timeout'));
423
+ }, NIP46_TIMEOUT);
424
+
425
+ this.rpc.sendRequest(bunkerPubkey, 'create_account', params, undefined, response => {
426
+ clearTimeout(timeoutId);
427
+ if (response.error) {
428
+ err(new Error(response.error));
429
+ } else {
430
+ ok(response);
431
+ }
432
+ });
461
433
  });
462
434
 
463
435
  console.log('create_account pubkey', r);