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.
@@ -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,6 +14,12 @@ 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;
@@ -58,25 +41,27 @@ export class NostrConnectSigner {
58
41
  onAuth = defaultHandleAuth;
59
42
  verifyEvent = verifyEvent;
60
43
  /** A secret used when initiating a connection from the client side */
61
- clientSecret = nanoid(12);
44
+ secret;
62
45
  nip04;
63
46
  nip44;
64
- /** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
65
- static subscriptionMethod = undefined;
66
- /** A fallback method to use for publishMethod if none is pass in when creating the signer */
67
- static publishMethod = undefined;
68
47
  constructor(opts) {
69
48
  this.relays = opts.relays;
70
49
  this.pubkey = opts.pubkey;
71
50
  this.remote = opts.remote;
72
- 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;
73
57
  if (!subscriptionMethod)
74
58
  throw new Error("Missing subscriptionMethod, either pass a method or set NostrConnectSigner.subscriptionMethod");
75
- const publishMethod = opts.publishMethod || NostrConnectSigner.publishMethod;
59
+ const publishMethod = opts.publishMethod || opts.pool?.publish || NostrConnectSigner.publishMethod || NostrConnectSigner.pool?.publish;
76
60
  if (!publishMethod)
77
61
  throw new Error("Missing publishMethod, either pass a method or set NostrConnectSigner.publishMethod");
78
- this.subscriptionMethod = subscriptionMethod;
79
- 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);
80
65
  if (opts.onAuth)
81
66
  this.onAuth = opts.onAuth;
82
67
  this.signer = opts?.signer || new SimpleSigner();
@@ -134,12 +119,15 @@ export class NostrConnectSigner {
134
119
  if (this.remote && event.pubkey !== this.remote)
135
120
  return;
136
121
  try {
137
- const responseStr = isNIP04(event.content)
138
- ? await this.signer.nip04.decrypt(event.pubkey, event.content)
139
- : 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;
140
128
  const response = JSON.parse(responseStr);
141
129
  // handle remote signer connection
142
- 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))) {
143
131
  this.log("Got ack response from", event.pubkey, response.result);
144
132
  this.isConnected = true;
145
133
  this.remote = event.pubkey;
@@ -316,37 +304,20 @@ export class NostrConnectSigner {
316
304
  }
317
305
  /** Returns the nostrconnect:// URI for this signer */
318
306
  getNostrConnectURI(metadata) {
319
- const params = new URLSearchParams();
320
- params.set("secret", this.clientSecret);
321
- if (metadata?.name)
322
- params.set("name", metadata.name);
323
- if (metadata?.url)
324
- params.set("url", String(metadata.url));
325
- if (metadata?.image)
326
- params.set("image", metadata.image);
327
- if (metadata?.permissions)
328
- params.set("perms", metadata.permissions.join(","));
329
- for (const relay of this.relays)
330
- params.append("relay", relay);
331
- const client = getPublicKey(this.signer.key);
332
- 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
+ });
333
313
  }
334
314
  /** Parses a bunker:// URI */
335
315
  static parseBunkerURI(uri) {
336
- const url = new URL(uri);
337
- // firefox puts pubkey part in host, chrome puts pubkey in pathname
338
- const remote = url.host || url.pathname.replace("//", "");
339
- if (!isHexKey(remote))
340
- throw new Error("Invalid connection URI");
341
- const relays = url.searchParams.getAll("relay");
342
- if (relays.length === 0)
343
- throw new Error("Missing relays");
344
- const secret = url.searchParams.get("secret") ?? undefined;
345
- return { remote, relays, secret };
316
+ return parseBunkerURI(uri);
346
317
  }
347
318
  /** Builds an array of signing permissions for event kinds */
348
319
  static buildSigningPermissions(kinds) {
349
- return [Permission.GetPublicKey, ...kinds.map((k) => `${Permission.SignEvent}:${k}`)];
320
+ return buildSigningPermissions(kinds);
350
321
  }
351
322
  /** Create a {@link NostrConnectSigner} from a bunker:// URI */
352
323
  static async fromBunkerURI(uri, options) {
@@ -1,17 +1,17 @@
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();
@@ -52,9 +52,14 @@ export class PasswordSigner {
52
52
  if (this.key)
53
53
  return;
54
54
  if (this.ncryptsec) {
55
- this.key = decrypt(this.ncryptsec, password);
56
- if (!this.key)
57
- 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
+ }
58
63
  }
59
64
  else
60
65
  throw new Error("Missing ncryptsec");
@@ -1,24 +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>;
22
21
  /** Creates a ReadonlySigner from a hex public key or NIP-19 npub */
23
22
  static fromPubkey(pubkey: string): ReadonlySigner;
24
23
  }
@@ -17,25 +17,22 @@ export class ReadonlySigner {
17
17
  decrypt: this.nip44Decrypt.bind(this),
18
18
  };
19
19
  }
20
- getPublicKey() {
20
+ async getPublicKey() {
21
21
  return this.pubkey;
22
22
  }
23
- getRelays() {
24
- return {};
25
- }
26
- signEvent() {
23
+ async signEvent() {
27
24
  throw new Error("Cant sign events with readonly");
28
25
  }
29
- nip04Encrypt() {
26
+ async nip04Encrypt() {
30
27
  throw new Error("Cant encrypt with readonly");
31
28
  }
32
- nip04Decrypt() {
29
+ async nip04Decrypt() {
33
30
  throw new Error("Cant decrypt with readonly");
34
31
  }
35
- nip44Encrypt() {
32
+ async nip44Encrypt() {
36
33
  throw new Error("Cant encrypt with readonly");
37
34
  }
38
- nip44Decrypt() {
35
+ async nip44Decrypt() {
39
36
  throw new Error("Cant decrypt with readonly");
40
37
  }
41
38
  /** Creates a ReadonlySigner from a hex public key or NIP-19 npub */
@@ -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>;
package/package.json CHANGED
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "name": "applesauce-signers",
3
- "version": "2.0.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,7 +38,7 @@
36
38
  "@noble/hashes": "^1.7.1",
37
39
  "@noble/secp256k1": "^1.7.1",
38
40
  "@scure/base": "^1.2.4",
39
- "applesauce-core": "^2.0.0",
41
+ "applesauce-core": "^3.0.0",
40
42
  "debug": "^4.4.0",
41
43
  "nanoid": "^5.0.9",
42
44
  "nostr-tools": "^2.13"
@@ -45,7 +47,8 @@
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.3"
50
+ "vitest": "^3.1.3",
51
+ "vitest-websocket-mock": "^0.5.0"
49
52
  },
50
53
  "funding": {
51
54
  "type": "lightning",
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
- };
File without changes