@welshman/signer 0.0.15 → 0.0.17
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/README.md +92 -2
- package/build/src/signers/nip46.cjs +243 -178
- package/build/src/signers/nip46.cjs.map +1 -1
- package/build/src/signers/nip46.d.ts +61 -42
- package/build/src/signers/nip46.mjs +242 -180
- package/build/src/signers/nip46.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
import { Emitter } from "@welshman/lib";
|
|
2
2
|
import { TrustedEvent, StampedEvent } from "@welshman/util";
|
|
3
|
+
import { Subscription } from "@welshman/net";
|
|
3
4
|
import { ISigner } from '../util';
|
|
5
|
+
import { Nip01Signer } from './nip01';
|
|
4
6
|
export type Nip46Algorithm = "nip04" | "nip44";
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
10
|
-
export type Nip46InitiateParams = {
|
|
11
|
-
url: string;
|
|
12
|
-
name: string;
|
|
13
|
-
image: string;
|
|
14
|
-
perms: string;
|
|
15
|
-
relays: string[];
|
|
16
|
-
abortController?: AbortController;
|
|
17
|
-
};
|
|
7
|
+
export declare enum Nip46Event {
|
|
8
|
+
Send = "send",
|
|
9
|
+
Receive = "receive"
|
|
10
|
+
}
|
|
18
11
|
export type Nip46BrokerParams = {
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
relays: string[];
|
|
13
|
+
clientSecret: string;
|
|
14
|
+
signerPubkey?: string;
|
|
21
15
|
algorithm?: Nip46Algorithm;
|
|
22
16
|
};
|
|
23
|
-
export type Nip46Request = {
|
|
24
|
-
method: string;
|
|
25
|
-
params: string[];
|
|
26
|
-
resolve: (result: Nip46ResponseWithResult) => void;
|
|
27
|
-
};
|
|
28
17
|
export type Nip46Response = {
|
|
29
18
|
id: string;
|
|
30
19
|
url: string;
|
|
@@ -44,39 +33,69 @@ export type Nip46ResponseWithError = {
|
|
|
44
33
|
event: TrustedEvent;
|
|
45
34
|
error: string;
|
|
46
35
|
};
|
|
36
|
+
export declare class Nip46Receiver extends Emitter {
|
|
37
|
+
signer: ISigner;
|
|
38
|
+
params: Nip46BrokerParams;
|
|
39
|
+
sub?: Subscription;
|
|
40
|
+
constructor(signer: ISigner, params: Nip46BrokerParams);
|
|
41
|
+
start: () => Promise<void>;
|
|
42
|
+
stop: () => void;
|
|
43
|
+
}
|
|
44
|
+
export declare class Nip46Sender extends Emitter {
|
|
45
|
+
signer: ISigner;
|
|
46
|
+
params: Nip46BrokerParams;
|
|
47
|
+
processing: boolean;
|
|
48
|
+
queue: Nip46Request[];
|
|
49
|
+
constructor(signer: ISigner, params: Nip46BrokerParams);
|
|
50
|
+
send: (request: Nip46Request) => Promise<void>;
|
|
51
|
+
process: () => Promise<void>;
|
|
52
|
+
enqueue: (request: Nip46Request) => void;
|
|
53
|
+
stop: () => void;
|
|
54
|
+
}
|
|
55
|
+
export declare class Nip46Request {
|
|
56
|
+
readonly method: string;
|
|
57
|
+
readonly params: string[];
|
|
58
|
+
id: string;
|
|
59
|
+
promise: import("@welshman/lib").Deferred<Nip46ResponseWithResult, Nip46ResponseWithError>;
|
|
60
|
+
constructor(method: string, params: string[]);
|
|
61
|
+
listen: (receiver: Nip46Receiver) => Promise<void>;
|
|
62
|
+
send: (sender: Nip46Sender) => Promise<void>;
|
|
63
|
+
}
|
|
47
64
|
export declare class Nip46Broker extends Emitter {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
params: URLSearchParams;
|
|
53
|
-
nostrconnect: string;
|
|
54
|
-
clientSecret: string;
|
|
55
|
-
clientPubkey: string;
|
|
56
|
-
};
|
|
57
|
-
static parseBunkerLink(link: string): {
|
|
58
|
-
token: string;
|
|
59
|
-
pubkey: string;
|
|
60
|
-
relays: string[];
|
|
61
|
-
};
|
|
62
|
-
static get(params: Nip46BrokerParams): Nip46Broker;
|
|
65
|
+
params: Nip46BrokerParams;
|
|
66
|
+
signer: ISigner;
|
|
67
|
+
sender: Nip46Sender;
|
|
68
|
+
receiver: Nip46Receiver;
|
|
63
69
|
constructor(params: Nip46BrokerParams);
|
|
70
|
+
static get(params: Nip46BrokerParams): Nip46Broker;
|
|
64
71
|
hasParams(params: Nip46BrokerParams): boolean;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
makeSigner: () => Nip01Signer;
|
|
73
|
+
makeSender: () => Nip46Sender;
|
|
74
|
+
makeReceiver: () => Nip46Receiver;
|
|
75
|
+
setParams: (params: Partial<Nip46BrokerParams>) => void;
|
|
76
|
+
teardown: () => void;
|
|
77
|
+
enqueue: (method: string, params: string[]) => Promise<Nip46Request>;
|
|
78
|
+
send: (method: string, params: string[]) => Promise<string>;
|
|
79
|
+
parseBunkerUrl: (url: string) => {
|
|
80
|
+
signerPubkey: string;
|
|
81
|
+
connectSecret: string;
|
|
82
|
+
relays: string[];
|
|
83
|
+
};
|
|
84
|
+
makeNostrconnectUrl: (meta?: Record<string, string>) => Promise<string>;
|
|
85
|
+
waitForNostrconnect: (url: string, abort?: AbortController) => import("@welshman/lib").CustomPromise<Nip46ResponseWithResult, Nip46Response | undefined>;
|
|
86
|
+
ping: () => Promise<string>;
|
|
69
87
|
getPublicKey: () => Promise<string>;
|
|
88
|
+
createAccount: (username: string, domain: string, perms?: string) => Promise<string>;
|
|
89
|
+
connect: (signerPubkey: string, connectSecret?: string, perms?: string) => Promise<string>;
|
|
70
90
|
signEvent: (event: StampedEvent) => Promise<any>;
|
|
71
91
|
nip04Encrypt: (pk: string, message: string) => Promise<string>;
|
|
72
92
|
nip04Decrypt: (pk: string, message: string) => Promise<string>;
|
|
73
93
|
nip44Encrypt: (pk: string, message: string) => Promise<string>;
|
|
74
94
|
nip44Decrypt: (pk: string, message: string) => Promise<string>;
|
|
75
|
-
teardown: () => void;
|
|
76
95
|
}
|
|
77
96
|
export declare class Nip46Signer implements ISigner {
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
broker: Nip46Broker;
|
|
98
|
+
pubkey?: string;
|
|
80
99
|
constructor(broker: Nip46Broker);
|
|
81
100
|
getPubkey: () => Promise<string>;
|
|
82
101
|
sign: (template: StampedEvent) => Promise<any>;
|
|
@@ -1,230 +1,293 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _Nip46Broker_signer, _Nip46Broker_handler, _Nip46Broker_algorithm, _Nip46Broker_closed, _Nip46Broker_processing, _Nip46Broker_connectResponse, _Nip46Broker_queue, _Nip46Broker_window, _Nip46Broker_sub, _Nip46Broker_subscribe, _Nip46Broker_processQueue, _Nip46Broker_getResult, _Nip46Signer_pubkey;
|
|
13
|
-
import { Emitter, sleep, tryCatch, randomId, equals } from "@welshman/lib";
|
|
14
|
-
import { createEvent, NOSTR_CONNECT } from "@welshman/util";
|
|
1
|
+
import { Emitter, throttle, makePromise, defer, sleep, tryCatch, randomId, equals } from "@welshman/lib";
|
|
2
|
+
import { createEvent, normalizeRelayUrl, NOSTR_CONNECT } from "@welshman/util";
|
|
15
3
|
import { subscribe, publish, SubscriptionEvent } from "@welshman/net";
|
|
16
|
-
import { decrypt, hash, own
|
|
4
|
+
import { decrypt, hash, own } from "../util.mjs";
|
|
17
5
|
import { Nip01Signer } from "./nip01.mjs";
|
|
6
|
+
export var Nip46Event;
|
|
7
|
+
(function (Nip46Event) {
|
|
8
|
+
Nip46Event["Send"] = "send";
|
|
9
|
+
Nip46Event["Receive"] = "receive";
|
|
10
|
+
})(Nip46Event || (Nip46Event = {}));
|
|
18
11
|
let singleton;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
12
|
+
const popupManager = (() => {
|
|
13
|
+
let pendingUrl = "";
|
|
14
|
+
let pendingSince = 0;
|
|
15
|
+
let currentWindow;
|
|
16
|
+
const openPending = throttle(1000, () => {
|
|
17
|
+
// If it's been a while since they asked for it, drop the request
|
|
18
|
+
if (Date.now() - pendingSince > 10000)
|
|
19
|
+
return;
|
|
20
|
+
// If we have an active, open window, continue to wait
|
|
21
|
+
if (currentWindow && !currentWindow.closed) {
|
|
22
|
+
setTimeout(() => openPending(), 100);
|
|
28
23
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const response = await tryCatch(async () => JSON.parse(await decrypt(clientSigner, pubkey, content)));
|
|
40
|
-
if ((response === null || response === void 0 ? void 0 : response.result) === secret) {
|
|
41
|
-
complete(pubkey);
|
|
42
|
-
}
|
|
43
|
-
if ((response === null || response === void 0 ? void 0 : response.result) === 'ack') {
|
|
44
|
-
console.warn("Bunker responded to nostrconnect with 'ack', which can lead to session hijacking");
|
|
45
|
-
complete(pubkey);
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
abortController === null || abortController === void 0 ? void 0 : abortController.signal.addEventListener('abort', () => complete());
|
|
50
|
-
});
|
|
51
|
-
return { result, params, nostrconnect, clientSecret, clientPubkey };
|
|
52
|
-
}
|
|
53
|
-
static parseBunkerLink(link) {
|
|
54
|
-
let token = "";
|
|
55
|
-
let pubkey = "";
|
|
56
|
-
let relays = [];
|
|
57
|
-
try {
|
|
58
|
-
const url = new URL(link);
|
|
59
|
-
pubkey = url.hostname || url.pathname.replace(/\//g, '');
|
|
60
|
-
relays = url.searchParams.getAll("relay") || [];
|
|
61
|
-
token = url.searchParams.get("secret") || "";
|
|
62
|
-
}
|
|
63
|
-
catch (_a) {
|
|
64
|
-
// pass
|
|
65
|
-
}
|
|
66
|
-
return { token, pubkey, relays };
|
|
67
|
-
}
|
|
68
|
-
static get(params) {
|
|
69
|
-
if (!(singleton === null || singleton === void 0 ? void 0 : singleton.hasParams(params))) {
|
|
70
|
-
singleton === null || singleton === void 0 ? void 0 : singleton.teardown();
|
|
71
|
-
singleton = new Nip46Broker(params);
|
|
24
|
+
else {
|
|
25
|
+
// Attempt to open the window
|
|
26
|
+
const w = window.open(pendingUrl, "", "width=600,height=800,popup=yes");
|
|
27
|
+
// If open was successful, keep track of our window
|
|
28
|
+
if (w) {
|
|
29
|
+
currentWindow = w;
|
|
30
|
+
}
|
|
31
|
+
// In any case, this url has been handled
|
|
32
|
+
pendingUrl = "";
|
|
33
|
+
pendingSince = 0;
|
|
72
34
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
open: (url) => {
|
|
38
|
+
pendingUrl = url;
|
|
39
|
+
pendingSince = Date.now();
|
|
40
|
+
openPending();
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
export class Nip46Receiver extends Emitter {
|
|
45
|
+
constructor(signer, params) {
|
|
76
46
|
super();
|
|
47
|
+
this.signer = signer;
|
|
77
48
|
this.params = params;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
_Nip46Broker_queue.set(this, []);
|
|
85
|
-
_Nip46Broker_window.set(this, void 0);
|
|
86
|
-
_Nip46Broker_sub.set(this, void 0);
|
|
87
|
-
_Nip46Broker_subscribe.set(this, async () => {
|
|
88
|
-
const pubkey = await __classPrivateFieldGet(this, _Nip46Broker_signer, "f").getPubkey();
|
|
49
|
+
this.start = async () => {
|
|
50
|
+
if (this.sub)
|
|
51
|
+
return;
|
|
52
|
+
const userPubkey = await this.signer.getPubkey();
|
|
53
|
+
const filters = [{ kinds: [NOSTR_CONNECT], "#p": [userPubkey] }];
|
|
54
|
+
this.sub = subscribe({ relays: this.params.relays, filters });
|
|
89
55
|
return new Promise(resolve => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
__classPrivateFieldSet(this, _Nip46Broker_sub, subscribe({
|
|
94
|
-
relays: __classPrivateFieldGet(this, _Nip46Broker_handler, "f").relays,
|
|
95
|
-
filters: [{ kinds: [NOSTR_CONNECT], "#p": [pubkey] }],
|
|
96
|
-
}), "f");
|
|
97
|
-
__classPrivateFieldGet(this, _Nip46Broker_sub, "f").emitter.on(SubscriptionEvent.Send, resolve);
|
|
98
|
-
__classPrivateFieldGet(this, _Nip46Broker_sub, "f").emitter.on(SubscriptionEvent.Event, async (url, event) => {
|
|
99
|
-
const json = await decrypt(__classPrivateFieldGet(this, _Nip46Broker_signer, "f"), event.pubkey, event.content);
|
|
56
|
+
this.sub.emitter.on(SubscriptionEvent.Send, resolve);
|
|
57
|
+
this.sub.emitter.on(SubscriptionEvent.Event, async (url, event) => {
|
|
58
|
+
const json = await decrypt(this.signer, event.pubkey, event.content);
|
|
100
59
|
const response = tryCatch(() => JSON.parse(json)) || {};
|
|
101
|
-
if (!response.id) {
|
|
102
|
-
console.error(`Invalid nostr-connect response: ${json}`);
|
|
103
|
-
}
|
|
104
60
|
// Delay errors in case there's a zombie signer out there clogging things up
|
|
105
61
|
if (response.error) {
|
|
106
62
|
await sleep(3000);
|
|
107
63
|
}
|
|
108
|
-
|
|
109
|
-
this.emit(`auth-${response.id}`, response);
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
this.emit(`res-${response.id}`, response);
|
|
113
|
-
}
|
|
64
|
+
this.emit(Nip46Event.Receive, { ...response, url, event });
|
|
114
65
|
});
|
|
115
|
-
|
|
116
|
-
|
|
66
|
+
this.sub.emitter.on(SubscriptionEvent.Complete, () => {
|
|
67
|
+
this.sub = undefined;
|
|
117
68
|
});
|
|
118
69
|
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
70
|
+
};
|
|
71
|
+
this.stop = () => {
|
|
72
|
+
var _a;
|
|
73
|
+
(_a = this.sub) === null || _a === void 0 ? void 0 : _a.close();
|
|
74
|
+
this.removeAllListeners();
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export class Nip46Sender extends Emitter {
|
|
79
|
+
constructor(signer, params) {
|
|
80
|
+
super();
|
|
81
|
+
this.signer = signer;
|
|
82
|
+
this.params = params;
|
|
83
|
+
this.processing = false;
|
|
84
|
+
this.queue = [];
|
|
85
|
+
this.send = async (request) => {
|
|
86
|
+
const { id, method, params } = request;
|
|
87
|
+
const { relays, signerPubkey, algorithm = "nip44" } = this.params;
|
|
88
|
+
if (!signerPubkey) {
|
|
89
|
+
throw new Error("Unable to send nip46 request without a signer pubkey");
|
|
90
|
+
}
|
|
91
|
+
const payload = JSON.stringify({ id, method, params });
|
|
92
|
+
const content = await this.signer[algorithm].encrypt(signerPubkey, payload);
|
|
93
|
+
const template = createEvent(NOSTR_CONNECT, { content, tags: [["p", signerPubkey]] });
|
|
94
|
+
const event = await this.signer.sign(template);
|
|
95
|
+
const pub = publish({ relays, event });
|
|
96
|
+
this.emit(Nip46Event.Send, { ...request, pub });
|
|
97
|
+
};
|
|
98
|
+
this.process = async () => {
|
|
99
|
+
if (this.processing) {
|
|
122
100
|
return;
|
|
123
101
|
}
|
|
124
|
-
|
|
102
|
+
this.processing = true;
|
|
125
103
|
try {
|
|
126
|
-
while (
|
|
127
|
-
const [
|
|
104
|
+
while (this.queue.length > 0) {
|
|
105
|
+
const [request] = this.queue.splice(0, 1);
|
|
128
106
|
try {
|
|
129
|
-
|
|
130
|
-
console.log('nip46 response:', { method, params, ...response });
|
|
131
|
-
resolve(response);
|
|
107
|
+
await this.send(request);
|
|
132
108
|
}
|
|
133
109
|
catch (error) {
|
|
134
|
-
console.error(`nip46 error:`,
|
|
110
|
+
console.error(`nip46 error:`, error, request);
|
|
135
111
|
}
|
|
136
112
|
}
|
|
137
113
|
}
|
|
138
114
|
finally {
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
_Nip46Broker_getResult.set(this, async (promise) => {
|
|
143
|
-
const { result } = await promise;
|
|
144
|
-
return result;
|
|
145
|
-
});
|
|
146
|
-
this.request = async (method, params) => {
|
|
147
|
-
if (__classPrivateFieldGet(this, _Nip46Broker_closed, "f")) {
|
|
148
|
-
throw new Error("Attempted to make a nip46 request with a closed broker");
|
|
149
|
-
}
|
|
150
|
-
if (!__classPrivateFieldGet(this, _Nip46Broker_sub, "f")) {
|
|
151
|
-
await __classPrivateFieldGet(this, _Nip46Broker_subscribe, "f").call(this);
|
|
115
|
+
this.processing = false;
|
|
152
116
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
117
|
+
};
|
|
118
|
+
this.enqueue = (request) => {
|
|
119
|
+
this.queue.push(request);
|
|
120
|
+
this.process();
|
|
121
|
+
};
|
|
122
|
+
this.stop = () => {
|
|
123
|
+
this.removeAllListeners();
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export class Nip46Request {
|
|
128
|
+
constructor(method, params) {
|
|
129
|
+
this.method = method;
|
|
130
|
+
this.params = params;
|
|
131
|
+
this.id = randomId();
|
|
132
|
+
this.promise = defer();
|
|
133
|
+
this.listen = async (receiver) => {
|
|
134
|
+
await receiver.start();
|
|
135
|
+
const onReceive = (response) => {
|
|
136
|
+
if (response.id !== this.id) {
|
|
137
|
+
return;
|
|
169
138
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
139
|
+
if (response.result === "auth_url") {
|
|
140
|
+
popupManager.open(response.error);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
173
143
|
if (response.error) {
|
|
174
|
-
reject(response);
|
|
144
|
+
this.promise.reject(response);
|
|
175
145
|
}
|
|
176
146
|
else {
|
|
177
|
-
resolve(response);
|
|
147
|
+
this.promise.resolve(response);
|
|
178
148
|
}
|
|
179
|
-
|
|
149
|
+
receiver.off(Nip46Event.Receive, onReceive);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
receiver.on(Nip46Event.Receive, onReceive);
|
|
153
|
+
};
|
|
154
|
+
this.send = async (sender) => {
|
|
155
|
+
sender.enqueue(this);
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export class Nip46Broker extends Emitter {
|
|
160
|
+
constructor(params) {
|
|
161
|
+
super();
|
|
162
|
+
this.params = params;
|
|
163
|
+
// Getters for helper objects
|
|
164
|
+
this.makeSigner = () => new Nip01Signer(this.params.clientSecret);
|
|
165
|
+
this.makeSender = () => {
|
|
166
|
+
const sender = new Nip46Sender(this.signer, this.params);
|
|
167
|
+
sender.on(Nip46Event.Send, (data) => {
|
|
168
|
+
console.log('nip46 send:', data);
|
|
180
169
|
});
|
|
170
|
+
return sender;
|
|
171
|
+
};
|
|
172
|
+
this.makeReceiver = () => {
|
|
173
|
+
const receiver = new Nip46Receiver(this.signer, this.params);
|
|
174
|
+
receiver.on(Nip46Event.Receive, (data) => {
|
|
175
|
+
console.log('nip46 receive:', data);
|
|
176
|
+
});
|
|
177
|
+
return receiver;
|
|
178
|
+
};
|
|
179
|
+
// Lifecycle methods
|
|
180
|
+
this.setParams = (params) => {
|
|
181
|
+
this.params = { ...this.params, ...params };
|
|
182
|
+
// Stop everything that's stateful
|
|
183
|
+
this.teardown();
|
|
184
|
+
// Set it back up again
|
|
185
|
+
this.sender = this.makeSender();
|
|
186
|
+
this.receiver = this.makeReceiver();
|
|
187
|
+
};
|
|
188
|
+
this.teardown = () => {
|
|
189
|
+
this.sender.stop();
|
|
190
|
+
this.receiver.stop();
|
|
181
191
|
};
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
// General purpose utility methods
|
|
193
|
+
this.enqueue = async (method, params) => {
|
|
194
|
+
const request = new Nip46Request(method, params);
|
|
195
|
+
await request.listen(this.receiver);
|
|
196
|
+
await request.send(this.sender);
|
|
197
|
+
return request;
|
|
198
|
+
};
|
|
199
|
+
this.send = async (method, params) => {
|
|
200
|
+
const request = await this.enqueue(method, params);
|
|
201
|
+
const response = await request.promise;
|
|
202
|
+
return response.result;
|
|
203
|
+
};
|
|
204
|
+
// Methods for initiating a connection
|
|
205
|
+
this.parseBunkerUrl = (url) => {
|
|
206
|
+
let connectSecret = "";
|
|
207
|
+
let signerPubkey = "";
|
|
208
|
+
let relays = [];
|
|
209
|
+
try {
|
|
210
|
+
const _url = new URL(url);
|
|
211
|
+
relays = _url.searchParams.getAll("relay") || [];
|
|
212
|
+
signerPubkey = _url.hostname || _url.pathname.replace(/\//g, '');
|
|
213
|
+
connectSecret = _url.searchParams.get("secret") || "";
|
|
189
214
|
}
|
|
190
|
-
|
|
215
|
+
catch (_a) {
|
|
216
|
+
// pass
|
|
217
|
+
}
|
|
218
|
+
return { signerPubkey, connectSecret, relays: relays.map(normalizeRelayUrl) };
|
|
191
219
|
};
|
|
192
|
-
this.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
220
|
+
this.makeNostrconnectUrl = async (meta = {}) => {
|
|
221
|
+
const clientPubkey = await this.signer.getPubkey();
|
|
222
|
+
const secret = Math.random().toString(36).substring(7);
|
|
223
|
+
const params = new URLSearchParams({ ...meta, secret });
|
|
224
|
+
for (const relay of this.params.relays) {
|
|
225
|
+
params.append('relay', relay);
|
|
196
226
|
}
|
|
197
|
-
return
|
|
198
|
-
};
|
|
199
|
-
this.getPublicKey = () => __classPrivateFieldGet(this, _Nip46Broker_getResult, "f").call(this, this.enqueue("get_public_key", []));
|
|
200
|
-
this.signEvent = async (event) => JSON.parse(await __classPrivateFieldGet(this, _Nip46Broker_getResult, "f").call(this, this.enqueue("sign_event", [JSON.stringify(event)])));
|
|
201
|
-
this.nip04Encrypt = (pk, message) => __classPrivateFieldGet(this, _Nip46Broker_getResult, "f").call(this, this.enqueue("nip04_encrypt", [pk, message]));
|
|
202
|
-
this.nip04Decrypt = (pk, message) => __classPrivateFieldGet(this, _Nip46Broker_getResult, "f").call(this, this.enqueue("nip04_decrypt", [pk, message]));
|
|
203
|
-
this.nip44Encrypt = (pk, message) => __classPrivateFieldGet(this, _Nip46Broker_getResult, "f").call(this, this.enqueue("nip44_encrypt", [pk, message]));
|
|
204
|
-
this.nip44Decrypt = (pk, message) => __classPrivateFieldGet(this, _Nip46Broker_getResult, "f").call(this, this.enqueue("nip44_decrypt", [pk, message]));
|
|
205
|
-
this.teardown = () => {
|
|
206
|
-
var _a;
|
|
207
|
-
__classPrivateFieldSet(this, _Nip46Broker_closed, true, "f");
|
|
208
|
-
(_a = __classPrivateFieldGet(this, _Nip46Broker_sub, "f")) === null || _a === void 0 ? void 0 : _a.close();
|
|
227
|
+
return `nostrconnect://${clientPubkey}?${params.toString()}`;
|
|
209
228
|
};
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
229
|
+
this.waitForNostrconnect = (url, abort) => {
|
|
230
|
+
const secret = new URL(url).searchParams.get('secret');
|
|
231
|
+
return makePromise((resolve, reject) => {
|
|
232
|
+
const onReceive = (response) => {
|
|
233
|
+
if (["ack", secret].includes(response.result)) {
|
|
234
|
+
this.setParams({ signerPubkey: response.event.pubkey });
|
|
235
|
+
if (response.result === 'ack') {
|
|
236
|
+
console.warn("Bunker responded to nostrconnect with 'ack', which can lead to session hijacking");
|
|
237
|
+
}
|
|
238
|
+
resolve(response);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
reject(response);
|
|
242
|
+
}
|
|
243
|
+
cleanup();
|
|
244
|
+
};
|
|
245
|
+
const cleanup = () => {
|
|
246
|
+
this.receiver.off(Nip46Event.Receive, onReceive);
|
|
247
|
+
};
|
|
248
|
+
this.receiver.on(Nip46Event.Receive, onReceive);
|
|
249
|
+
this.receiver.start();
|
|
250
|
+
abort === null || abort === void 0 ? void 0 : abort.signal.addEventListener('abort', () => {
|
|
251
|
+
reject(undefined);
|
|
252
|
+
cleanup();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
// Normal NIP 46 methods
|
|
257
|
+
this.ping = () => this.send("ping", []);
|
|
258
|
+
this.getPublicKey = () => this.send("get_public_key", []);
|
|
259
|
+
this.createAccount = (username, domain, perms = "") => this.send("create_account", [username, domain, "", perms]);
|
|
260
|
+
this.connect = async (signerPubkey, connectSecret = "", perms = "") => this.send("connect", [signerPubkey, connectSecret, perms]);
|
|
261
|
+
this.signEvent = async (event) => JSON.parse(await this.send("sign_event", [JSON.stringify(event)]));
|
|
262
|
+
this.nip04Encrypt = (pk, message) => this.send("nip04_encrypt", [pk, message]);
|
|
263
|
+
this.nip04Decrypt = (pk, message) => this.send("nip04_decrypt", [pk, message]);
|
|
264
|
+
this.nip44Encrypt = (pk, message) => this.send("nip44_encrypt", [pk, message]);
|
|
265
|
+
this.nip44Decrypt = (pk, message) => this.send("nip44_decrypt", [pk, message]);
|
|
266
|
+
this.signer = this.makeSigner();
|
|
267
|
+
this.sender = this.makeSender();
|
|
268
|
+
this.receiver = this.makeReceiver();
|
|
269
|
+
}
|
|
270
|
+
// Use a static getter to avoid duplicate connections
|
|
271
|
+
static get(params) {
|
|
272
|
+
if (!(singleton === null || singleton === void 0 ? void 0 : singleton.hasParams(params))) {
|
|
273
|
+
singleton === null || singleton === void 0 ? void 0 : singleton.teardown();
|
|
274
|
+
singleton = new Nip46Broker(params);
|
|
275
|
+
}
|
|
276
|
+
return singleton;
|
|
213
277
|
}
|
|
278
|
+
// Expose params without exposing params
|
|
214
279
|
hasParams(params) {
|
|
215
280
|
return equals(this.params, params);
|
|
216
281
|
}
|
|
217
282
|
}
|
|
218
|
-
_Nip46Broker_signer = new WeakMap(), _Nip46Broker_handler = new WeakMap(), _Nip46Broker_algorithm = new WeakMap(), _Nip46Broker_closed = new WeakMap(), _Nip46Broker_processing = new WeakMap(), _Nip46Broker_connectResponse = new WeakMap(), _Nip46Broker_queue = new WeakMap(), _Nip46Broker_window = new WeakMap(), _Nip46Broker_sub = new WeakMap(), _Nip46Broker_subscribe = new WeakMap(), _Nip46Broker_processQueue = new WeakMap(), _Nip46Broker_getResult = new WeakMap();
|
|
219
283
|
export class Nip46Signer {
|
|
220
284
|
constructor(broker) {
|
|
221
285
|
this.broker = broker;
|
|
222
|
-
_Nip46Signer_pubkey.set(this, void 0);
|
|
223
286
|
this.getPubkey = async () => {
|
|
224
|
-
if (!
|
|
225
|
-
|
|
287
|
+
if (!this.pubkey) {
|
|
288
|
+
this.pubkey = await this.broker.getPublicKey();
|
|
226
289
|
}
|
|
227
|
-
return
|
|
290
|
+
return this.pubkey;
|
|
228
291
|
};
|
|
229
292
|
this.sign = async (template) => this.broker.signEvent(hash(own(template, await this.getPubkey())));
|
|
230
293
|
this.nip04 = {
|
|
@@ -237,5 +300,4 @@ export class Nip46Signer {
|
|
|
237
300
|
};
|
|
238
301
|
}
|
|
239
302
|
}
|
|
240
|
-
_Nip46Signer_pubkey = new WeakMap();
|
|
241
303
|
//# sourceMappingURL=nip46.mjs.map
|