applesauce-signers 2.0.0 → 3.0.0
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/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/nostr-connect.d.ts +92 -0
- package/dist/helpers/nostr-connect.js +93 -0
- package/dist/helpers/observable.d.ts +11 -0
- package/dist/helpers/observable.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/interface.d.ts +15 -0
- package/dist/signers/amber-clipboard-signer.d.ts +6 -6
- package/dist/signers/extension-signer.d.ts +9 -16
- package/dist/signers/extension-signer.js +9 -20
- package/dist/signers/index.d.ts +1 -0
- package/dist/signers/index.js +1 -0
- package/dist/signers/nostr-connect-provider.d.ts +128 -0
- package/dist/signers/nostr-connect-provider.js +406 -0
- package/dist/signers/nostr-connect-signer.d.ts +26 -89
- package/dist/signers/nostr-connect-signer.js +37 -66
- package/dist/signers/password-signer.d.ts +6 -6
- package/dist/signers/password-signer.js +8 -3
- package/dist/signers/readonly-signer.d.ts +12 -13
- package/dist/signers/readonly-signer.js +6 -9
- package/dist/signers/serial-port-signer.d.ts +4 -4
- package/dist/signers/simple-signer.d.ts +2 -1
- package/package.json +6 -3
- package/dist/nip-07.d.ts +0 -20
- /package/dist/{nip-07.js → interface.js} +0 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import { logger } from "applesauce-core";
|
|
2
|
+
import { getEncryptedContentEncryptionMethods, getHiddenContent, isEvent, unixNow, unlockHiddenContent, } from "applesauce-core/helpers";
|
|
3
|
+
import { createDefer } from "applesauce-core/promise";
|
|
4
|
+
import { kinds, verifyEvent } from "nostr-tools";
|
|
5
|
+
import { nanoid } from "nanoid";
|
|
6
|
+
import { isNIP04 } from "../helpers/encryption.js";
|
|
7
|
+
import { createBunkerURI, NostrConnectMethod, parseNostrConnectURI, } from "../helpers/nostr-connect.js";
|
|
8
|
+
import { SimpleSigner } from "./simple-signer.js";
|
|
9
|
+
export class NostrConnectProvider {
|
|
10
|
+
/** A fallback method to use for subscriptionMethod if none is passed in when creating the provider */
|
|
11
|
+
static subscriptionMethod = undefined;
|
|
12
|
+
/** A fallback method to use for publishMethod if none is passed in when creating the provider */
|
|
13
|
+
static publishMethod = undefined;
|
|
14
|
+
/** A fallback pool to use if none is pass in when creating the provider */
|
|
15
|
+
static pool = undefined;
|
|
16
|
+
/** A method that is called when an event needs to be published */
|
|
17
|
+
publishMethod;
|
|
18
|
+
/** The active nostr subscription */
|
|
19
|
+
subscriptionMethod;
|
|
20
|
+
/** Internal logger */
|
|
21
|
+
log = logger.extend("NostrConnectProvider");
|
|
22
|
+
/** A set of nostr requests that have been seen */
|
|
23
|
+
seen = new Set();
|
|
24
|
+
/** The main signer for the actual signing operations */
|
|
25
|
+
upstream;
|
|
26
|
+
/** The identity signer (provider's identity) */
|
|
27
|
+
signer;
|
|
28
|
+
/** Whether the provider is listening for events */
|
|
29
|
+
listening = false;
|
|
30
|
+
/** The connected client's public key */
|
|
31
|
+
client;
|
|
32
|
+
/** The secret used to authorize clients to connect */
|
|
33
|
+
secret;
|
|
34
|
+
/** Relays to communicate over */
|
|
35
|
+
relays;
|
|
36
|
+
/** Whether a client is connected (received a `connect` request) */
|
|
37
|
+
connected = false;
|
|
38
|
+
/** Callbacks */
|
|
39
|
+
onClientConnect;
|
|
40
|
+
onClientDisconnect;
|
|
41
|
+
/** A method used to accept or reject `connect` requests */
|
|
42
|
+
onConnect;
|
|
43
|
+
/** A method used to accept or reject `sign_event` requests */
|
|
44
|
+
onSignEvent;
|
|
45
|
+
/** A method used to accept or reject `nip04_encrypt` requests */
|
|
46
|
+
onNip04Encrypt;
|
|
47
|
+
/** A method used to accept or reject `nip04_decrypt` requests */
|
|
48
|
+
onNip04Decrypt;
|
|
49
|
+
/** A method used to accept or reject `nip44_encrypt` requests */
|
|
50
|
+
onNip44Encrypt;
|
|
51
|
+
/** A method used to accept or reject `nip44_decrypt` requests */
|
|
52
|
+
onNip44Decrypt;
|
|
53
|
+
constructor(opts) {
|
|
54
|
+
this.relays = opts.relays;
|
|
55
|
+
this.upstream = opts.upstream;
|
|
56
|
+
this.signer = opts.signer ?? new SimpleSigner();
|
|
57
|
+
this.secret = opts.secret;
|
|
58
|
+
// Get the subscription and publish methods
|
|
59
|
+
const subscriptionMethod = opts.subscriptionMethod ||
|
|
60
|
+
opts.pool?.subscription ||
|
|
61
|
+
NostrConnectProvider.subscriptionMethod ||
|
|
62
|
+
NostrConnectProvider.pool?.subscription;
|
|
63
|
+
if (!subscriptionMethod)
|
|
64
|
+
throw new Error("Missing subscriptionMethod, either pass a method or set NostrConnectProvider.subscriptionMethod");
|
|
65
|
+
const publishMethod = opts.publishMethod ||
|
|
66
|
+
opts.pool?.publish ||
|
|
67
|
+
NostrConnectProvider.publishMethod ||
|
|
68
|
+
NostrConnectProvider.pool?.publish;
|
|
69
|
+
if (!publishMethod)
|
|
70
|
+
throw new Error("Missing publishMethod, either pass a method or set NostrConnectProvider.publishMethod");
|
|
71
|
+
// Use arrow functions so "this" isn't bound to the signer
|
|
72
|
+
this.subscriptionMethod = (relays, filters) => subscriptionMethod(relays, filters);
|
|
73
|
+
this.publishMethod = (relays, event) => publishMethod(relays, event);
|
|
74
|
+
// Set client connection callbacks
|
|
75
|
+
if (opts.onClientConnect)
|
|
76
|
+
this.onClientConnect = opts.onClientConnect;
|
|
77
|
+
if (opts.onClientDisconnect)
|
|
78
|
+
this.onClientDisconnect = opts.onClientDisconnect;
|
|
79
|
+
// Set authorization callbacks
|
|
80
|
+
if (opts.onConnect)
|
|
81
|
+
this.onConnect = opts.onConnect;
|
|
82
|
+
if (opts.onSignEvent)
|
|
83
|
+
this.onSignEvent = opts.onSignEvent;
|
|
84
|
+
if (opts.onNip04Encrypt)
|
|
85
|
+
this.onNip04Encrypt = opts.onNip04Encrypt;
|
|
86
|
+
if (opts.onNip04Decrypt)
|
|
87
|
+
this.onNip04Decrypt = opts.onNip04Decrypt;
|
|
88
|
+
if (opts.onNip44Encrypt)
|
|
89
|
+
this.onNip44Encrypt = opts.onNip44Encrypt;
|
|
90
|
+
if (opts.onNip44Decrypt)
|
|
91
|
+
this.onNip44Decrypt = opts.onNip44Decrypt;
|
|
92
|
+
}
|
|
93
|
+
/** The currently active REQ subscription */
|
|
94
|
+
req;
|
|
95
|
+
/** Updates the relay subscription to listen for request events */
|
|
96
|
+
async updateSubscription() {
|
|
97
|
+
if (this.req)
|
|
98
|
+
this.req.unsubscribe();
|
|
99
|
+
const pubkey = await this.signer.getPublicKey();
|
|
100
|
+
// Setup subscription to listen for requests
|
|
101
|
+
this.req = this.subscriptionMethod(this.relays, [
|
|
102
|
+
// If client is known, only listen for requests from the client
|
|
103
|
+
this.client
|
|
104
|
+
? {
|
|
105
|
+
kinds: [kinds.NostrConnect],
|
|
106
|
+
"#p": [pubkey],
|
|
107
|
+
authors: [this.client],
|
|
108
|
+
}
|
|
109
|
+
: // Otherwise listen for all requests (waiting for a `connect` request)
|
|
110
|
+
{
|
|
111
|
+
kinds: [kinds.NostrConnect],
|
|
112
|
+
"#p": [pubkey],
|
|
113
|
+
},
|
|
114
|
+
]).subscribe({
|
|
115
|
+
next: (event) => typeof event !== "string" && this.handleEvent(event),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Start the provider
|
|
120
|
+
* @param request - An inital `connect` request to respond to or a {@link NostrConnectURI}
|
|
121
|
+
*/
|
|
122
|
+
async start(request) {
|
|
123
|
+
if (this.listening)
|
|
124
|
+
return;
|
|
125
|
+
this.listening = true;
|
|
126
|
+
// Handle first request if provided (e.g. from a `connect` request)
|
|
127
|
+
if (isEvent(request))
|
|
128
|
+
await this.handleEvent(request);
|
|
129
|
+
// Handle NostrConnectURI
|
|
130
|
+
else if (request)
|
|
131
|
+
await this.handleNostrConnectURI(request);
|
|
132
|
+
// Start the subscription (if its not already started)
|
|
133
|
+
if (!this.req)
|
|
134
|
+
await this.updateSubscription();
|
|
135
|
+
this.log("Started", this.relays);
|
|
136
|
+
}
|
|
137
|
+
/** Stop the provider */
|
|
138
|
+
async stop() {
|
|
139
|
+
this.listening = false;
|
|
140
|
+
// Close the current subscription
|
|
141
|
+
if (this.req) {
|
|
142
|
+
this.req.unsubscribe();
|
|
143
|
+
this.req = undefined;
|
|
144
|
+
}
|
|
145
|
+
// Cancel waiting promise
|
|
146
|
+
if (this.waitingPromise) {
|
|
147
|
+
this.waitingPromise.reject(new Error("Closed"));
|
|
148
|
+
this.waitingPromise = null;
|
|
149
|
+
}
|
|
150
|
+
// Notify client disconnect
|
|
151
|
+
if (this.client && this.connected && this.onClientDisconnect)
|
|
152
|
+
this.onClientDisconnect(this.client);
|
|
153
|
+
// Forget all seen requests
|
|
154
|
+
this.seen.clear();
|
|
155
|
+
this.client = undefined;
|
|
156
|
+
this.connected = false;
|
|
157
|
+
this.log("Stopped");
|
|
158
|
+
}
|
|
159
|
+
waitingPromise = null;
|
|
160
|
+
/** Wait for a client to connect */
|
|
161
|
+
waitForClient(abort) {
|
|
162
|
+
if (this.client)
|
|
163
|
+
return Promise.resolve(this.client);
|
|
164
|
+
this.start();
|
|
165
|
+
this.waitingPromise = createDefer();
|
|
166
|
+
abort?.addEventListener("abort", () => {
|
|
167
|
+
this.waitingPromise?.reject(new Error("Aborted"));
|
|
168
|
+
this.waitingPromise = null;
|
|
169
|
+
this.stop();
|
|
170
|
+
}, true);
|
|
171
|
+
return this.waitingPromise;
|
|
172
|
+
}
|
|
173
|
+
/** Call this method with incoming events */
|
|
174
|
+
async handleEvent(event) {
|
|
175
|
+
if (!verifyEvent(event))
|
|
176
|
+
return;
|
|
177
|
+
// Do nothing if this request has already been seen
|
|
178
|
+
if (this.seen.has(event.id))
|
|
179
|
+
return;
|
|
180
|
+
this.seen.add(event.id);
|
|
181
|
+
try {
|
|
182
|
+
const content = getHiddenContent(event) ||
|
|
183
|
+
// Support legacy NIP-04 encryption
|
|
184
|
+
(isNIP04(event.content)
|
|
185
|
+
? await unlockHiddenContent(event, this.signer, "nip04")
|
|
186
|
+
: await unlockHiddenContent(event, this.signer, "nip44"));
|
|
187
|
+
const request = JSON.parse(content);
|
|
188
|
+
// If the client isn't known, reject the request
|
|
189
|
+
if (!this.client && request.method !== NostrConnectMethod.Connect)
|
|
190
|
+
throw new Error("Received request from unknown client");
|
|
191
|
+
else if (this.client && event.pubkey !== this.client)
|
|
192
|
+
throw new Error("Received request from wrong client");
|
|
193
|
+
// Process the request
|
|
194
|
+
this.log(`Processing ${request.method} from ${event.pubkey}`);
|
|
195
|
+
try {
|
|
196
|
+
let result;
|
|
197
|
+
switch (request.method) {
|
|
198
|
+
case NostrConnectMethod.Connect:
|
|
199
|
+
result = await this.handleConnect(event.pubkey, request.params);
|
|
200
|
+
break;
|
|
201
|
+
case NostrConnectMethod.GetPublicKey:
|
|
202
|
+
result = await this.upstream.getPublicKey();
|
|
203
|
+
break;
|
|
204
|
+
case NostrConnectMethod.SignEvent:
|
|
205
|
+
result = await this.handleSignEvent(request.params);
|
|
206
|
+
break;
|
|
207
|
+
case NostrConnectMethod.Nip04Encrypt:
|
|
208
|
+
result = await this.handleNip04Encrypt(request.params);
|
|
209
|
+
break;
|
|
210
|
+
case NostrConnectMethod.Nip04Decrypt:
|
|
211
|
+
result = await this.handleNip04Decrypt(request.params);
|
|
212
|
+
break;
|
|
213
|
+
case NostrConnectMethod.Nip44Encrypt:
|
|
214
|
+
result = await this.handleNip44Encrypt(request.params);
|
|
215
|
+
break;
|
|
216
|
+
case NostrConnectMethod.Nip44Decrypt:
|
|
217
|
+
result = await this.handleNip44Decrypt(request.params);
|
|
218
|
+
break;
|
|
219
|
+
default:
|
|
220
|
+
throw new Error(`Unsupported method: ${request.method}`);
|
|
221
|
+
}
|
|
222
|
+
// Send success response
|
|
223
|
+
await this.sendResponse(event, request.id, result);
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
this.log(`Error processing ${request.method}:`, error);
|
|
227
|
+
await this.sendErrorResponse(event, request.id, error instanceof Error ? error.message : "Unknown error");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
this.log("Error handling request:", err);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** Handle an initial NostrConnectURI */
|
|
235
|
+
async handleNostrConnectURI(uri) {
|
|
236
|
+
if (this.client)
|
|
237
|
+
throw new Error("Already connected to a client");
|
|
238
|
+
// Parse the URI
|
|
239
|
+
if (typeof uri === "string")
|
|
240
|
+
uri = parseNostrConnectURI(uri);
|
|
241
|
+
// Get a response to a fake initial `connect` request
|
|
242
|
+
const response = await this.handleConnect(uri.client, [
|
|
243
|
+
await this.signer.getPublicKey(),
|
|
244
|
+
uri.secret,
|
|
245
|
+
uri.metadata?.permissions?.join(",") ?? "",
|
|
246
|
+
]);
|
|
247
|
+
// Send `connect` response with random id
|
|
248
|
+
await this.sendResponse(uri.client, nanoid(8), response);
|
|
249
|
+
}
|
|
250
|
+
/** Handle connect request */
|
|
251
|
+
async handleConnect(client, [target, secret, permissionsStr]) {
|
|
252
|
+
const permissions = permissionsStr ? permissionsStr.split(",") : [];
|
|
253
|
+
// Check if this is a connection to our provider
|
|
254
|
+
const providerPubkey = await this.signer.getPublicKey();
|
|
255
|
+
if (target !== providerPubkey)
|
|
256
|
+
throw new Error("Invalid target pubkey");
|
|
257
|
+
// If a secret is set, check that if matches
|
|
258
|
+
if (this.secret && this.secret !== secret)
|
|
259
|
+
throw new Error("Invalid secret");
|
|
260
|
+
// Handle authorization if callback is provided
|
|
261
|
+
if (this.onConnect) {
|
|
262
|
+
const authorized = await this.onConnect(client, permissions);
|
|
263
|
+
if (authorized === false)
|
|
264
|
+
throw new Error("Authorization denied");
|
|
265
|
+
}
|
|
266
|
+
// If the client isn't set yet, this if the first `connect` request
|
|
267
|
+
const isFirstRequest = !this.client;
|
|
268
|
+
// Establish connection
|
|
269
|
+
this.client = client;
|
|
270
|
+
this.connected = true;
|
|
271
|
+
if (!this.secret)
|
|
272
|
+
this.secret = secret;
|
|
273
|
+
// Update the subscription since we now know the client pubkey
|
|
274
|
+
if (isFirstRequest)
|
|
275
|
+
await this.updateSubscription();
|
|
276
|
+
// Notify connection
|
|
277
|
+
if (this.onClientConnect)
|
|
278
|
+
this.onClientConnect(client);
|
|
279
|
+
// Resolve waiting promise
|
|
280
|
+
if (this.waitingPromise) {
|
|
281
|
+
this.waitingPromise.resolve(client);
|
|
282
|
+
this.waitingPromise = null;
|
|
283
|
+
}
|
|
284
|
+
// Return ack or the secret if provided
|
|
285
|
+
return secret || "ack";
|
|
286
|
+
}
|
|
287
|
+
/** Handle sign event request */
|
|
288
|
+
async handleSignEvent([eventJson]) {
|
|
289
|
+
const template = JSON.parse(eventJson);
|
|
290
|
+
// Check if the sign event is allowed
|
|
291
|
+
if (this.onSignEvent) {
|
|
292
|
+
const result = await this.onSignEvent(template, this.client);
|
|
293
|
+
if (result === false)
|
|
294
|
+
throw new Error("Sign event denied");
|
|
295
|
+
}
|
|
296
|
+
const signedEvent = await this.upstream.signEvent(template);
|
|
297
|
+
return JSON.stringify(signedEvent);
|
|
298
|
+
}
|
|
299
|
+
/** Handle NIP-04 encryption */
|
|
300
|
+
async handleNip04Encrypt([pubkey, plaintext,]) {
|
|
301
|
+
if (!this.upstream.nip04)
|
|
302
|
+
throw new Error("NIP-04 not supported");
|
|
303
|
+
// Check if the nip04 encryption is allowed
|
|
304
|
+
if (this.onNip04Encrypt) {
|
|
305
|
+
const result = await this.onNip04Encrypt(pubkey, plaintext, this.client);
|
|
306
|
+
if (result === false)
|
|
307
|
+
throw new Error("NIP-04 encryption denied");
|
|
308
|
+
}
|
|
309
|
+
return await this.upstream.nip04.encrypt(pubkey, plaintext);
|
|
310
|
+
}
|
|
311
|
+
/** Handle NIP-04 decryption */
|
|
312
|
+
async handleNip04Decrypt([pubkey, ciphertext,]) {
|
|
313
|
+
if (!this.upstream.nip04)
|
|
314
|
+
throw new Error("NIP-04 not supported");
|
|
315
|
+
// Check if the nip04 decryption is allowed
|
|
316
|
+
if (this.onNip04Decrypt) {
|
|
317
|
+
const result = await this.onNip04Decrypt(pubkey, ciphertext, this.client);
|
|
318
|
+
if (result === false)
|
|
319
|
+
throw new Error("NIP-04 decryption denied");
|
|
320
|
+
}
|
|
321
|
+
return await this.upstream.nip04.decrypt(pubkey, ciphertext);
|
|
322
|
+
}
|
|
323
|
+
/** Handle NIP-44 encryption */
|
|
324
|
+
async handleNip44Encrypt([pubkey, plaintext,]) {
|
|
325
|
+
if (!this.upstream.nip44)
|
|
326
|
+
throw new Error("NIP-44 not supported");
|
|
327
|
+
// Check if the nip44 encryption is allowed
|
|
328
|
+
if (this.onNip44Encrypt) {
|
|
329
|
+
const result = await this.onNip44Encrypt(pubkey, plaintext, this.client);
|
|
330
|
+
if (result === false)
|
|
331
|
+
throw new Error("NIP-44 encryption denied");
|
|
332
|
+
}
|
|
333
|
+
return await this.upstream.nip44.encrypt(pubkey, plaintext);
|
|
334
|
+
}
|
|
335
|
+
/** Handle NIP-44 decryption */
|
|
336
|
+
async handleNip44Decrypt([pubkey, ciphertext,]) {
|
|
337
|
+
if (!this.upstream.nip44)
|
|
338
|
+
throw new Error("NIP-44 not supported");
|
|
339
|
+
// Check if the nip44 decryption is allowed
|
|
340
|
+
if (this.onNip44Decrypt) {
|
|
341
|
+
const result = await this.onNip44Decrypt(pubkey, ciphertext, this.client);
|
|
342
|
+
if (result === false)
|
|
343
|
+
throw new Error("NIP-44 decryption denied");
|
|
344
|
+
}
|
|
345
|
+
return await this.upstream.nip44.decrypt(pubkey, ciphertext);
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Send a response to the client
|
|
349
|
+
* @param clientOrRequest - The client pubkey or request event
|
|
350
|
+
* @param requestId - The id of the request
|
|
351
|
+
* @param result - The result of the request
|
|
352
|
+
*/
|
|
353
|
+
async sendResponse(clientOrRequest, requestId, result) {
|
|
354
|
+
const response = {
|
|
355
|
+
id: requestId,
|
|
356
|
+
result,
|
|
357
|
+
};
|
|
358
|
+
await this.sendMessage(clientOrRequest, response);
|
|
359
|
+
}
|
|
360
|
+
/** Send an error response to the client */
|
|
361
|
+
async sendErrorResponse(event, requestId, error) {
|
|
362
|
+
const response = {
|
|
363
|
+
id: requestId,
|
|
364
|
+
result: "",
|
|
365
|
+
error,
|
|
366
|
+
};
|
|
367
|
+
await this.sendMessage(event, response);
|
|
368
|
+
}
|
|
369
|
+
/** Send an encrypted message to the client */
|
|
370
|
+
async sendMessage(clientOrRequest, message) {
|
|
371
|
+
// Get the pubkey of the client
|
|
372
|
+
const client = typeof clientOrRequest === "string" ? clientOrRequest : clientOrRequest.pubkey;
|
|
373
|
+
// Try NIP-44 first, fallback to NIP-04
|
|
374
|
+
let encryption;
|
|
375
|
+
// If responding to a request, try to use the same encryption
|
|
376
|
+
if (typeof clientOrRequest !== "string")
|
|
377
|
+
encryption = getEncryptedContentEncryptionMethods(clientOrRequest.kind, this.signer, isNIP04(clientOrRequest.content) ? "nip04" : "nip44");
|
|
378
|
+
// Get default encryption method (nip44)
|
|
379
|
+
else
|
|
380
|
+
encryption = getEncryptedContentEncryptionMethods(kinds.NostrConnect, this.signer);
|
|
381
|
+
const content = JSON.stringify(message);
|
|
382
|
+
const event = await this.signer.signEvent({
|
|
383
|
+
kind: kinds.NostrConnect,
|
|
384
|
+
created_at: unixNow(),
|
|
385
|
+
tags: [["p", client]],
|
|
386
|
+
content: await encryption.encrypt(client, content),
|
|
387
|
+
});
|
|
388
|
+
// Publish the event
|
|
389
|
+
const result = this.publishMethod(this.relays, event);
|
|
390
|
+
// Handle returned Promise or Observable
|
|
391
|
+
if (result instanceof Promise) {
|
|
392
|
+
await result;
|
|
393
|
+
}
|
|
394
|
+
else if ("subscribe" in result) {
|
|
395
|
+
await new Promise((res) => result.subscribe({ complete: res }));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
/** Get the connection string that clients can use to connect */
|
|
399
|
+
async getBunkerURI() {
|
|
400
|
+
return createBunkerURI({
|
|
401
|
+
remote: await this.signer.getPublicKey(),
|
|
402
|
+
relays: this.relays,
|
|
403
|
+
secret: this.secret,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
}
|
|
@@ -1,60 +1,8 @@
|
|
|
1
|
-
import { EventTemplate, Filter, NostrEvent, verifyEvent } from "nostr-tools";
|
|
2
|
-
import { Nip07Interface, SimpleSigner } from "applesauce-signers";
|
|
3
1
|
import { Deferred } from "applesauce-core/promise";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Nip04Encrypt = "nip04_encrypt",
|
|
9
|
-
Nip04Decrypt = "nip04_decrypt",
|
|
10
|
-
Nip44Encrypt = "nip44_encrypt",
|
|
11
|
-
Nip44Decrypt = "nip44_decrypt"
|
|
12
|
-
}
|
|
13
|
-
export declare enum NostrConnectMethod {
|
|
14
|
-
Connect = "connect",
|
|
15
|
-
CreateAccount = "create_account",
|
|
16
|
-
GetPublicKey = "get_public_key",
|
|
17
|
-
SignEvent = "sign_event",
|
|
18
|
-
Nip04Encrypt = "nip04_encrypt",
|
|
19
|
-
Nip04Decrypt = "nip04_decrypt",
|
|
20
|
-
Nip44Encrypt = "nip44_encrypt",
|
|
21
|
-
Nip44Decrypt = "nip44_decrypt"
|
|
22
|
-
}
|
|
23
|
-
type RequestParams = {
|
|
24
|
-
[NostrConnectMethod.Connect]: [string] | [string, string] | [string, string, string];
|
|
25
|
-
[NostrConnectMethod.CreateAccount]: [string, string] | [string, string, string] | [string, string, string, string];
|
|
26
|
-
[NostrConnectMethod.GetPublicKey]: [];
|
|
27
|
-
[NostrConnectMethod.SignEvent]: [string];
|
|
28
|
-
[NostrConnectMethod.Nip04Encrypt]: [string, string];
|
|
29
|
-
[NostrConnectMethod.Nip04Decrypt]: [string, string];
|
|
30
|
-
[NostrConnectMethod.Nip44Encrypt]: [string, string];
|
|
31
|
-
[NostrConnectMethod.Nip44Decrypt]: [string, string];
|
|
32
|
-
};
|
|
33
|
-
type ResponseResults = {
|
|
34
|
-
[NostrConnectMethod.Connect]: "ack";
|
|
35
|
-
[NostrConnectMethod.CreateAccount]: string;
|
|
36
|
-
[NostrConnectMethod.GetPublicKey]: string;
|
|
37
|
-
[NostrConnectMethod.SignEvent]: string;
|
|
38
|
-
[NostrConnectMethod.Nip04Encrypt]: string;
|
|
39
|
-
[NostrConnectMethod.Nip04Decrypt]: string;
|
|
40
|
-
[NostrConnectMethod.Nip44Encrypt]: string;
|
|
41
|
-
[NostrConnectMethod.Nip44Decrypt]: string;
|
|
42
|
-
};
|
|
43
|
-
export type NostrConnectRequest<N extends NostrConnectMethod> = {
|
|
44
|
-
id: string;
|
|
45
|
-
method: N;
|
|
46
|
-
params: RequestParams[N];
|
|
47
|
-
};
|
|
48
|
-
export type NostrConnectResponse<N extends NostrConnectMethod> = {
|
|
49
|
-
id: string;
|
|
50
|
-
result: ResponseResults[N];
|
|
51
|
-
error?: string;
|
|
52
|
-
};
|
|
53
|
-
export type NostrConnectErrorResponse = {
|
|
54
|
-
id: string;
|
|
55
|
-
result: string;
|
|
56
|
-
error: string;
|
|
57
|
-
};
|
|
2
|
+
import { ISigner, SimpleSigner } from "applesauce-signers";
|
|
3
|
+
import { EventTemplate, Filter, NostrEvent, verifyEvent } from "nostr-tools";
|
|
4
|
+
import { BunkerURI, NostrConnectAppMetadata } from "../helpers/nostr-connect.js";
|
|
5
|
+
import { Subscribable, Unsubscribable } from "../helpers/observable.js";
|
|
58
6
|
export type NostrConnectSignerOptions = {
|
|
59
7
|
/** The relays to communicate over */
|
|
60
8
|
relays: string[];
|
|
@@ -64,39 +12,37 @@ export type NostrConnectSignerOptions = {
|
|
|
64
12
|
remote?: string;
|
|
65
13
|
/** Users pubkey */
|
|
66
14
|
pubkey?: string;
|
|
15
|
+
/** A secret used when initalizing the connection from the client side */
|
|
16
|
+
secret?: string;
|
|
67
17
|
/** A method for handling "auth" requests */
|
|
68
18
|
onAuth?: (url: string) => Promise<void>;
|
|
69
19
|
/** A method for subscribing to relays */
|
|
70
20
|
subscriptionMethod?: NostrSubscriptionMethod;
|
|
71
21
|
/** A method for publishing events */
|
|
72
22
|
publishMethod?: NostrPublishMethod;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
unsubscribe(): void;
|
|
76
|
-
}
|
|
77
|
-
interface Observer<T> {
|
|
78
|
-
next: (value: T) => void;
|
|
79
|
-
error: (err: any) => void;
|
|
80
|
-
complete: () => void;
|
|
81
|
-
}
|
|
82
|
-
type Subscribable<T extends unknown> = {
|
|
83
|
-
subscribe: (observer: Partial<Observer<T>>) => Unsubscribable;
|
|
23
|
+
/** A pool of methods to use if none are passed in when creating the signer */
|
|
24
|
+
pool?: NostrPool;
|
|
84
25
|
};
|
|
85
26
|
/** A method used to subscribe to events on a set of relays */
|
|
86
27
|
export type NostrSubscriptionMethod = (relays: string[], filters: Filter[]) => Subscribable<NostrEvent | string>;
|
|
87
28
|
/** A method used for publishing an event, can return a Promise that completes when published or an Observable that completes when published*/
|
|
88
29
|
export type NostrPublishMethod = (relays: string[], event: NostrEvent) => Promise<any> | Subscribable<any>;
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
permissions?: string[];
|
|
30
|
+
/** A simple pool type that combines the subscription and publish methods */
|
|
31
|
+
export type NostrPool = {
|
|
32
|
+
subscription: NostrSubscriptionMethod;
|
|
33
|
+
publish: NostrPublishMethod;
|
|
94
34
|
};
|
|
95
|
-
export declare class NostrConnectSigner implements
|
|
35
|
+
export declare class NostrConnectSigner implements ISigner {
|
|
96
36
|
/** A method that is called when an event needs to be published */
|
|
97
37
|
protected publishMethod: NostrPublishMethod;
|
|
98
38
|
/** The active nostr subscription */
|
|
99
39
|
protected subscriptionMethod: NostrSubscriptionMethod;
|
|
40
|
+
/** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
|
|
41
|
+
static subscriptionMethod: NostrSubscriptionMethod | undefined;
|
|
42
|
+
/** A fallback method to use for publishMethod if none is pass in when creating the signer */
|
|
43
|
+
static publishMethod: NostrPublishMethod | undefined;
|
|
44
|
+
/** A fallback pool to use if none is pass in when creating the signer */
|
|
45
|
+
static pool: NostrPool | undefined;
|
|
100
46
|
protected log: import("debug").Debugger;
|
|
101
47
|
/** The local client signer */
|
|
102
48
|
signer: SimpleSigner;
|
|
@@ -116,19 +62,15 @@ export declare class NostrConnectSigner implements Nip07Interface {
|
|
|
116
62
|
onAuth: (url: string) => Promise<void>;
|
|
117
63
|
verifyEvent: typeof verifyEvent;
|
|
118
64
|
/** A secret used when initiating a connection from the client side */
|
|
119
|
-
|
|
65
|
+
secret: string;
|
|
120
66
|
nip04?: {
|
|
121
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string
|
|
122
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string
|
|
67
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
68
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
123
69
|
} | undefined;
|
|
124
70
|
nip44?: {
|
|
125
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string
|
|
126
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string
|
|
71
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
72
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
127
73
|
} | undefined;
|
|
128
|
-
/** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
|
|
129
|
-
static subscriptionMethod: NostrSubscriptionMethod | undefined;
|
|
130
|
-
/** A fallback method to use for publishMethod if none is pass in when creating the signer */
|
|
131
|
-
static publishMethod: NostrPublishMethod | undefined;
|
|
132
74
|
constructor(opts: NostrConnectSignerOptions);
|
|
133
75
|
/** The currently active REQ subscription */
|
|
134
76
|
protected req?: Unsubscribable;
|
|
@@ -143,7 +85,7 @@ export declare class NostrConnectSigner implements Nip07Interface {
|
|
|
143
85
|
protected createRequestEvent(content: string, target?: string | undefined, kind?: number): Promise<import("nostr-tools").VerifiedEvent>;
|
|
144
86
|
private makeRequest;
|
|
145
87
|
/** Connect to remote signer */
|
|
146
|
-
connect(secret?: string | undefined, permissions?: string[]): Promise<
|
|
88
|
+
connect(secret?: string | undefined, permissions?: string[]): Promise<string>;
|
|
147
89
|
private waitingPromise;
|
|
148
90
|
/** Wait for a remote signer to connect */
|
|
149
91
|
waitForSigner(abort?: AbortSignal): Promise<void>;
|
|
@@ -164,11 +106,7 @@ export declare class NostrConnectSigner implements Nip07Interface {
|
|
|
164
106
|
/** Returns the nostrconnect:// URI for this signer */
|
|
165
107
|
getNostrConnectURI(metadata?: NostrConnectAppMetadata): string;
|
|
166
108
|
/** Parses a bunker:// URI */
|
|
167
|
-
static parseBunkerURI(uri: string):
|
|
168
|
-
remote: string;
|
|
169
|
-
relays: string[];
|
|
170
|
-
secret?: string;
|
|
171
|
-
};
|
|
109
|
+
static parseBunkerURI(uri: string): BunkerURI;
|
|
172
110
|
/** Builds an array of signing permissions for event kinds */
|
|
173
111
|
static buildSigningPermissions(kinds: number[]): string[];
|
|
174
112
|
/** Create a {@link NostrConnectSigner} from a bunker:// URI */
|
|
@@ -177,4 +115,3 @@ export declare class NostrConnectSigner implements Nip07Interface {
|
|
|
177
115
|
signer?: SimpleSigner;
|
|
178
116
|
}): Promise<NostrConnectSigner>;
|
|
179
117
|
}
|
|
180
|
-
export {};
|