applesauce-signers 1.2.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.
Files changed (38) hide show
  1. package/dist/helpers/encryption.d.ts +1 -1
  2. package/dist/helpers/encryption.js +1 -6
  3. package/dist/helpers/index.d.ts +1 -0
  4. package/dist/helpers/index.js +1 -0
  5. package/dist/helpers/nostr-connect.d.ts +92 -0
  6. package/dist/helpers/nostr-connect.js +93 -0
  7. package/dist/helpers/observable.d.ts +11 -0
  8. package/dist/helpers/observable.js +2 -0
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/interface.d.ts +15 -0
  12. package/dist/signers/amber-clipboard-signer.d.ts +6 -6
  13. package/dist/signers/extension-signer.d.ts +9 -16
  14. package/dist/signers/extension-signer.js +9 -20
  15. package/dist/signers/index.d.ts +1 -0
  16. package/dist/signers/index.js +1 -0
  17. package/dist/signers/nostr-connect-provider.d.ts +128 -0
  18. package/dist/signers/nostr-connect-provider.js +406 -0
  19. package/dist/signers/nostr-connect-signer.d.ts +33 -93
  20. package/dist/signers/nostr-connect-signer.js +67 -75
  21. package/dist/signers/password-signer.d.ts +15 -6
  22. package/dist/signers/password-signer.js +31 -3
  23. package/dist/signers/readonly-signer.d.ts +14 -13
  24. package/dist/signers/readonly-signer.js +13 -9
  25. package/dist/signers/serial-port-signer.d.ts +4 -4
  26. package/dist/signers/simple-signer.d.ts +4 -1
  27. package/dist/signers/simple-signer.js +5 -0
  28. package/package.json +7 -4
  29. package/dist/__tests__/exports.test.js +0 -22
  30. package/dist/helpers/__tests__/exports.test.d.ts +0 -1
  31. package/dist/helpers/__tests__/exports.test.js +0 -11
  32. package/dist/nip-07.d.ts +0 -20
  33. package/dist/nip-07.js +0 -1
  34. package/dist/signers/__tests__/exports.test.d.ts +0 -1
  35. package/dist/signers/__tests__/exports.test.js +0 -21
  36. package/dist/signers/__tests__/nostr-connect-signer.test.d.ts +0 -1
  37. package/dist/signers/__tests__/nostr-connect-signer.test.js +0 -23
  38. /package/dist/{__tests__/exports.test.d.ts → interface.js} +0 -0
@@ -1,34 +1,11 @@
1
- import { kinds, verifyEvent } from "nostr-tools";
2
- import { SimpleSigner } from "applesauce-signers";
3
- import { createDefer } from "applesauce-core/promise";
4
- import { isHexKey, unixNow } from "applesauce-core/helpers";
5
1
  import { logger } from "applesauce-core";
6
- import { getPublicKey } from "nostr-tools";
2
+ import { getHiddenContent, unixNow } from "applesauce-core/helpers";
3
+ import { createDefer } from "applesauce-core/promise";
4
+ import { SimpleSigner } from "applesauce-signers";
7
5
  import { nanoid } from "nanoid";
6
+ import { getPublicKey, kinds, verifyEvent } from "nostr-tools";
8
7
  import { isNIP04 } from "../helpers/encryption.js";
9
- export function isErrorResponse(response) {
10
- return !!response.error;
11
- }
12
- export var Permission;
13
- (function (Permission) {
14
- Permission["GetPublicKey"] = "get_pubic_key";
15
- Permission["SignEvent"] = "sign_event";
16
- Permission["Nip04Encrypt"] = "nip04_encrypt";
17
- Permission["Nip04Decrypt"] = "nip04_decrypt";
18
- Permission["Nip44Encrypt"] = "nip44_encrypt";
19
- Permission["Nip44Decrypt"] = "nip44_decrypt";
20
- })(Permission || (Permission = {}));
21
- export var NostrConnectMethod;
22
- (function (NostrConnectMethod) {
23
- NostrConnectMethod["Connect"] = "connect";
24
- NostrConnectMethod["CreateAccount"] = "create_account";
25
- NostrConnectMethod["GetPublicKey"] = "get_public_key";
26
- NostrConnectMethod["SignEvent"] = "sign_event";
27
- NostrConnectMethod["Nip04Encrypt"] = "nip04_encrypt";
28
- NostrConnectMethod["Nip04Decrypt"] = "nip04_decrypt";
29
- NostrConnectMethod["Nip44Encrypt"] = "nip44_encrypt";
30
- NostrConnectMethod["Nip44Decrypt"] = "nip44_decrypt";
31
- })(NostrConnectMethod || (NostrConnectMethod = {}));
8
+ import { NostrConnectMethod, buildSigningPermissions, createNostrConnectURI, parseBunkerURI, } from "../helpers/nostr-connect.js";
32
9
  async function defaultHandleAuth(url) {
33
10
  window.open(url, "auth", "width=400,height=600,resizable=no,status=no,location=no,toolbar=no,menubar=no");
34
11
  }
@@ -37,10 +14,17 @@ export class NostrConnectSigner {
37
14
  publishMethod;
38
15
  /** The active nostr subscription */
39
16
  subscriptionMethod;
17
+ /** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
18
+ static subscriptionMethod = undefined;
19
+ /** A fallback method to use for publishMethod if none is pass in when creating the signer */
20
+ static publishMethod = undefined;
21
+ /** A fallback pool to use if none is pass in when creating the signer */
22
+ static pool = undefined;
40
23
  log = logger.extend("NostrConnectSigner");
41
24
  /** The local client signer */
42
25
  signer;
43
- subscriptionOpen = false;
26
+ /** Whether the signer is listening for events */
27
+ listening = false;
44
28
  /** Whether the signer is connected to the remote signer */
45
29
  isConnected = false;
46
30
  /** The users pubkey */
@@ -57,25 +41,27 @@ export class NostrConnectSigner {
57
41
  onAuth = defaultHandleAuth;
58
42
  verifyEvent = verifyEvent;
59
43
  /** A secret used when initiating a connection from the client side */
60
- clientSecret = nanoid(12);
44
+ secret;
61
45
  nip04;
62
46
  nip44;
63
- /** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
64
- static subscriptionMethod = undefined;
65
- /** A fallback method to use for publishMethod if none is pass in when creating the signer */
66
- static publishMethod = undefined;
67
47
  constructor(opts) {
68
48
  this.relays = opts.relays;
69
49
  this.pubkey = opts.pubkey;
70
50
  this.remote = opts.remote;
71
- const subscriptionMethod = opts.subscriptionMethod || NostrConnectSigner.subscriptionMethod;
51
+ this.secret = opts.secret || nanoid(12);
52
+ // Get the subscription and publish methods
53
+ const subscriptionMethod = opts.subscriptionMethod ||
54
+ opts.pool?.subscription ||
55
+ NostrConnectSigner.subscriptionMethod ||
56
+ NostrConnectSigner.pool?.subscription;
72
57
  if (!subscriptionMethod)
73
58
  throw new Error("Missing subscriptionMethod, either pass a method or set NostrConnectSigner.subscriptionMethod");
74
- const publishMethod = opts.publishMethod || NostrConnectSigner.publishMethod;
59
+ const publishMethod = opts.publishMethod || opts.pool?.publish || NostrConnectSigner.publishMethod || NostrConnectSigner.pool?.publish;
75
60
  if (!publishMethod)
76
61
  throw new Error("Missing publishMethod, either pass a method or set NostrConnectSigner.publishMethod");
77
- this.subscriptionMethod = subscriptionMethod;
78
- this.publishMethod = publishMethod;
62
+ // Use arrow functions so "this" isn't bound to the signer
63
+ this.subscriptionMethod = (relays, filters) => subscriptionMethod(relays, filters);
64
+ this.publishMethod = (relays, event) => publishMethod(relays, event);
79
65
  if (opts.onAuth)
80
66
  this.onAuth = opts.onAuth;
81
67
  this.signer = opts?.signer || new SimpleSigner();
@@ -92,9 +78,9 @@ export class NostrConnectSigner {
92
78
  req;
93
79
  /** Open the connection */
94
80
  async open() {
95
- if (this.subscriptionOpen)
81
+ if (this.listening)
96
82
  return;
97
- this.subscriptionOpen = true;
83
+ this.listening = true;
98
84
  const pubkey = await this.signer.getPublicKey();
99
85
  // Setup subscription
100
86
  this.req = this.subscriptionMethod(this.relays, [
@@ -103,15 +89,24 @@ export class NostrConnectSigner {
103
89
  "#p": [pubkey],
104
90
  },
105
91
  ]).subscribe({
106
- next: (event) => this.handleEvent(event),
92
+ next: (event) => typeof event !== "string" && this.handleEvent(event),
107
93
  });
108
94
  this.log("Opened", this.relays);
109
95
  }
110
96
  /** Close the connection */
111
97
  async close() {
112
- this.subscriptionOpen = false;
98
+ this.listening = false;
113
99
  this.isConnected = false;
114
- this.req?.unsubscribe();
100
+ // Close the current subscription
101
+ if (this.req) {
102
+ this.req.unsubscribe();
103
+ this.req = undefined;
104
+ }
105
+ // Cancel waiting promise
106
+ if (this.waitingPromise) {
107
+ this.waitingPromise.reject(new Error("Closed"));
108
+ this.waitingPromise = null;
109
+ }
115
110
  this.log("Closed");
116
111
  }
117
112
  requests = new Map();
@@ -124,12 +119,15 @@ export class NostrConnectSigner {
124
119
  if (this.remote && event.pubkey !== this.remote)
125
120
  return;
126
121
  try {
127
- const responseStr = isNIP04(event.content)
128
- ? await this.signer.nip04.decrypt(event.pubkey, event.content)
129
- : await this.signer.nip44.decrypt(event.pubkey, event.content);
122
+ const responseStr = getHiddenContent(event) ??
123
+ (isNIP04(event.content)
124
+ ? await this.signer.nip04.decrypt(event.pubkey, event.content)
125
+ : await this.signer.nip44.decrypt(event.pubkey, event.content));
126
+ if (!responseStr)
127
+ return;
130
128
  const response = JSON.parse(responseStr);
131
129
  // handle remote signer connection
132
- if (!this.remote && (response.result === "ack" || (this.clientSecret && response.result === this.clientSecret))) {
130
+ if (!this.remote && (response.result === "ack" || (this.secret && response.result === this.secret))) {
133
131
  this.log("Got ack response from", event.pubkey, response.result);
134
132
  this.isConnected = true;
135
133
  this.remote = event.pubkey;
@@ -185,10 +183,16 @@ export class NostrConnectSigner {
185
183
  const request = { id, method, params };
186
184
  const encrypted = await this.signer.nip44.encrypt(this.remote, JSON.stringify(request));
187
185
  const event = await this.createRequestEvent(encrypted, this.remote, kind);
188
- this.log(`Sending request ${id} (${method}) ${JSON.stringify(params)}`);
186
+ this.log(`Sending ${id} (${method}) ${JSON.stringify(params)}`);
189
187
  const p = createDefer();
190
188
  this.requests.set(id, p);
191
- await this.publishMethod?.(this.relays, event);
189
+ const result = this.publishMethod?.(this.relays, event);
190
+ // Handle returned Promise or Observable
191
+ if (result instanceof Promise)
192
+ await result;
193
+ else if ("subscribe" in result)
194
+ await new Promise((res) => result.subscribe({ complete: res }));
195
+ this.log(`Sent ${id} (${method})`);
192
196
  return p;
193
197
  }
194
198
  /** Connect to remote signer */
@@ -216,11 +220,16 @@ export class NostrConnectSigner {
216
220
  }
217
221
  waitingPromise = null;
218
222
  /** Wait for a remote signer to connect */
219
- waitForSigner() {
223
+ waitForSigner(abort) {
220
224
  if (this.isConnected)
221
225
  return Promise.resolve();
222
226
  this.open();
223
227
  this.waitingPromise = createDefer();
228
+ abort?.addEventListener("abort", () => {
229
+ this.waitingPromise?.reject(new Error("Aborted"));
230
+ this.waitingPromise = null;
231
+ this.close();
232
+ }, true);
224
233
  return this.waitingPromise;
225
234
  }
226
235
  /** Request to create an account on the remote signer */
@@ -295,37 +304,20 @@ export class NostrConnectSigner {
295
304
  }
296
305
  /** Returns the nostrconnect:// URI for this signer */
297
306
  getNostrConnectURI(metadata) {
298
- const params = new URLSearchParams();
299
- params.set("secret", this.clientSecret);
300
- if (metadata?.name)
301
- params.set("name", metadata.name);
302
- if (metadata?.url)
303
- params.set("url", String(metadata.url));
304
- if (metadata?.image)
305
- params.set("image", metadata.image);
306
- if (metadata?.permissions)
307
- params.set("perms", metadata.permissions.join(","));
308
- for (const relay of this.relays)
309
- params.append("relay", relay);
310
- const client = getPublicKey(this.signer.key);
311
- return `nostrconnect://${client}?` + params.toString();
307
+ return createNostrConnectURI({
308
+ client: getPublicKey(this.signer.key),
309
+ secret: this.secret,
310
+ relays: this.relays,
311
+ metadata,
312
+ });
312
313
  }
313
314
  /** Parses a bunker:// URI */
314
315
  static parseBunkerURI(uri) {
315
- const url = new URL(uri);
316
- // firefox puts pubkey part in host, chrome puts pubkey in pathname
317
- const remote = url.host || url.pathname.replace("//", "");
318
- if (!isHexKey(remote))
319
- throw new Error("Invalid connection URI");
320
- const relays = url.searchParams.getAll("relay");
321
- if (relays.length === 0)
322
- throw new Error("Missing relays");
323
- const secret = url.searchParams.get("secret") ?? undefined;
324
- return { remote, relays, secret };
316
+ return parseBunkerURI(uri);
325
317
  }
326
318
  /** Builds an array of signing permissions for event kinds */
327
319
  static buildSigningPermissions(kinds) {
328
- return [Permission.GetPublicKey, ...kinds.map((k) => `${Permission.SignEvent}:${k}`)];
320
+ return buildSigningPermissions(kinds);
329
321
  }
330
322
  /** Create a {@link NostrConnectSigner} from a bunker:// URI */
331
323
  static async fromBunkerURI(uri, options) {
@@ -1,29 +1,38 @@
1
1
  import { EventTemplate } from "nostr-tools";
2
2
  import { Deferred } from "applesauce-core/promise";
3
- import { Nip07Interface } from "../nip-07.js";
3
+ import { ISigner } from "../interface.js";
4
4
  /** A NIP-49 (Private Key Encryption) signer */
5
- export declare class PasswordSigner implements Nip07Interface {
5
+ export declare class PasswordSigner implements ISigner {
6
6
  key: Uint8Array | null;
7
7
  ncryptsec?: string;
8
8
  nip04: {
9
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
10
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
9
+ encrypt: (pubkey: string, plaintext: string) => Promise<string>;
10
+ decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
11
11
  };
12
12
  nip44: {
13
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
14
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
13
+ encrypt: (pubkey: string, plaintext: string) => Promise<string>;
14
+ decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
15
15
  };
16
16
  get unlocked(): boolean;
17
17
  constructor();
18
18
  unlockPromise?: Deferred<void>;
19
19
  protected requestUnlock(): Deferred<void> | undefined;
20
+ /** Sets the ncryptsec from the key and password */
20
21
  setPassword(password: string): Promise<void>;
22
+ /** Tests if the provided password is correct by decrypting the ncryptsec */
21
23
  testPassword(password: string): Promise<void>;
24
+ /** Unlocks the signer by decrypting the ncryptsec using the provided password */
22
25
  unlock(password: string): Promise<void>;
26
+ /** Locks the signer by removing the unencrypted key from memory */
27
+ lock(): void;
23
28
  getPublicKey(): Promise<string>;
24
29
  signEvent(event: EventTemplate): Promise<import("nostr-tools").VerifiedEvent>;
25
30
  nip04Encrypt(pubkey: string, plaintext: string): Promise<string>;
26
31
  nip04Decrypt(pubkey: string, ciphertext: string): Promise<string>;
27
32
  nip44Encrypt(pubkey: string, plaintext: string): Promise<string>;
28
33
  nip44Decrypt(pubkey: string, ciphertext: string): Promise<string>;
34
+ /** Creates a PasswordSigner from a hex private key or NIP-19 nsec and password */
35
+ static fromPrivateKey(privateKey: Uint8Array | string, password: string): Promise<PasswordSigner>;
36
+ /** Creates a PasswordSigner from a ncryptsec and unlocks it with the provided password */
37
+ static fromNcryptsec(ncryptsec: string, password?: string): Promise<PasswordSigner>;
29
38
  }
@@ -1,6 +1,7 @@
1
1
  import { finalizeEvent, getPublicKey, nip04, nip44 } from "nostr-tools";
2
2
  import { encrypt, decrypt } from "nostr-tools/nip49";
3
3
  import { createDefer } from "applesauce-core/promise";
4
+ import { normalizeToSecretKey } from "applesauce-core/helpers";
4
5
  /** A NIP-49 (Private Key Encryption) signer */
5
6
  export class PasswordSigner {
6
7
  key = null;
@@ -30,11 +31,13 @@ export class PasswordSigner {
30
31
  this.unlockPromise = p;
31
32
  return p;
32
33
  }
34
+ /** Sets the ncryptsec from the key and password */
33
35
  async setPassword(password) {
34
36
  if (!this.key)
35
37
  throw new Error("Cant set password until unlocked");
36
38
  this.ncryptsec = encrypt(this.key, password);
37
39
  }
40
+ /** Tests if the provided password is correct by decrypting the ncryptsec */
38
41
  async testPassword(password) {
39
42
  if (this.ncryptsec) {
40
43
  const key = decrypt(this.ncryptsec, password);
@@ -44,17 +47,27 @@ export class PasswordSigner {
44
47
  else
45
48
  throw new Error("Missing ncryptsec");
46
49
  }
50
+ /** Unlocks the signer by decrypting the ncryptsec using the provided password */
47
51
  async unlock(password) {
48
52
  if (this.key)
49
53
  return;
50
54
  if (this.ncryptsec) {
51
- this.key = decrypt(this.ncryptsec, password);
52
- if (!this.key)
53
- throw new Error("Failed to decrypt key");
55
+ try {
56
+ this.key = decrypt(this.ncryptsec, password);
57
+ if (!this.key)
58
+ throw new Error("Failed to decrypt key");
59
+ }
60
+ catch (error) {
61
+ throw new Error("failed to decrypt key: " + (error instanceof Error ? error.message : String(error)));
62
+ }
54
63
  }
55
64
  else
56
65
  throw new Error("Missing ncryptsec");
57
66
  }
67
+ /** Locks the signer by removing the unencrypted key from memory */
68
+ lock() {
69
+ this.key = null;
70
+ }
58
71
  // public methods
59
72
  async getPublicKey() {
60
73
  await this.requestUnlock();
@@ -82,4 +95,19 @@ export class PasswordSigner {
82
95
  await this.requestUnlock();
83
96
  return nip44.v2.decrypt(ciphertext, nip44.v2.utils.getConversationKey(this.key, pubkey));
84
97
  }
98
+ /** Creates a PasswordSigner from a hex private key or NIP-19 nsec and password */
99
+ static async fromPrivateKey(privateKey, password) {
100
+ const signer = new PasswordSigner();
101
+ signer.key = normalizeToSecretKey(privateKey);
102
+ await signer.setPassword(password);
103
+ return signer;
104
+ }
105
+ /** Creates a PasswordSigner from a ncryptsec and unlocks it with the provided password */
106
+ static async fromNcryptsec(ncryptsec, password) {
107
+ const signer = new PasswordSigner();
108
+ signer.ncryptsec = ncryptsec;
109
+ if (password)
110
+ await signer.unlock(password);
111
+ return signer;
112
+ }
85
113
  }
@@ -1,22 +1,23 @@
1
1
  import { VerifiedEvent } from "nostr-tools";
2
- import { Nip07Interface } from "../nip-07.js";
2
+ import { ISigner } from "../interface.js";
3
3
  /** A signer that only implements getPublicKey and throws on ever other method */
4
- export declare class ReadonlySigner implements Nip07Interface {
4
+ export declare class ReadonlySigner implements ISigner {
5
5
  private pubkey;
6
6
  nip04: {
7
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
8
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
7
+ encrypt: (pubkey: string, plaintext: string) => Promise<string>;
8
+ decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
9
9
  };
10
10
  nip44: {
11
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
12
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
11
+ encrypt: (pubkey: string, plaintext: string) => Promise<string>;
12
+ decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
13
13
  };
14
14
  constructor(pubkey: string);
15
- getPublicKey(): string;
16
- getRelays(): {};
17
- signEvent(): VerifiedEvent;
18
- nip04Encrypt(): string;
19
- nip04Decrypt(): string;
20
- nip44Encrypt(): string;
21
- nip44Decrypt(): string;
15
+ getPublicKey(): Promise<string>;
16
+ signEvent(): Promise<VerifiedEvent>;
17
+ nip04Encrypt(): Promise<string>;
18
+ nip04Decrypt(): Promise<string>;
19
+ nip44Encrypt(): Promise<string>;
20
+ nip44Decrypt(): Promise<string>;
21
+ /** Creates a ReadonlySigner from a hex public key or NIP-19 npub */
22
+ static fromPubkey(pubkey: string): ReadonlySigner;
22
23
  }
@@ -1,3 +1,4 @@
1
+ import { isHexKey, normalizeToPubkey } from "applesauce-core/helpers";
1
2
  /** A signer that only implements getPublicKey and throws on ever other method */
2
3
  export class ReadonlySigner {
3
4
  pubkey;
@@ -5,6 +6,8 @@ export class ReadonlySigner {
5
6
  nip44;
6
7
  constructor(pubkey) {
7
8
  this.pubkey = pubkey;
9
+ if (!isHexKey(pubkey))
10
+ throw new Error("Invalid public key");
8
11
  this.nip04 = {
9
12
  encrypt: this.nip04Encrypt.bind(this),
10
13
  decrypt: this.nip04Decrypt.bind(this),
@@ -14,25 +17,26 @@ export class ReadonlySigner {
14
17
  decrypt: this.nip44Decrypt.bind(this),
15
18
  };
16
19
  }
17
- getPublicKey() {
20
+ async getPublicKey() {
18
21
  return this.pubkey;
19
22
  }
20
- getRelays() {
21
- return {};
22
- }
23
- signEvent() {
23
+ async signEvent() {
24
24
  throw new Error("Cant sign events with readonly");
25
25
  }
26
- nip04Encrypt() {
26
+ async nip04Encrypt() {
27
27
  throw new Error("Cant encrypt with readonly");
28
28
  }
29
- nip04Decrypt() {
29
+ async nip04Decrypt() {
30
30
  throw new Error("Cant decrypt with readonly");
31
31
  }
32
- nip44Encrypt() {
32
+ async nip44Encrypt() {
33
33
  throw new Error("Cant encrypt with readonly");
34
34
  }
35
- nip44Decrypt() {
35
+ async nip44Decrypt() {
36
36
  throw new Error("Cant decrypt with readonly");
37
37
  }
38
+ /** Creates a ReadonlySigner from a hex public key or NIP-19 npub */
39
+ static fromPubkey(pubkey) {
40
+ return new ReadonlySigner(normalizeToPubkey(pubkey));
41
+ }
38
42
  }
@@ -1,6 +1,6 @@
1
1
  import { Deferred } from "applesauce-core/promise";
2
2
  import { EventTemplate, verifyEvent } from "nostr-tools";
3
- import { Nip07Interface } from "../nip-07.js";
3
+ import { ISigner } from "../interface.js";
4
4
  type Callback = () => void;
5
5
  type DeviceOpts = {
6
6
  onConnect?: Callback;
@@ -9,15 +9,15 @@ type DeviceOpts = {
9
9
  onDone?: Callback;
10
10
  };
11
11
  /** A signer that works with [nostr-signing-device](https://github.com/lnbits/nostr-signing-device) */
12
- export declare class SerialPortSigner implements Nip07Interface {
12
+ export declare class SerialPortSigner implements ISigner {
13
13
  protected log: import("debug").Debugger;
14
14
  protected writer: WritableStreamDefaultWriter<string> | null;
15
15
  pubkey?: string;
16
16
  get isConnected(): boolean;
17
17
  verifyEvent: typeof verifyEvent;
18
18
  nip04: {
19
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
20
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
19
+ encrypt: (pubkey: string, plaintext: string) => Promise<string>;
20
+ decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
21
21
  };
22
22
  constructor();
23
23
  protected lastCommand: Deferred<string> | null;
@@ -1,6 +1,7 @@
1
1
  import { EventTemplate } from "nostr-tools";
2
+ import { ISigner } from "../interface.js";
2
3
  /** A Simple NIP-07 signer class */
3
- export declare class SimpleSigner {
4
+ export declare class SimpleSigner implements ISigner {
4
5
  key: Uint8Array;
5
6
  constructor(key?: Uint8Array);
6
7
  getPublicKey(): Promise<string>;
@@ -13,4 +14,6 @@ export declare class SimpleSigner {
13
14
  encrypt: (pubkey: string, plaintext: string) => Promise<string>;
14
15
  decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
15
16
  };
17
+ /** Creates a SimpleSigner from a hex private key or NIP-19 nsec */
18
+ static fromKey(privateKey: Uint8Array | string): SimpleSigner;
16
19
  }
@@ -1,3 +1,4 @@
1
+ import { normalizeToSecretKey } from "applesauce-core/helpers";
1
2
  import { finalizeEvent, generateSecretKey, getPublicKey, nip04, nip44 } from "nostr-tools";
2
3
  /** A Simple NIP-07 signer class */
3
4
  export class SimpleSigner {
@@ -19,4 +20,8 @@ export class SimpleSigner {
19
20
  encrypt: async (pubkey, plaintext) => nip44.v2.encrypt(plaintext, nip44.v2.utils.getConversationKey(this.key, pubkey)),
20
21
  decrypt: async (pubkey, ciphertext) => nip44.v2.decrypt(ciphertext, nip44.v2.utils.getConversationKey(this.key, pubkey)),
21
22
  };
23
+ /** Creates a SimpleSigner from a hex private key or NIP-19 nsec */
24
+ static fromKey(privateKey) {
25
+ return new SimpleSigner(normalizeToSecretKey(privateKey));
26
+ }
22
27
  }
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "applesauce-signers",
3
- "version": "1.2.0",
3
+ "version": "3.0.0",
4
4
  "description": "Signer classes for applesauce",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "keywords": [
9
9
  "nostr",
10
+ "signer",
11
+ "nostr-connect",
10
12
  "applesauce"
11
13
  ],
12
14
  "author": "hzrd149",
@@ -36,16 +38,17 @@
36
38
  "@noble/hashes": "^1.7.1",
37
39
  "@noble/secp256k1": "^1.7.1",
38
40
  "@scure/base": "^1.2.4",
39
- "applesauce-core": "^1.2.0",
41
+ "applesauce-core": "^3.0.0",
40
42
  "debug": "^4.4.0",
41
43
  "nanoid": "^5.0.9",
42
- "nostr-tools": "^2.10.4"
44
+ "nostr-tools": "^2.13"
43
45
  },
44
46
  "devDependencies": {
45
47
  "@types/debug": "^4.1.12",
46
48
  "@types/dom-serial": "^1.0.6",
47
49
  "typescript": "^5.8.3",
48
- "vitest": "^3.1.1"
50
+ "vitest": "^3.1.3",
51
+ "vitest-websocket-mock": "^0.5.0"
49
52
  },
50
53
  "funding": {
51
54
  "type": "lightning",
@@ -1,22 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "AmberClipboardSigner",
8
- "ExtensionMissingError",
9
- "ExtensionSigner",
10
- "Helpers",
11
- "NostrConnectMethod",
12
- "NostrConnectSigner",
13
- "PasswordSigner",
14
- "Permission",
15
- "ReadonlySigner",
16
- "SerialPortSigner",
17
- "SimpleSigner",
18
- "isErrorResponse",
19
- ]
20
- `);
21
- });
22
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,11 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "isNIP04",
8
- ]
9
- `);
10
- });
11
- });
package/dist/nip-07.d.ts DELETED
@@ -1,20 +0,0 @@
1
- import { EventTemplate, NostrEvent } from "nostr-tools";
2
- export type Nip07Interface = {
3
- getPublicKey: () => Promise<string> | string;
4
- signEvent: (template: EventTemplate) => Promise<NostrEvent> | NostrEvent;
5
- getRelays?: () => Record<string, {
6
- read: boolean;
7
- write: boolean;
8
- }> | Promise<Record<string, {
9
- read: boolean;
10
- write: boolean;
11
- }>>;
12
- nip04?: {
13
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
14
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
15
- };
16
- nip44?: {
17
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
18
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
19
- };
20
- };
package/dist/nip-07.js DELETED
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1,21 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import * as exports from "../index.js";
3
- describe("exports", () => {
4
- it("should export the expected functions", () => {
5
- expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
6
- [
7
- "AmberClipboardSigner",
8
- "ExtensionMissingError",
9
- "ExtensionSigner",
10
- "NostrConnectMethod",
11
- "NostrConnectSigner",
12
- "PasswordSigner",
13
- "Permission",
14
- "ReadonlySigner",
15
- "SerialPortSigner",
16
- "SimpleSigner",
17
- "isErrorResponse",
18
- ]
19
- `);
20
- });
21
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,23 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { NostrConnectSigner } from "../nostr-connect-signer.js";
3
- import { SimpleSigner } from "../simple-signer.js";
4
- describe("NostrConnectSigner", () => {
5
- describe("connection", () => {
6
- it("should call subscription method with filters", async () => {
7
- const relays = ["wss://relay.signer.com"];
8
- const subscription = vi.fn().mockReturnValue({ subscribe: vi.fn() });
9
- const publish = vi.fn(async () => { });
10
- const client = new SimpleSigner();
11
- const remote = new SimpleSigner();
12
- const signer = new NostrConnectSigner({
13
- relays,
14
- remote: await remote.getPublicKey(),
15
- signer: client,
16
- subscriptionMethod: subscription,
17
- publishMethod: publish,
18
- });
19
- signer.connect();
20
- expect(subscription).toHaveBeenCalledWith(relays, [{ "#p": [await client.getPublicKey()], kinds: [24133] }]);
21
- });
22
- });
23
- });