@konemono/nostr-login 1.11.0 → 1.11.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.
- package/dist/index.esm.js +25 -19
- package/dist/index.esm.js.map +1 -1
- package/dist/modules/AuthNostrService.d.ts +4 -14
- package/dist/modules/Nip46.d.ts +23 -52
- package/dist/modules/Signer.d.ts +6 -12
- package/dist/unpkg.js +25 -19
- package/dist/utils/index.d.ts +3 -6
- package/dist/utils/nip44.d.ts +3 -3
- package/package.json +8 -8
- package/src/modules/AuthNostrService.ts +103 -223
- package/src/modules/ModalManager.ts +2 -1
- package/src/modules/Nip46.ts +171 -270
- package/src/modules/NostrExtensionService.ts +0 -2
- package/src/modules/Signer.ts +13 -35
- package/src/utils/index.ts +22 -71
- package/src/utils/nip44.ts +8 -12
- package/test-relay-management.html +0 -407
package/src/modules/Nip46.ts
CHANGED
|
@@ -1,138 +1,136 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { validateEvent, verifyEvent, type Event as NostrEventSDK } from 'nostr-tools';
|
|
1
|
+
import NDK, { NDKEvent, NDKFilter, NDKNip46Signer, NDKNostrRpc, NDKRpcRequest, NDKRpcResponse, NDKSubscription, NDKSubscriptionCacheUsage, NostrEvent } from '@nostr-dev-kit/ndk';
|
|
2
|
+
import { validateEvent } from 'nostr-tools';
|
|
4
3
|
import { PrivateKeySigner } from './Signer';
|
|
5
4
|
import { NIP46_REQUEST_TIMEOUT, NIP46_CONNECT_TIMEOUT } from '../const';
|
|
6
5
|
|
|
6
|
+
// タイムアウト付きPromiseラッパー
|
|
7
7
|
function withTimeout<T>(promise: Promise<T>, timeoutMs: number, errorMessage: string): Promise<T> {
|
|
8
8
|
return Promise.race([promise, new Promise<T>((_, reject) => setTimeout(() => reject(new Error(errorMessage)), timeoutMs))]);
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* NDKのリレー接続を待機する
|
|
13
|
+
*/
|
|
14
|
+
export function waitForConnection(ndk: NDK, timeout: number): Promise<void> {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const start = Date.now();
|
|
17
|
+
|
|
18
|
+
const check = () => {
|
|
19
|
+
// 接続済みリレーがあるか確認
|
|
20
|
+
const connected = Array.from(ndk.pool.relays.values()).some(relay => relay.status === 1); // 1 = CONNECTED
|
|
21
|
+
|
|
22
|
+
if (connected) {
|
|
23
|
+
resolve();
|
|
24
|
+
} else if (Date.now() - start > timeout) {
|
|
25
|
+
reject(new Error(`接続タイムアウト: ${timeout}ms`));
|
|
26
|
+
} else {
|
|
27
|
+
setTimeout(check, 100);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
check();
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* NDKの接続を強制的に確立する
|
|
37
|
+
*/
|
|
38
|
+
export async function ensureNDKConnection(ndk: NDK, timeout: number): Promise<void> {
|
|
39
|
+
// connect()は冪等なので安全
|
|
40
|
+
await ndk.connect();
|
|
41
|
+
await waitForConnection(ndk, timeout);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* NIP-46専用リレーも含めて接続を確立する
|
|
46
|
+
*/
|
|
47
|
+
export async function ensureNIP46Connection(ndk: NDK, timeout: number): Promise<void> {
|
|
48
|
+
try {
|
|
49
|
+
await ensureNDKConnection(ndk, timeout);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.warn('Initial connection attempt failed, retrying with force disconnect...', e);
|
|
52
|
+
// 接続数0なら明示的に全切断して再試行
|
|
53
|
+
if (Array.from(ndk.pool.relays.values()).filter(r => r.status === 1).length === 0) {
|
|
54
|
+
ndk.pool.relays.forEach(relay => {
|
|
55
|
+
try {
|
|
56
|
+
relay.disconnect();
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error('Error disconnecting relay:', err);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
// タイムアウトを延長して再試行
|
|
62
|
+
await ensureNDKConnection(ndk, timeout * 2);
|
|
63
|
+
} else {
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// NIP-46用リレーが個別に定義されている場合の接続確認
|
|
69
|
+
// @ts-ignore
|
|
70
|
+
const signerRelays = ndk.signer?.relayUrls;
|
|
71
|
+
if (signerRelays && Array.isArray(signerRelays)) {
|
|
72
|
+
for (const url of signerRelays) {
|
|
73
|
+
const relay = ndk.pool.getRelay(url);
|
|
74
|
+
if (relay && relay.status !== 1) {
|
|
75
|
+
await relay.connect();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
66
78
|
}
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
|
|
80
|
+
// 最終チェック
|
|
81
|
+
const connectedRelays = Array.from(ndk.pool.relays.values()).filter(r => r.status === 1);
|
|
82
|
+
if (connectedRelays.length === 0) {
|
|
83
|
+
throw new Error('リレー接続に失敗しました');
|
|
69
84
|
}
|
|
70
85
|
}
|
|
71
86
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
public rxNostr: RxNostr;
|
|
87
|
+
class NostrRpc extends NDKNostrRpc {
|
|
88
|
+
protected _ndk: NDK;
|
|
75
89
|
protected _signer: PrivateKeySigner;
|
|
76
90
|
protected requests: Set<string> = new Set();
|
|
77
|
-
|
|
78
|
-
private sub?: RxReqAdapter;
|
|
91
|
+
private sub?: NDKSubscription;
|
|
79
92
|
protected _useNip44: boolean = false;
|
|
80
93
|
private reconnectAttempts: number = 0;
|
|
81
94
|
private maxReconnectAttempts: number = 3;
|
|
82
95
|
private reconnectDelay: number = 2000;
|
|
83
96
|
|
|
84
|
-
public constructor(
|
|
85
|
-
super();
|
|
86
|
-
this.
|
|
97
|
+
public constructor(ndk: NDK, signer: PrivateKeySigner) {
|
|
98
|
+
super(ndk, signer, ndk.debug.extend('nip46:signer:rpc'));
|
|
99
|
+
this._ndk = ndk;
|
|
87
100
|
this._signer = signer;
|
|
88
101
|
this.setupConnectionMonitoring();
|
|
89
102
|
}
|
|
90
103
|
|
|
91
|
-
public async subscribe(filter:
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
this.sub = new RxReqAdapter(this.rxNostr, [f]);
|
|
104
|
+
public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
|
|
105
|
+
// NOTE: fixing ndk
|
|
106
|
+
filter.kinds = filter.kinds?.filter(k => k === 24133);
|
|
107
|
+
this.sub = await super.subscribe(filter);
|
|
98
108
|
return this.sub;
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
public stop()
|
|
111
|
+
public stop() {
|
|
102
112
|
if (this.sub) {
|
|
103
113
|
this.sub.stop();
|
|
104
114
|
this.sub = undefined;
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
117
|
|
|
108
|
-
public setUseNip44(useNip44: boolean)
|
|
118
|
+
public setUseNip44(useNip44: boolean) {
|
|
109
119
|
this._useNip44 = useNip44;
|
|
110
120
|
}
|
|
111
121
|
|
|
112
|
-
private isNip04(ciphertext: string)
|
|
122
|
+
private isNip04(ciphertext: string) {
|
|
113
123
|
const l = ciphertext.length;
|
|
114
124
|
if (l < 28) return false;
|
|
115
125
|
return ciphertext[l - 28] === '?' && ciphertext[l - 27] === 'i' && ciphertext[l - 26] === 'v' && ciphertext[l - 25] === '=';
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
// override to auto-decrypt nip04/nip44
|
|
119
|
-
public async parseEvent(event:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// 2. Check content for NIP-04 pattern (?iv=...).
|
|
123
|
-
// 3. Call signer.decrypt(remoteUser, content) or signer.decryptNip44(remoteUser, content).
|
|
124
|
-
//
|
|
125
|
-
// New Implementation Plan:
|
|
126
|
-
// 1. Use direct NIP-04/NIP-44 decryption functions from local signer.
|
|
127
|
-
// 2. Pass event.pubkey (string) instead of NDKUser object.
|
|
128
|
-
|
|
129
|
-
// const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
|
|
130
|
-
// remoteUser.ndk = this._ndk;
|
|
131
|
-
|
|
129
|
+
public async parseEvent(event: NDKEvent): Promise<NDKRpcRequest | NDKRpcResponse> {
|
|
130
|
+
const remoteUser = this._ndk.getUser({ pubkey: event.pubkey });
|
|
131
|
+
remoteUser.ndk = this._ndk;
|
|
132
132
|
const decrypt = this.isNip04(event.content) ? this._signer.decrypt : this._signer.decryptNip44;
|
|
133
|
-
|
|
134
|
-
// NOTE: We need to adjust _signer.decrypt signature to accept string pubkey
|
|
135
|
-
const decryptedContent = await decrypt.call(this._signer, event.pubkey as any, event.content);
|
|
133
|
+
const decryptedContent = await decrypt.call(this._signer, remoteUser, event.content);
|
|
136
134
|
const parsedContent = JSON.parse(decryptedContent);
|
|
137
135
|
const { id, method, params, result, error } = parsedContent;
|
|
138
136
|
|
|
@@ -143,8 +141,8 @@ class NostrRpc extends EventEmitter {
|
|
|
143
141
|
}
|
|
144
142
|
}
|
|
145
143
|
|
|
146
|
-
public async parseNostrConnectReply(reply: any, secret: string)
|
|
147
|
-
const event =
|
|
144
|
+
public async parseNostrConnectReply(reply: any, secret: string) {
|
|
145
|
+
const event = new NDKEvent(this._ndk, reply);
|
|
148
146
|
const parsedEvent = await this.parseEvent(event);
|
|
149
147
|
console.log('nostr connect parsedEvent', parsedEvent);
|
|
150
148
|
if (!(parsedEvent as NDKRpcRequest).method) {
|
|
@@ -160,15 +158,14 @@ class NostrRpc extends EventEmitter {
|
|
|
160
158
|
// we just listed to an unsolicited reply to
|
|
161
159
|
// our pubkey and if it's ack/secret - we're fine
|
|
162
160
|
public async listen(nostrConnectSecret: string): Promise<string> {
|
|
163
|
-
// TODO: rx-nostrを使用して実装する
|
|
164
161
|
const pubkey = this._signer.pubkey;
|
|
165
162
|
console.log('nostr-login listening for conn to', pubkey);
|
|
166
163
|
const sub = await this.subscribe({
|
|
167
164
|
'kinds': [24133],
|
|
168
165
|
'#p': [pubkey],
|
|
169
|
-
}
|
|
166
|
+
});
|
|
170
167
|
return new Promise<string>((ok, err) => {
|
|
171
|
-
sub.on('event', async (event:
|
|
168
|
+
sub.on('event', async (event: NDKEvent) => {
|
|
172
169
|
try {
|
|
173
170
|
const parsedEvent = await this.parseEvent(event);
|
|
174
171
|
// console.log('ack parsedEvent', parsedEvent);
|
|
@@ -187,7 +184,7 @@ class NostrRpc extends EventEmitter {
|
|
|
187
184
|
}
|
|
188
185
|
}
|
|
189
186
|
} catch (e) {
|
|
190
|
-
console.log('error parsing event', e,
|
|
187
|
+
console.log('error parsing event', e, event.rawEvent());
|
|
191
188
|
}
|
|
192
189
|
// done
|
|
193
190
|
this.stop();
|
|
@@ -198,8 +195,7 @@ class NostrRpc extends EventEmitter {
|
|
|
198
195
|
// since ndk doesn't yet support perms param
|
|
199
196
|
// we reimplement the 'connect' call here
|
|
200
197
|
// instead of await signer.blockUntilReady();
|
|
201
|
-
public async connect(pubkey: string, token?: string, perms?: string)
|
|
202
|
-
// TODO: nostr-toolsを使用して実装する
|
|
198
|
+
public async connect(pubkey: string, token?: string, perms?: string) {
|
|
203
199
|
return new Promise<void>((ok, err) => {
|
|
204
200
|
const connectParams = [pubkey!, token || '', perms || ''];
|
|
205
201
|
this.sendRequest(pubkey!, 'connect', connectParams, 24133, (response: NDKRpcResponse) => {
|
|
@@ -241,7 +237,7 @@ class NostrRpc extends EventEmitter {
|
|
|
241
237
|
}
|
|
242
238
|
|
|
243
239
|
// 接続監視のセットアップ
|
|
244
|
-
private setupConnectionMonitoring()
|
|
240
|
+
private setupConnectionMonitoring() {
|
|
245
241
|
// アプリがフォアグラウンドに戻ったときの処理
|
|
246
242
|
if (typeof document !== 'undefined') {
|
|
247
243
|
document.addEventListener('visibilitychange', async () => {
|
|
@@ -259,22 +255,11 @@ class NostrRpc extends EventEmitter {
|
|
|
259
255
|
await this.reconnect();
|
|
260
256
|
});
|
|
261
257
|
}
|
|
262
|
-
|
|
263
|
-
// 定期的な接続状態チェック(オプション)
|
|
264
|
-
setInterval(() => {
|
|
265
|
-
const stats = this.rxNostr.getAllRelayStatus();
|
|
266
|
-
const connected = Object.values(stats).filter(s => String(s) === 'connected').length;
|
|
267
|
-
if (connected === 0) {
|
|
268
|
-
console.warn('No relays connected, triggering reconnection...');
|
|
269
|
-
this.ensureConnected();
|
|
270
|
-
}
|
|
271
|
-
}, 60000); // 60秒ごとにチェック
|
|
272
258
|
}
|
|
273
259
|
|
|
274
260
|
// 接続を確認して必要なら再接続
|
|
275
261
|
private async ensureConnected(): Promise<void> {
|
|
276
|
-
const
|
|
277
|
-
const connectedRelays = Object.values(states).filter(s => String(s) === 'connected');
|
|
262
|
+
const connectedRelays = Array.from(this._ndk.pool.relays.values()).filter(r => r.status === 1); // 1 = CONNECTED
|
|
278
263
|
|
|
279
264
|
if (connectedRelays.length === 0) {
|
|
280
265
|
console.log('No connected relays, attempting reconnection...');
|
|
@@ -282,7 +267,7 @@ class NostrRpc extends EventEmitter {
|
|
|
282
267
|
}
|
|
283
268
|
}
|
|
284
269
|
|
|
285
|
-
//
|
|
270
|
+
// 再接続処理
|
|
286
271
|
protected async reconnect(): Promise<void> {
|
|
287
272
|
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
288
273
|
console.error('Max reconnection attempts reached');
|
|
@@ -299,42 +284,25 @@ class NostrRpc extends EventEmitter {
|
|
|
299
284
|
this.sub.stop();
|
|
300
285
|
}
|
|
301
286
|
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const relays = Object.keys(this.rxNostr.getAllRelayStatus());
|
|
312
|
-
if (relays.length > 0) {
|
|
313
|
-
for (const relay of relays) {
|
|
314
|
-
try {
|
|
315
|
-
this.rxNostr.reconnect(relay);
|
|
316
|
-
console.log(`Reconnected to ${relay}`);
|
|
317
|
-
} catch (error) {
|
|
318
|
-
console.warn(`Failed to reconnect to ${relay}:`, error);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
287
|
+
// 少し待ってから再接続
|
|
288
|
+
await new Promise(resolve => setTimeout(resolve, this.reconnectDelay));
|
|
289
|
+
|
|
290
|
+
// 再接続
|
|
291
|
+
await this._ndk.connect();
|
|
292
|
+
|
|
293
|
+
// サブスクリプションを再開
|
|
294
|
+
if (this.sub) {
|
|
295
|
+
await this.sub.start();
|
|
321
296
|
}
|
|
322
297
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
}
|
|
332
|
-
}, 2000);
|
|
333
|
-
} catch (error) {
|
|
334
|
-
console.error('Reconnection error:', error);
|
|
335
|
-
this.emit('reconnectError', error);
|
|
336
|
-
// 次の再接続を試みる
|
|
337
|
-
setTimeout(() => this.reconnect(), this.reconnectDelay);
|
|
298
|
+
this.reconnectAttempts = 0;
|
|
299
|
+
this.emit('reconnected');
|
|
300
|
+
console.log('Successfully reconnected to relays');
|
|
301
|
+
} catch (e) {
|
|
302
|
+
console.error('Reconnection failed:', e);
|
|
303
|
+
|
|
304
|
+
// リトライ
|
|
305
|
+
setTimeout(() => this.reconnect(), this.reconnectDelay * this.reconnectAttempts);
|
|
338
306
|
}
|
|
339
307
|
}
|
|
340
308
|
|
|
@@ -343,6 +311,9 @@ class NostrRpc extends EventEmitter {
|
|
|
343
311
|
}
|
|
344
312
|
|
|
345
313
|
public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
|
|
314
|
+
// リクエスト送信前に接続を確認
|
|
315
|
+
await ensureNIP46Connection(this._ndk, 5000);
|
|
316
|
+
|
|
346
317
|
const id = this.getId();
|
|
347
318
|
|
|
348
319
|
// response handler will deduplicate auth urls and responses
|
|
@@ -353,11 +324,7 @@ class NostrRpc extends EventEmitter {
|
|
|
353
324
|
console.log('sendRequest', { event, method, remotePubkey, params });
|
|
354
325
|
|
|
355
326
|
// send to relays
|
|
356
|
-
|
|
357
|
-
this.rxNostr.send(event).subscribe({
|
|
358
|
-
error: (err) => console.error('Nip46 publish error', err),
|
|
359
|
-
complete: () => console.log('Nip46 publish complete')
|
|
360
|
-
});
|
|
327
|
+
await event.publish();
|
|
361
328
|
|
|
362
329
|
// NOTE: ndk returns a promise that never resolves and
|
|
363
330
|
// in fact REQUIRES cb to be provided (otherwise no way
|
|
@@ -368,8 +335,7 @@ class NostrRpc extends EventEmitter {
|
|
|
368
335
|
return undefined as NDKRpcResponse;
|
|
369
336
|
}
|
|
370
337
|
|
|
371
|
-
protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void)
|
|
372
|
-
// TODO: 再実装する
|
|
338
|
+
protected setResponseHandler(id: string, cb?: (res: NDKRpcResponse) => void) {
|
|
373
339
|
let authUrlSent = false;
|
|
374
340
|
const now = Date.now();
|
|
375
341
|
return new Promise<NDKRpcResponse>(() => {
|
|
@@ -393,43 +359,22 @@ class NostrRpc extends EventEmitter {
|
|
|
393
359
|
});
|
|
394
360
|
}
|
|
395
361
|
|
|
396
|
-
protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133)
|
|
397
|
-
// TODO: 元のNDKロジック:
|
|
398
|
-
// const event = new NDKEvent(this._ndk, { ... });
|
|
399
|
-
// await event.sign(this._signer);
|
|
400
|
-
//
|
|
401
|
-
// New Implementation Plan:
|
|
402
|
-
// 1. Create a plain event object:
|
|
403
|
-
// { kind, content, tags, pubkey, created_at: Math.floor(Date.now()/1000) }
|
|
404
|
-
// 2. Encrypt content:
|
|
405
|
-
// if (useNip44) await signer.encryptNip44(remotePubkey, content)
|
|
406
|
-
// else await signer.encrypt(remotePubkey, content)
|
|
407
|
-
// 3. Sign:
|
|
408
|
-
// event.id = getEventHash(event)
|
|
409
|
-
// event.sig = await signer.signEvent(event)
|
|
410
|
-
// 4. Return the signed event object.
|
|
411
|
-
|
|
362
|
+
protected async createRequestEvent(id: string, remotePubkey: string, method: string, params: string[] = [], kind = 24133) {
|
|
412
363
|
this.requests.add(id);
|
|
413
|
-
const localUser = await this._signer.user();
|
|
414
|
-
|
|
364
|
+
const localUser = await this._signer.user();
|
|
365
|
+
const remoteUser = this._ndk.getUser({ pubkey: remotePubkey });
|
|
415
366
|
const request = { id, method, params };
|
|
416
367
|
|
|
417
|
-
|
|
418
|
-
const event = {
|
|
368
|
+
const event = new NDKEvent(this._ndk, {
|
|
419
369
|
kind,
|
|
420
370
|
content: JSON.stringify(request),
|
|
421
371
|
tags: [['p', remotePubkey]],
|
|
422
372
|
pubkey: localUser.pubkey,
|
|
423
|
-
|
|
424
|
-
} as any;
|
|
373
|
+
} as NostrEvent);
|
|
425
374
|
|
|
426
375
|
const useNip44 = this._useNip44 && method !== 'create_account';
|
|
427
376
|
const encrypt = useNip44 ? this._signer.encryptNip44 : this._signer.encrypt;
|
|
428
|
-
|
|
429
|
-
// NOTE: Adjust encrypt to accept string pubkey
|
|
430
|
-
event.content = await encrypt.call(this._signer, remotePubkey as any, event.content);
|
|
431
|
-
|
|
432
|
-
// NOTE: This assumes _signer has a sign() method compatible with the event
|
|
377
|
+
event.content = await encrypt.call(this._signer, remoteUser, event.content);
|
|
433
378
|
await event.sign(this._signer);
|
|
434
379
|
|
|
435
380
|
return event;
|
|
@@ -444,29 +389,27 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
444
389
|
private lastResponseTime: number = Date.now();
|
|
445
390
|
private heartbeatTimeoutMs: number = 30000; // 30秒応答がなければ再接続
|
|
446
391
|
|
|
447
|
-
public constructor(
|
|
448
|
-
super(
|
|
392
|
+
public constructor(ndk: NDK, localSigner: PrivateKeySigner, iframePeerOrigin?: string) {
|
|
393
|
+
super(ndk, localSigner);
|
|
394
|
+
this._ndk = ndk;
|
|
449
395
|
this.peerOrigin = iframePeerOrigin;
|
|
450
396
|
}
|
|
451
397
|
|
|
452
|
-
public async subscribe(filter:
|
|
398
|
+
public async subscribe(filter: NDKFilter): Promise<NDKSubscription> {
|
|
453
399
|
if (!this.peerOrigin) return super.subscribe(filter);
|
|
454
|
-
return super.subscribe(filter);
|
|
455
|
-
/*
|
|
456
400
|
return new NDKSubscription(
|
|
457
401
|
this._ndk,
|
|
458
402
|
{},
|
|
459
403
|
{
|
|
460
404
|
// don't send to relay
|
|
461
405
|
closeOnEose: true,
|
|
462
|
-
cacheUsage:
|
|
406
|
+
cacheUsage: NDKSubscriptionCacheUsage.ONLY_CACHE,
|
|
463
407
|
},
|
|
464
408
|
);
|
|
465
|
-
*/
|
|
466
409
|
}
|
|
467
410
|
|
|
468
411
|
// ハートビート開始
|
|
469
|
-
private startHeartbeat()
|
|
412
|
+
private startHeartbeat() {
|
|
470
413
|
this.stopHeartbeat();
|
|
471
414
|
|
|
472
415
|
this.heartbeatInterval = window.setInterval(async () => {
|
|
@@ -479,14 +422,14 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
479
422
|
}, 10000); // 10秒ごとにチェック
|
|
480
423
|
}
|
|
481
424
|
|
|
482
|
-
private stopHeartbeat()
|
|
425
|
+
private stopHeartbeat() {
|
|
483
426
|
if (this.heartbeatInterval) {
|
|
484
427
|
clearInterval(this.heartbeatInterval);
|
|
485
428
|
this.heartbeatInterval = undefined;
|
|
486
429
|
}
|
|
487
430
|
}
|
|
488
431
|
|
|
489
|
-
public setWorkerIframePort(port: MessagePort)
|
|
432
|
+
public setWorkerIframePort(port: MessagePort) {
|
|
490
433
|
if (!this.peerOrigin) throw new Error('Unexpected iframe port');
|
|
491
434
|
|
|
492
435
|
this.iframePort = port;
|
|
@@ -512,8 +455,7 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
512
455
|
const event = ev.data;
|
|
513
456
|
|
|
514
457
|
if (!validateEvent(event)) throw new Error('Invalid event from iframe');
|
|
515
|
-
|
|
516
|
-
const nevent = event as NostrEvent;
|
|
458
|
+
const nevent = new NDKEvent(this._ndk, event);
|
|
517
459
|
const parsedEvent = await this.parseEvent(nevent);
|
|
518
460
|
// レスポンス受信時にタイムスタンプを更新
|
|
519
461
|
this.lastResponseTime = Date.now();
|
|
@@ -529,27 +471,33 @@ export class IframeNostrRpc extends NostrRpc {
|
|
|
529
471
|
}
|
|
530
472
|
|
|
531
473
|
public async sendRequest(remotePubkey: string, method: string, params: string[] = [], kind = 24133, cb?: (res: NDKRpcResponse) => void): Promise<NDKRpcResponse> {
|
|
474
|
+
// リクエスト送信前に接続を確認 (relayを使用する場合に備えて)
|
|
475
|
+
if (!this.iframePort) {
|
|
476
|
+
await ensureNIP46Connection(this._ndk, 5000);
|
|
477
|
+
}
|
|
478
|
+
|
|
532
479
|
const id = this.getId();
|
|
533
480
|
|
|
534
481
|
// create and sign request event
|
|
535
482
|
const event = await this.createRequestEvent(id, remotePubkey, method, params, kind);
|
|
536
483
|
|
|
537
|
-
// set response handler
|
|
484
|
+
// set response handler, it will dedup auth urls,
|
|
485
|
+
// and also dedup response handlers - we're sending
|
|
486
|
+
// to relays and to iframe
|
|
538
487
|
this.setResponseHandler(id, cb);
|
|
539
488
|
|
|
540
489
|
if (this.iframePort) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
490
|
+
// map request event id to request id, if iframe
|
|
491
|
+
// has no key it will reply with error:event_id (it can't
|
|
492
|
+
// decrypt the request id without keys)
|
|
493
|
+
this.iframeRequests.set(event.id, { id, pubkey: remotePubkey });
|
|
494
|
+
|
|
495
|
+
// send to iframe
|
|
496
|
+
console.log('iframe-nip46 sending request to', this.peerOrigin, event.rawEvent());
|
|
497
|
+
this.iframePort.postMessage(event.rawEvent());
|
|
544
498
|
} else {
|
|
545
|
-
// send to relays
|
|
546
|
-
|
|
547
|
-
next: (packet) => {
|
|
548
|
-
console.log('Nip46 request published to', packet.from);
|
|
549
|
-
},
|
|
550
|
-
error: (err) => console.error('Nip46 publish error', err),
|
|
551
|
-
complete: () => console.log('Nip46 publish complete')
|
|
552
|
-
});
|
|
499
|
+
// send to relays
|
|
500
|
+
await event.publish();
|
|
553
501
|
}
|
|
554
502
|
|
|
555
503
|
// see notes in 'super'
|
|
@@ -607,20 +555,15 @@ export class ReadyListener {
|
|
|
607
555
|
}
|
|
608
556
|
}
|
|
609
557
|
|
|
610
|
-
|
|
611
|
-
export class Nip46Signer extends EventEmitter {
|
|
612
|
-
public remotePubkey: string = '';
|
|
613
|
-
// @ts-ignore
|
|
614
|
-
public rpc: NostrRpc;
|
|
558
|
+
export class Nip46Signer extends NDKNip46Signer {
|
|
615
559
|
private _userPubkey: string = '';
|
|
616
560
|
private _rpc: IframeNostrRpc;
|
|
617
561
|
|
|
618
|
-
constructor(
|
|
619
|
-
super();
|
|
620
|
-
// super(ndk, signerPubkey, localSigner);
|
|
562
|
+
constructor(ndk: NDK, localSigner: PrivateKeySigner, signerPubkey: string, iframeOrigin?: string) {
|
|
563
|
+
super(ndk, signerPubkey, localSigner);
|
|
621
564
|
|
|
622
565
|
// override with our own rpc implementation
|
|
623
|
-
this._rpc = new IframeNostrRpc(
|
|
566
|
+
this._rpc = new IframeNostrRpc(ndk, localSigner, iframeOrigin);
|
|
624
567
|
this._rpc.setUseNip44(true); // !!this.params.optionsModal.dev);
|
|
625
568
|
this._rpc.on('authUrl', (url: string) => {
|
|
626
569
|
this.emit('authUrl', url);
|
|
@@ -629,11 +572,11 @@ export class Nip46Signer extends EventEmitter {
|
|
|
629
572
|
this.rpc = this._rpc;
|
|
630
573
|
}
|
|
631
574
|
|
|
632
|
-
get userPubkey()
|
|
575
|
+
get userPubkey() {
|
|
633
576
|
return this._userPubkey;
|
|
634
577
|
}
|
|
635
578
|
|
|
636
|
-
private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false)
|
|
579
|
+
private async setSignerPubkey(signerPubkey: string, sameAsUser: boolean = false) {
|
|
637
580
|
console.log('setSignerPubkey', signerPubkey);
|
|
638
581
|
|
|
639
582
|
// ensure it's set
|
|
@@ -648,7 +591,7 @@ export class Nip46Signer extends EventEmitter {
|
|
|
648
591
|
await this.initUserPubkey(sameAsUser ? signerPubkey : '');
|
|
649
592
|
}
|
|
650
593
|
|
|
651
|
-
public async initUserPubkey(hintPubkey?: string)
|
|
594
|
+
public async initUserPubkey(hintPubkey?: string) {
|
|
652
595
|
if (this._userPubkey) throw new Error('Already called initUserPubkey');
|
|
653
596
|
|
|
654
597
|
if (hintPubkey) {
|
|
@@ -665,7 +608,7 @@ export class Nip46Signer extends EventEmitter {
|
|
|
665
608
|
if (response.error) {
|
|
666
609
|
err(new Error(response.error));
|
|
667
610
|
} else {
|
|
668
|
-
ok(response.result
|
|
611
|
+
ok(response.result);
|
|
669
612
|
}
|
|
670
613
|
});
|
|
671
614
|
}),
|
|
@@ -674,23 +617,23 @@ export class Nip46Signer extends EventEmitter {
|
|
|
674
617
|
);
|
|
675
618
|
}
|
|
676
619
|
|
|
677
|
-
public async listen(nostrConnectSecret: string)
|
|
620
|
+
public async listen(nostrConnectSecret: string) {
|
|
678
621
|
const signerPubkey = await (this.rpc as IframeNostrRpc).listen(nostrConnectSecret);
|
|
679
622
|
await this.setSignerPubkey(signerPubkey);
|
|
680
623
|
}
|
|
681
624
|
|
|
682
|
-
public async connect(token?: string, perms?: string)
|
|
625
|
+
public async connect(token?: string, perms?: string) {
|
|
683
626
|
if (!this.remotePubkey) throw new Error('No signer pubkey');
|
|
684
627
|
await (this._rpc as any).connectWithTimeout(this.remotePubkey, token, perms, NIP46_CONNECT_TIMEOUT);
|
|
685
628
|
await this.setSignerPubkey(this.remotePubkey);
|
|
686
629
|
}
|
|
687
630
|
|
|
688
|
-
public async setListenReply(reply: any, nostrConnectSecret: string)
|
|
631
|
+
public async setListenReply(reply: any, nostrConnectSecret: string) {
|
|
689
632
|
const signerPubkey = await this._rpc.parseNostrConnectReply(reply, nostrConnectSecret);
|
|
690
633
|
await this.setSignerPubkey(signerPubkey, true);
|
|
691
634
|
}
|
|
692
635
|
|
|
693
|
-
public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string })
|
|
636
|
+
public async createAccount2({ bunkerPubkey, name, domain, perms = '' }: { bunkerPubkey: string; name: string; domain: string; perms?: string }) {
|
|
694
637
|
const params = [
|
|
695
638
|
name,
|
|
696
639
|
domain,
|
|
@@ -703,52 +646,10 @@ export class Nip46Signer extends EventEmitter {
|
|
|
703
646
|
});
|
|
704
647
|
|
|
705
648
|
console.log('create_account pubkey', r);
|
|
706
|
-
if (r.result === 'error'
|
|
707
|
-
throw new Error(r.error
|
|
649
|
+
if (r.result === 'error') {
|
|
650
|
+
throw new Error(r.error);
|
|
708
651
|
}
|
|
709
652
|
|
|
710
653
|
return r.result;
|
|
711
654
|
}
|
|
712
|
-
// TODO: 必要であればNDKNip46Signerから不足しているメソッドを実装する
|
|
713
|
-
public async encrypt(recipient: any, value: string): Promise<string> {
|
|
714
|
-
const recipientPubkey = typeof recipient === 'string' ? recipient : recipient.pubkey;
|
|
715
|
-
return await this.rpcSend('nip04_encrypt', [recipientPubkey, value]);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
public async decrypt(sender: any, value: string): Promise<string> {
|
|
719
|
-
const senderPubkey = typeof sender === 'string' ? sender : sender.pubkey;
|
|
720
|
-
return await this.rpcSend('nip04_decrypt', [senderPubkey, value]);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
public async encryptNip44(recipient: any, value: string): Promise<string> {
|
|
724
|
-
const recipientPubkey = typeof recipient === 'string' ? recipient : recipient.pubkey;
|
|
725
|
-
return await this.rpcSend('nip44_encrypt', [recipientPubkey, value]);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
public async decryptNip44(sender: any, value: string): Promise<string> {
|
|
729
|
-
const senderPubkey = typeof sender === 'string' ? sender : sender.pubkey;
|
|
730
|
-
return await this.rpcSend('nip44_decrypt', [senderPubkey, value]);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
public async sign(event: any): Promise<string> {
|
|
734
|
-
const eventString = JSON.stringify(event);
|
|
735
|
-
const res = await this.rpcSend('sign_event', [eventString]);
|
|
736
|
-
// The result matches NIP-46 sign_event response which is the signed event (stringified json)
|
|
737
|
-
const signedEvent = JSON.parse(res);
|
|
738
|
-
event.sig = signedEvent.sig;
|
|
739
|
-
return signedEvent.sig;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
public async user(): Promise<any> {
|
|
743
|
-
return { pubkey: this.userPubkey };
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
private async rpcSend(method: string, params: any[]): Promise<string> {
|
|
747
|
-
return new Promise<string>((resolve, reject) => {
|
|
748
|
-
this.rpc.sendRequest(this.remotePubkey, method, params, 24133, (response: any) => {
|
|
749
|
-
if (response.error) reject(new Error(response.error));
|
|
750
|
-
else resolve(response.result);
|
|
751
|
-
});
|
|
752
|
-
});
|
|
753
|
-
}
|
|
754
655
|
}
|