@konemono/nostr-login 1.15.8 → 1.16.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.
@@ -19,7 +19,6 @@ export declare const checkNip05: (nip05: string) => Promise<{
19
19
  }>;
20
20
  export declare const localStorageAddAccount: (info: Info) => void;
21
21
  export declare const localStorageRemoveCurrentAccount: () => void;
22
- export declare const localStorageRemoveAccount: (user: Info) => void;
23
22
  export declare const localStorageRemoveRecent: (user: RecentType) => void;
24
23
  export declare const localStorageGetRecents: () => RecentType[];
25
24
  export declare const localStorageGetAccounts: () => Info[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@konemono/nostr-login",
3
- "version": "1.15.8",
3
+ "version": "1.16.1",
4
4
  "description": "Extended fork of nostr-login with multi-relay support, QR scanner, and improved stability",
5
5
  "main": "./dist/index.esm.js",
6
6
  "types": "./dist/index.d.ts",
@@ -27,7 +27,7 @@
27
27
  "tseep": "^1.2.1"
28
28
  },
29
29
  "devDependencies": {
30
- "@konemono/nostr-login-components": "^1.4.8",
30
+ "@konemono/nostr-login-components": "^1.5.0",
31
31
  "@rollup/plugin-commonjs": "^25.0.7",
32
32
  "@rollup/plugin-node-resolve": "^15.2.3",
33
33
  "@rollup/plugin-terser": "^0.4.4",
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import '@konemono/nostr-login-components';
2
2
  import { AuthNostrService, NostrExtensionService, Popup, NostrParams, Nostr, ProcessManager, BannerManager, ModalManager } from './modules';
3
3
  import { NostrLoginAuthOptions, NostrLoginOptions, StartScreens } from './types';
4
- import { localStorageGetAccounts, localStorageGetCurrent, localStorageGetRecents, localStorageRemoveAccount, localStorageSetItem } from './utils';
4
+ import { localStorageGetAccounts, localStorageGetCurrent, localStorageGetRecents, localStorageSetItem } from './utils';
5
5
  import { Info } from '@konemono/nostr-login-components/dist/types/types';
6
6
  import { NostrObjectParams } from './modules/Nostr';
7
7
 
@@ -105,10 +105,6 @@ export class NostrLoginInitializer {
105
105
  this.switchAccount(info);
106
106
  });
107
107
 
108
- this.modalManager.on('onRemoveAccount', async (info: Info) => {
109
- this.removeAccount(info);
110
- });
111
-
112
108
  this.modalManager.on('onLogoutBanner', async (info: Info) => {
113
109
  logout();
114
110
  });
@@ -133,10 +129,6 @@ export class NostrLoginInitializer {
133
129
  this.switchAccount(info);
134
130
  });
135
131
 
136
- this.bannerManager.on('onRemoveAccount', async (info: Info) => {
137
- this.removeAccount(info);
138
- });
139
-
140
132
  this.bannerManager.on('import', () => {
141
133
  this.launch('import');
142
134
  });
@@ -189,19 +181,6 @@ export class NostrLoginInitializer {
189
181
  }
190
182
  }
191
183
 
192
- private async removeAccount(info: Info) {
193
- const current = this.params.userInfo || localStorageGetCurrent();
194
- const isCurrent = current?.pubkey === info.pubkey && current?.authMethod === info.authMethod;
195
-
196
- if (isCurrent) {
197
- await this.logout();
198
- return;
199
- }
200
-
201
- localStorageRemoveAccount(info);
202
- this.updateAccounts();
203
- }
204
-
205
184
  private updateAccounts() {
206
185
  const accounts = localStorageGetAccounts();
207
186
  const recents = localStorageGetRecents();
@@ -456,6 +456,15 @@ class AuthNostrService extends EventEmitter implements Signer {
456
456
  }
457
457
 
458
458
  public async logout(keepSigner = false) {
459
+ // NIP-46 logout を送信(signer が session を削除)
460
+ if (this.signer && !keepSigner) {
461
+ try {
462
+ await this.signer.logout();
463
+ } catch (e) {
464
+ console.warn('NIP-46 logout RPC failed (non-fatal):', e);
465
+ }
466
+ }
467
+
459
468
  if (!keepSigner) this.releaseSigner();
460
469
 
461
470
  localStorageRemoveCurrentAccount();
@@ -693,6 +702,27 @@ class AuthNostrService extends EventEmitter implements Signer {
693
702
  await this.signer!.initUserPubkey(info.pubkey);
694
703
  }
695
704
 
705
+ // NIP-46 Spec: 接続確立後に switch_relays を即時送信(should、非致命的)
706
+ try {
707
+ const newRelays = await this.signer!.switchRelays();
708
+ if (newRelays && newRelays.length > 0) {
709
+ // 一時的にリレーゼロにならないよう、先に追加してから削除
710
+ const oldRelays = this.pool.relayUrls;
711
+ for (const r of newRelays) {
712
+ this.pool.addRelay(r);
713
+ }
714
+ // 古いリレーを削除(新規に含まれないものを除外)
715
+ for (const r of oldRelays) {
716
+ if (!newRelays.includes(r)) {
717
+ this.pool.removeRelay(r);
718
+ }
719
+ }
720
+ this.signer!.rpc.resubscribe();
721
+ }
722
+ } catch (e) {
723
+ console.warn('NIP-46 switch_relays failed (non-fatal):', e);
724
+ }
725
+
696
726
  info.pubkey = this.signer!.userPubkey as string;
697
727
  info.signerPubkey = this.signer!.bunkerPubkey;
698
728
 
@@ -862,7 +892,7 @@ class AuthNostrService extends EventEmitter implements Signer {
862
892
  return this.localSigner.decrypt({ pubkey }, ciphertext);
863
893
  }
864
894
  await this.ensureRelayConnection();
865
- return this.codec_call('nip04_decrypt', pubkey, ciphertext);
895
+ return this.signer!.decrypt(pubkey, ciphertext);
866
896
  }
867
897
 
868
898
  public async encrypt44(pubkey: string, plaintext: string) {
@@ -870,7 +900,7 @@ class AuthNostrService extends EventEmitter implements Signer {
870
900
  return this.nip44Codec.encrypt(this.localSigner.privateKey!, pubkey, plaintext);
871
901
  }
872
902
  await this.ensureRelayConnection();
873
- return this.codec_call('nip44_encrypt', pubkey, plaintext);
903
+ return this.signer!.nip44Encrypt(pubkey, plaintext);
874
904
  }
875
905
 
876
906
  public async decrypt44(pubkey: string, ciphertext: string) {
@@ -878,7 +908,7 @@ class AuthNostrService extends EventEmitter implements Signer {
878
908
  return this.nip44Codec.decrypt(this.localSigner.privateKey!, pubkey, ciphertext);
879
909
  }
880
910
  await this.ensureRelayConnection();
881
- return this.codec_call('nip44_decrypt', pubkey, ciphertext);
911
+ return this.signer!.nip44Decrypt(pubkey, ciphertext);
882
912
  }
883
913
  }
884
914
 
@@ -130,10 +130,6 @@ class BannerManager extends EventEmitter {
130
130
  this.emit('onSwitchAccount', event.detail);
131
131
  });
132
132
 
133
- this.banner.addEventListener('nlRemoveAccount', (event: any) => {
134
- this.emit('onRemoveAccount', event.detail);
135
- });
136
-
137
133
  this.banner.addEventListener('nlOpenWelcomeModal', () => {
138
134
  this.emit('launch');
139
135
 
@@ -492,10 +492,6 @@ class ModalManager extends EventEmitter {
492
492
  this.emit('updateAccounts');
493
493
  });
494
494
 
495
- this.modal.addEventListener('nlRemoveAccount', (event: any) => {
496
- this.emit('onRemoveAccount', event.detail as Info);
497
- });
498
-
499
495
  const nameToPubkey = async (nameNpub: string) => {
500
496
  let pubkey = '';
501
497
  if (nameNpub.includes('@')) {
@@ -151,7 +151,94 @@ export class Nip46Signer extends EventEmitter {
151
151
  });
152
152
  }
153
153
 
154
+ /**
155
+ * NIP-46 ping — signer の死活確認
156
+ */
157
+ public async ping(): Promise<boolean> {
158
+ return new Promise<boolean>((resolve, reject) => {
159
+ this.rpc.sendRequest(this.bunkerPubkey, 'ping', [], 24133, (response: RpcResponse) => {
160
+ if (response.error) {
161
+ reject(new Nip46Error(response.error, 'SIGNER_REJECTED'));
162
+ } else {
163
+ resolve(response.result === 'pong');
164
+ }
165
+ });
166
+ });
167
+ }
168
+
169
+ /**
170
+ * NIP-46 switch_relays — リレーリストの更新
171
+ * Spec: 接続確立後に client が即時送信、signer が relay リストを返す
172
+ */
173
+ public async switchRelays(): Promise<string[] | null> {
174
+ return new Promise<string[] | null>((resolve, reject) => {
175
+ this.rpc.sendRequest(this.bunkerPubkey, 'switch_relays', [], 24133, (response: RpcResponse) => {
176
+ if (response.error) {
177
+ reject(new Nip46Error(response.error, 'SIGNER_REJECTED'));
178
+ } else {
179
+ // result は JSON 文字列: ["wss://...", ...] または "null"
180
+ try {
181
+ const parsed = JSON.parse(response.result);
182
+ resolve(Array.isArray(parsed) ? parsed : null);
183
+ } catch {
184
+ resolve(null);
185
+ }
186
+ }
187
+ });
188
+ });
189
+ }
190
+
191
+ /**
192
+ * NIP-46 logout — リモート signer にセッション終了を通知
193
+ */
194
+ public async logout(): Promise<void> {
195
+ return new Promise<void>((resolve, reject) => {
196
+ this.rpc.sendRequest(this.bunkerPubkey, 'logout', [], 24133, (response: RpcResponse) => {
197
+ if (response.error) {
198
+ reject(new Nip46Error(response.error, 'SIGNER_REJECTED'));
199
+ } else {
200
+ resolve();
201
+ }
202
+ });
203
+ });
204
+ }
205
+
206
+ /**
207
+ * NIP-46 remote encrypt (NIP-44)
208
+ */
209
+ public async nip44Encrypt(recipientPubkey: string, plaintext: string): Promise<string> {
210
+ return new Promise<string>((resolve, reject) => {
211
+ this.rpc.sendRequest(this.bunkerPubkey, 'nip44_encrypt', [recipientPubkey, plaintext], 24133, (response: RpcResponse) => {
212
+ if (response.error) {
213
+ reject(new Nip46Error(response.error, 'SIGNER_REJECTED'));
214
+ } else {
215
+ resolve(response.result);
216
+ }
217
+ });
218
+ });
219
+ }
220
+
221
+ /**
222
+ * NIP-46 remote decrypt (NIP-44)
223
+ */
224
+ public async nip44Decrypt(senderPubkey: string, ciphertext: string): Promise<string> {
225
+ return new Promise<string>((resolve, reject) => {
226
+ this.rpc.sendRequest(this.bunkerPubkey, 'nip44_decrypt', [senderPubkey, ciphertext], 24133, (response: RpcResponse) => {
227
+ if (response.error) {
228
+ reject(new Nip46Error(response.error, 'SIGNER_REJECTED'));
229
+ } else {
230
+ resolve(response.result);
231
+ }
232
+ });
233
+ });
234
+ }
235
+
236
+ /**
237
+ * @deprecated NIP-46 spec から create_account は別 NIP へ移動済み。
238
+ * 将来的に削除されます。
239
+ */
154
240
  public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
241
+ console.warn('[DEPRECATED] createAccount2 is deprecated per NIP-46 spec. Will be removed in a future version.');
155
242
  const params = [name, domain, '', perms];
156
243
 
157
244
  const r = await new Promise<RpcResponse>((ok, err) => {
@@ -303,48 +303,6 @@ export const localStorageRemoveCurrentAccount = () => {
303
303
  localStorageRemoveItem(LOCAL_STORE_KEY);
304
304
  };
305
305
 
306
- export const localStorageRemoveAccount = (user: Info) => {
307
- if (!user) return;
308
-
309
- upgradeInfo(user);
310
-
311
- const current: Info | null = localStorageGetItem(LOCAL_STORE_KEY);
312
- if (current) upgradeInfo(current);
313
-
314
- const recentUser: RecentType = { ...user };
315
- delete (recentUser as Record<string, unknown>)['sk'];
316
- delete (recentUser as Record<string, unknown>)['otpData'];
317
-
318
- const loggedInAccounts: Info[] = localStorageGetItem(LOGGED_IN_ACCOUNTS) || [];
319
- const recentsAccounts: RecentType[] = localStorageGetItem(RECENT_ACCOUNTS) || [];
320
-
321
- loggedInAccounts.forEach(a => upgradeInfo(a));
322
- recentsAccounts.forEach(a => upgradeInfo(a));
323
-
324
- const accounts = loggedInAccounts.filter(el => el.pubkey !== user.pubkey || el.authMethod !== user.authMethod);
325
- const recents: RecentType[] = recentsAccounts;
326
-
327
- if (recentUser.authMethod === 'connect' && recentUser.bunkerUrl && recentUser.bunkerUrl.includes('secret=')) {
328
- console.log('nostr login bunker conn with a secret not saved to recent');
329
- } else if (recentUser.authMethod === 'local') {
330
- console.log('nostr login temporary local keys not save to recent');
331
- } else {
332
- const index = recentsAccounts.findIndex((el: RecentType) => el.pubkey === recentUser.pubkey && el.authMethod === recentUser.authMethod);
333
- if (index !== -1) {
334
- recents[index] = recentUser;
335
- } else {
336
- recents.push(recentUser);
337
- }
338
- }
339
-
340
- localStorageSetItem(RECENT_ACCOUNTS, JSON.stringify(recents));
341
- localStorageSetItem(LOGGED_IN_ACCOUNTS, JSON.stringify(accounts));
342
-
343
- if (current && current.pubkey === user.pubkey && current.authMethod === user.authMethod) {
344
- localStorageRemoveItem(LOCAL_STORE_KEY);
345
- }
346
- };
347
-
348
306
  export const localStorageRemoveRecent = (user: RecentType) => {
349
307
  const recentsAccounts: RecentType[] = localStorageGetItem(RECENT_ACCOUNTS) || [];
350
308
  recentsAccounts.forEach(a => upgradeInfo(a));