@konemono/nostr-login 1.7.59 → 1.7.61

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.59",
3
+ "version": "1.7.61",
4
4
  "description": "",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",
@@ -1,5 +1,7 @@
1
1
  import { Signer } from './Nostr';
2
2
 
3
+ const PENDING_KEY = 'amber_pending_request';
4
+
3
5
  export class AmberDirectSigner implements Signer {
4
6
  private _pubkey: string = '';
5
7
  private static pendingResolves: Map<string, (result: any) => void> = new Map();
@@ -49,31 +51,36 @@ export class AmberDirectSigner implements Signer {
49
51
 
50
52
  private setPending(id: string, content: string, type: string) {
51
53
  const hash = this.getHash(content);
52
- sessionStorage.setItem(`amber_pending_${id}`, JSON.stringify({ hash, type }));
54
+ const data = JSON.stringify({ id, type, hash, timestamp: Date.now() });
55
+ sessionStorage.setItem(`amber_pending_${id}`, data);
56
+ localStorage.setItem(PENDING_KEY, data); // Backup for reloads
53
57
  }
54
58
 
55
59
  public static resolvePending(id: string, type: string, result: string) {
56
- // Try resolving by ID first
60
+ console.log('AmberDirectSigner: resolvePending', { id, type, pendingCount: this.pendingResolves.size });
61
+
57
62
  let resolve = this.pendingResolves.get(id);
58
63
 
59
- // Fallback: If no ID match, try resolving by type if only one is pending of that type
64
+ // Fallback by type if only one pending of that type
60
65
  if (!resolve) {
61
- console.log('No direct ID match for Amber resolve, searching by type', { id, type });
66
+ console.log('AmberDirectSigner: Falling back to resolve by type', type);
62
67
  for (const [pendingId, pendingResolve] of this.pendingResolves.entries()) {
63
- const pending = sessionStorage.getItem(`amber_pending_${pendingId}`);
68
+ const pending = sessionStorage.getItem(`amber_pending_${pendingId}`) || localStorage.getItem(PENDING_KEY);
64
69
  if (pending) {
65
- const { type: pendingType } = JSON.parse(pending);
66
- if (pendingType === type) {
67
- resolve = pendingResolve;
68
- id = pendingId;
69
- break;
70
- }
70
+ try {
71
+ const { type: pendingType } = JSON.parse(pending);
72
+ if (pendingType === type || type.startsWith(pendingType)) {
73
+ resolve = pendingResolve;
74
+ id = pendingId;
75
+ break;
76
+ }
77
+ } catch (e) {}
71
78
  }
72
79
  }
73
80
  }
74
81
 
75
82
  if (resolve) {
76
- console.log('Resolving pending Amber promise', { id, type });
83
+ console.log('AmberDirectSigner: Promise resolved', id);
77
84
  resolve(result);
78
85
  this.pendingResolves.delete(id);
79
86
  return true;
@@ -84,9 +91,7 @@ export class AmberDirectSigner implements Signer {
84
91
  async signEvent(event: any): Promise<any> {
85
92
  const content = JSON.stringify(event);
86
93
  const cached = this.checkCache(content, 'sign_event');
87
- if (cached) {
88
- return JSON.parse(cached);
89
- }
94
+ if (cached) return JSON.parse(cached);
90
95
 
91
96
  const id = Math.random().toString(36).substring(7);
92
97
  this.setPending(id, content, 'sign_event');
@@ -99,6 +104,7 @@ export class AmberDirectSigner implements Signer {
99
104
 
100
105
  async getPublicKey(): Promise<string> {
101
106
  const id = Math.random().toString(36).substring(7);
107
+ this.setPending(id, '', 'get_public_key');
102
108
  const url = this.generateUrl('', 'get_public_key', id);
103
109
  window.location.href = url;
104
110
  return new Promise((resolve) => {
@@ -161,36 +167,28 @@ export class AmberDirectSigner implements Signer {
161
167
  private generateUrl(content: string, type: string, id: string, recipient?: string): string {
162
168
  const callbackUrl = new URL(window.location.origin + window.location.pathname);
163
169
 
164
- // Preserve other relevant current URL params if necessary, but avoid amber internal ones
165
- const currentParams = new URLSearchParams(window.location.search);
166
- currentParams.forEach((value, key) => {
167
- if (!['amberType', 'amberId', 'signature', 'result', 'pubKey', 'pubkey'].includes(key)) {
168
- callbackUrl.searchParams.set(key, value);
169
- }
170
- });
171
-
170
+ // Minimal parameters in callbackUrl to avoid conflicts
172
171
  callbackUrl.searchParams.set('amberType', type);
173
172
  callbackUrl.searchParams.set('amberId', id);
174
173
 
175
174
  const name = document.title || window.location.hostname || 'Nostr Login';
176
175
 
177
- const params = new URLSearchParams();
178
- params.set('type', type);
179
- params.set('id', id);
180
- params.set('callbackUrl', callbackUrl.toString());
181
- params.set('name', name);
182
- params.set('app_name', name); // Alias just in case
176
+ const params: string[] = [];
177
+ params.push(`type=${encodeURIComponent(type)}`);
178
+ params.push(`id=${encodeURIComponent(id)}`);
179
+ params.push(`callbackUrl=${encodeURIComponent(callbackUrl.toString())}`);
180
+ params.push(`name=${encodeURIComponent(name)}`);
181
+ params.push(`app_name=${encodeURIComponent(name)}`);
183
182
 
184
183
  if (recipient) {
185
- params.set('pubkey', recipient);
186
- params.set('pubKey', recipient); // Alias
184
+ params.push(`pubkey=${encodeURIComponent(recipient)}`);
187
185
  } else if (this._pubkey) {
188
- params.set('pubkey', this._pubkey);
189
- params.set('pubKey', this._pubkey); // Alias
186
+ params.push(`pubkey=${encodeURIComponent(this._pubkey)}`);
190
187
  }
191
188
 
192
- const dataPart = content ? encodeURIComponent(content) : '/';
193
- return `nostrsigner:${dataPart}?${params.toString()}`;
189
+ const dataPart = content ? encodeURIComponent(content) : 'get_public_key';
190
+ // Use nostrsigner: scheme as primary which is standard NIP-55
191
+ return `nostrsigner:${dataPart}?${params.join('&')}`;
194
192
  }
195
193
 
196
194
  public static parseResponse(): { type: string; id: string; result: string } | null {
@@ -200,12 +198,31 @@ export class AmberDirectSigner implements Signer {
200
198
 
201
199
  const getParam = (name: string) => params.get(name) || hashParams.get(name);
202
200
 
203
- const type = getParam('amberType') || getParam('type');
204
- const id = getParam('amberId') || getParam('id');
201
+ let type = getParam('amberType') || getParam('type');
202
+ let id = getParam('amberId') || getParam('id');
205
203
  const result = getParam('signature') || getParam('result') || getParam('pubKey') || getParam('pubkey');
206
204
 
205
+ // Fallback to localStorage pending if params are missing/merged
206
+ if (result && (!type || !id)) {
207
+ const pending = localStorage.getItem(PENDING_KEY);
208
+ if (pending) {
209
+ try {
210
+ const { id: pId, type: pType } = JSON.parse(pending);
211
+ if (!type) type = pType;
212
+ if (!id) id = pId;
213
+ console.log('AmberDirectSigner: Recovered type/id from localStorage');
214
+ } catch (e) {}
215
+ }
216
+ }
217
+
207
218
  if (type && result) {
208
- // ID might be missing in some older redirect cases, but we need it for precise resolution
219
+ // Handle "ID stuck to type" or typo cases
220
+ let finalType = type.toLowerCase();
221
+ if (finalType.includes('get_pub')) finalType = 'get_public_key';
222
+ else if (finalType.includes('sign_event')) finalType = 'sign_event';
223
+ else if (finalType.includes('encrypt')) finalType = finalType.includes('44') ? 'nip44_encrypt' : 'nip04_encrypt';
224
+ else if (finalType.includes('decrypt')) finalType = finalType.includes('44') ? 'nip44_decrypt' : 'nip04_decrypt';
225
+
209
226
  const finalId = id || '';
210
227
 
211
228
  // Clean up URL
@@ -215,8 +232,10 @@ export class AmberDirectSigner implements Signer {
215
232
  });
216
233
  newUrl.hash = '';
217
234
  window.history.replaceState({}, '', newUrl.toString());
235
+
236
+ localStorage.removeItem(PENDING_KEY); // Done
218
237
 
219
- return { type, id: finalId, result };
238
+ return { type: finalType, id: finalId, result };
220
239
  }
221
240
  return null;
222
241
  }
@@ -88,10 +88,19 @@ class AuthNostrService extends EventEmitter implements Signer {
88
88
 
89
89
  setTimeout(() => this.checkAmberResponse(), 100);
90
90
 
91
- window.addEventListener('focus', () => {
92
- console.log('Window focused, checking Amber response');
91
+ const check = () => {
93
92
  this.checkAmberResponse();
93
+ };
94
+
95
+ window.addEventListener('focus', check);
96
+ window.addEventListener('visibilitychange', () => {
97
+ if (document.visibilityState === 'visible') check();
94
98
  });
99
+ window.addEventListener('popstate', check);
100
+ window.addEventListener('hashchange', check);
101
+
102
+ // Periodic check as a safety net
103
+ setInterval(check, 1000);
95
104
  }
96
105
 
97
106
  private checkAmberResponse() {
@@ -99,10 +108,16 @@ class AuthNostrService extends EventEmitter implements Signer {
99
108
  if (response) {
100
109
  console.log('Amber response detected', response);
101
110
 
111
+ // Stop the "Connecting..." spinner
112
+ this.emit('onAuthUrl', { url: '' });
113
+
102
114
  // Resolve pending promises if any (for non-reload cases)
103
- AmberDirectSigner.resolvePending(response.id, response.type, response.result);
115
+ const resolved = AmberDirectSigner.resolvePending(response.id, response.type, response.result);
116
+ if (resolved) {
117
+ console.log('Resolved pending Amber promise via resolvePending');
118
+ }
104
119
 
105
- if (response.type === 'get_public_key') {
120
+ if (response.type === 'get_public_key' || response.type.includes('pub')) {
106
121
  const info: Info = {
107
122
  pubkey: response.result,
108
123
  authMethod: 'amber' as any,
@@ -181,6 +196,8 @@ class AuthNostrService extends EventEmitter implements Signer {
181
196
  if (link && !iframeUrl) {
182
197
  if (link === 'amber') {
183
198
  const signer = new AmberDirectSigner();
199
+ const url = (signer as any).generateUrl('', 'get_public_key', Math.random().toString(36).substring(7));
200
+ this.emit('onAuthUrl', { url });
184
201
  return (signer as any).getPublicKey(); // will redirect
185
202
  }
186
203
  window.open(link, '_blank', 'width=400,height=700');