applesauce-signers 2.0.0 → 3.1.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,38 +1,22 @@
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, getConnectionMethods, } from "applesauce-signers";
7
5
  import { nanoid } from "nanoid";
6
+ import { getPublicKey, kinds, verifyEvent } from "nostr-tools";
7
+ import { filter, from, repeat, retry } from "rxjs";
8
8
  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 = {}));
9
+ import { NostrConnectMethod, buildSigningPermissions, createNostrConnectURI, parseBunkerURI, } from "../helpers/nostr-connect.js";
32
10
  async function defaultHandleAuth(url) {
33
11
  window.open(url, "auth", "width=400,height=600,resizable=no,status=no,location=no,toolbar=no,menubar=no");
34
12
  }
35
13
  export class NostrConnectSigner {
14
+ /** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
15
+ static subscriptionMethod = undefined;
16
+ /** A fallback method to use for publishMethod if none is pass in when creating the signer */
17
+ static publishMethod = undefined;
18
+ /** A fallback pool to use if none is pass in when creating the signer */
19
+ static pool = undefined;
36
20
  /** A method that is called when an event needs to be published */
37
21
  publishMethod;
38
22
  /** The active nostr subscription */
@@ -58,28 +42,23 @@ export class NostrConnectSigner {
58
42
  onAuth = defaultHandleAuth;
59
43
  verifyEvent = verifyEvent;
60
44
  /** A secret used when initiating a connection from the client side */
61
- clientSecret = nanoid(12);
45
+ secret;
62
46
  nip04;
63
47
  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
- constructor(opts) {
69
- this.relays = opts.relays;
70
- this.pubkey = opts.pubkey;
71
- this.remote = opts.remote;
72
- const subscriptionMethod = opts.subscriptionMethod || NostrConnectSigner.subscriptionMethod;
73
- if (!subscriptionMethod)
74
- throw new Error("Missing subscriptionMethod, either pass a method or set NostrConnectSigner.subscriptionMethod");
75
- const publishMethod = opts.publishMethod || NostrConnectSigner.publishMethod;
76
- if (!publishMethod)
77
- throw new Error("Missing publishMethod, either pass a method or set NostrConnectSigner.publishMethod");
78
- this.subscriptionMethod = subscriptionMethod;
79
- this.publishMethod = publishMethod;
80
- if (opts.onAuth)
81
- this.onAuth = opts.onAuth;
82
- this.signer = opts?.signer || new SimpleSigner();
48
+ constructor(options) {
49
+ this.relays = options.relays;
50
+ this.pubkey = options.pubkey;
51
+ this.remote = options.remote;
52
+ this.secret = options.secret || nanoid(12);
53
+ // Get the subscription and publish methods
54
+ const { subscriptionMethod, publishMethod } = getConnectionMethods(options, NostrConnectSigner);
55
+ // Use arrow functions so "this" isn't bound to the signer
56
+ this.subscriptionMethod = (relays, filters) => subscriptionMethod(relays, filters);
57
+ this.publishMethod = (relays, event) => publishMethod(relays, event);
58
+ if (options.onAuth)
59
+ this.onAuth = options.onAuth;
60
+ // Get or create the local signer
61
+ this.signer = options?.signer || new SimpleSigner();
83
62
  this.nip04 = {
84
63
  encrypt: this.nip04Encrypt.bind(this),
85
64
  decrypt: this.nip04Decrypt.bind(this),
@@ -98,14 +77,20 @@ export class NostrConnectSigner {
98
77
  this.listening = true;
99
78
  const pubkey = await this.signer.getPublicKey();
100
79
  // Setup subscription
101
- this.req = this.subscriptionMethod(this.relays, [
80
+ this.req = from(this.subscriptionMethod(this.relays, [
102
81
  {
103
82
  kinds: [kinds.NostrConnect],
104
83
  "#p": [pubkey],
105
84
  },
106
- ]).subscribe({
107
- next: (event) => typeof event !== "string" && this.handleEvent(event),
108
- });
85
+ ]))
86
+ .pipe(
87
+ // Keep the connection open indefinitely
88
+ repeat(),
89
+ // Retry on connection failure
90
+ retry(),
91
+ // Ignore strings (support for applesauce-relay)
92
+ filter((event) => typeof event !== "string"))
93
+ .subscribe(this.handleEvent.bind(this));
109
94
  this.log("Opened", this.relays);
110
95
  }
111
96
  /** Close the connection */
@@ -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 "../interop.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 "../interop.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 "../interop.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 "../interop.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.1.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,18 @@
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.1.0",
40
42
  "debug": "^4.4.0",
41
43
  "nanoid": "^5.0.9",
42
- "nostr-tools": "^2.13"
44
+ "nostr-tools": "~2.15",
45
+ "rxjs": "^7.8.2"
43
46
  },
44
47
  "devDependencies": {
45
48
  "@types/debug": "^4.1.12",
46
49
  "@types/dom-serial": "^1.0.6",
47
50
  "typescript": "^5.8.3",
48
- "vitest": "^3.1.3"
51
+ "vitest": "^3.1.3",
52
+ "vitest-websocket-mock": "^0.5.0"
49
53
  },
50
54
  "funding": {
51
55
  "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
- };
package/dist/nip-07.js DELETED
@@ -1 +0,0 @@
1
- export {};