applesauce-signers 0.0.0-next-20250315140539 → 0.0.0-next-20250323180355

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.
@@ -13,6 +13,7 @@ export declare class ExtensionSigner implements Nip07Interface {
13
13
  encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
14
14
  decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
15
15
  } | undefined;
16
+ protected pubkey: string | undefined;
16
17
  getPublicKey(): string | Promise<string>;
17
18
  getRelays(): Record<string, {
18
19
  read: boolean;
@@ -9,10 +9,22 @@ export class ExtensionSigner {
9
9
  get nip44() {
10
10
  return window.nostr?.nip44;
11
11
  }
12
+ pubkey = undefined;
12
13
  getPublicKey() {
13
14
  if (!window.nostr)
14
15
  throw new ExtensionMissingError("Signer extension missing");
15
- return window.nostr.getPublicKey();
16
+ if (this.pubkey)
17
+ return this.pubkey;
18
+ const p = window.nostr.getPublicKey();
19
+ if (p instanceof Promise)
20
+ return p.then((pubkey) => {
21
+ this.pubkey = pubkey;
22
+ return pubkey;
23
+ });
24
+ else {
25
+ this.pubkey = p;
26
+ return p;
27
+ }
16
28
  }
17
29
  getRelays() {
18
30
  if (!window.nostr)
@@ -64,17 +64,26 @@ export type NostrConnectSignerOptions = {
64
64
  remote?: string;
65
65
  /** Users pubkey */
66
66
  pubkey?: string;
67
- };
68
- export type NostrConnectConnectionMethods = {
69
- /** A method that is called when the subscription needs to be updated */
70
- onSubOpen: (filters: Filter[], relays: string[], onEvent: (event: NostrEvent) => void) => Promise<void>;
71
- /** A method called when the subscription should be closed */
72
- onSubClose: () => Promise<void>;
73
- /** A method that is called when an event needs to be published */
74
- onPublishEvent: (event: NostrEvent, relays: string[]) => Promise<void>;
75
67
  /** A method for handling "auth" requests */
76
68
  onAuth?: (url: string) => Promise<void>;
69
+ /** A method for subscribing to relays */
70
+ subscriptionMethod?: NostrSubscriptionMethod;
71
+ /** A method for publishing events */
72
+ publishMethod?: NostrPublishMethod;
73
+ };
74
+ interface Unsubscribable {
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;
77
84
  };
85
+ export type NostrSubscriptionMethod = (filters: Filter[], relays: string[]) => Subscribable<NostrEvent>;
86
+ export type NostrPublishMethod = (event: NostrEvent, relays: string[]) => void | Promise<void>;
78
87
  export type NostrConnectAppMetadata = {
79
88
  name?: string;
80
89
  image?: string;
@@ -82,12 +91,10 @@ export type NostrConnectAppMetadata = {
82
91
  permissions?: string[];
83
92
  };
84
93
  export declare class NostrConnectSigner implements Nip07Interface {
85
- /** A method that is called when the subscription needs to be updated */
86
- onSubOpen?: (filters: Filter[], relays: string[], onEvent: (event: NostrEvent) => void) => Promise<void>;
87
- /** A method called when the subscription should be closed */
88
- onSubClose?: () => Promise<void>;
89
94
  /** A method that is called when an event needs to be published */
90
- onPublishEvent?: (event: NostrEvent, relays: string[]) => Promise<void>;
95
+ protected publishMethod: NostrPublishMethod;
96
+ /** The active nostr subscription */
97
+ protected subscriptionMethod: NostrSubscriptionMethod;
91
98
  protected log: import("debug").Debugger;
92
99
  /** The local client signer */
93
100
  signer: SimpleSigner;
@@ -102,6 +109,7 @@ export declare class NostrConnectSigner implements Nip07Interface {
102
109
  remote?: string;
103
110
  /** Client pubkey */
104
111
  get clientPubkey(): string;
112
+ /** A method for handling "auth" requests */
105
113
  onAuth: (url: string) => Promise<void>;
106
114
  verifyEvent: typeof verifyEvent;
107
115
  /** A secret used when initiating a connection from the client side */
@@ -114,7 +122,11 @@ export declare class NostrConnectSigner implements Nip07Interface {
114
122
  encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
115
123
  decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
116
124
  } | undefined;
117
- constructor(opts: NostrConnectConnectionMethods & NostrConnectSignerOptions);
125
+ static subscriptionMethod: NostrSubscriptionMethod | undefined;
126
+ static publishMethod: NostrPublishMethod | undefined;
127
+ constructor(opts: NostrConnectSignerOptions);
128
+ /** The currently active REQ subscription */
129
+ protected req?: Unsubscribable;
118
130
  /** Open the connection */
119
131
  open(): Promise<void>;
120
132
  /** Close the connection */
@@ -155,7 +167,7 @@ export declare class NostrConnectSigner implements Nip07Interface {
155
167
  /** Builds an array of signing permissions for event kinds */
156
168
  static buildSigningPermissions(kinds: number[]): string[];
157
169
  /** Create a {@link NostrConnectSigner} from a bunker:// URI */
158
- static fromBunkerURI(uri: string, options: NostrConnectConnectionMethods & {
170
+ static fromBunkerURI(uri: string, options: Omit<NostrConnectSignerOptions, "relays"> & {
159
171
  permissions?: string[];
160
172
  signer?: SimpleSigner;
161
173
  }): Promise<NostrConnectSigner>;
@@ -33,14 +33,10 @@ async function defaultHandleAuth(url) {
33
33
  window.open(url, "auth", "width=400,height=600,resizable=no,status=no,location=no,toolbar=no,menubar=no");
34
34
  }
35
35
  export class NostrConnectSigner {
36
- /** A method that is called when the subscription needs to be updated */
37
- onSubOpen;
38
- /** A method called when the subscription should be closed */
39
- onSubClose;
40
36
  /** A method that is called when an event needs to be published */
41
- onPublishEvent;
42
- // protected pool: IConnectionPool;
43
- // protected sub: MultiSubscription;
37
+ publishMethod;
38
+ /** The active nostr subscription */
39
+ subscriptionMethod;
44
40
  log = logger.extend("NostrConnectSigner");
45
41
  /** The local client signer */
46
42
  signer;
@@ -57,19 +53,27 @@ export class NostrConnectSigner {
57
53
  get clientPubkey() {
58
54
  return getPublicKey(this.signer.key);
59
55
  }
56
+ /** A method for handling "auth" requests */
60
57
  onAuth = defaultHandleAuth;
61
58
  verifyEvent = verifyEvent;
62
59
  /** A secret used when initiating a connection from the client side */
63
60
  clientSecret = nanoid(12);
64
61
  nip04;
65
62
  nip44;
63
+ static subscriptionMethod = undefined;
64
+ static publishMethod = undefined;
66
65
  constructor(opts) {
67
66
  this.relays = opts.relays;
68
67
  this.pubkey = opts.pubkey;
69
68
  this.remote = opts.remote;
70
- this.onSubOpen = opts.onSubOpen;
71
- this.onSubClose = opts.onSubClose;
72
- this.onPublishEvent = opts.onPublishEvent;
69
+ const subscriptionMethod = opts.subscriptionMethod || NostrConnectSigner.subscriptionMethod;
70
+ if (!subscriptionMethod)
71
+ throw new Error("Missing subscriptionMethod, either pass a method or set NostrConnectSigner.subscriptionMethod");
72
+ const publishMethod = opts.publishMethod || NostrConnectSigner.publishMethod;
73
+ if (!publishMethod)
74
+ throw new Error("Missing publishMethod, either pass a method or set NostrConnectSigner.publishMethod");
75
+ this.subscriptionMethod = subscriptionMethod;
76
+ this.publishMethod = publishMethod;
73
77
  if (opts.onAuth)
74
78
  this.onAuth = opts.onAuth;
75
79
  this.signer = opts?.signer || new SimpleSigner();
@@ -82,6 +86,8 @@ export class NostrConnectSigner {
82
86
  decrypt: this.nip44Decrypt.bind(this),
83
87
  };
84
88
  }
89
+ /** The currently active REQ subscription */
90
+ req;
85
91
  /** Open the connection */
86
92
  async open() {
87
93
  if (this.subscriptionOpen)
@@ -89,19 +95,21 @@ export class NostrConnectSigner {
89
95
  this.subscriptionOpen = true;
90
96
  const pubkey = await this.signer.getPublicKey();
91
97
  // Setup subscription
92
- await this.onSubOpen?.([
98
+ this.req = this.subscriptionMethod([
93
99
  {
94
100
  kinds: [kinds.NostrConnect],
95
101
  "#p": [pubkey],
96
102
  },
97
- ], this.relays, this.handleEvent.bind(this));
103
+ ], this.relays).subscribe({
104
+ next: (event) => this.handleEvent(event),
105
+ });
98
106
  this.log("Opened", this.relays);
99
107
  }
100
108
  /** Close the connection */
101
109
  async close() {
102
110
  this.subscriptionOpen = false;
103
111
  this.isConnected = false;
104
- await this.onSubClose?.();
112
+ this.req?.unsubscribe();
105
113
  this.log("Closed");
106
114
  }
107
115
  requests = new Map();
@@ -178,7 +186,7 @@ export class NostrConnectSigner {
178
186
  this.log(`Sending request ${id} (${method}) ${JSON.stringify(params)}`);
179
187
  const p = createDefer();
180
188
  this.requests.set(id, p);
181
- await this.onPublishEvent?.(event, this.relays);
189
+ await this.publishMethod?.(event, this.relays);
182
190
  return p;
183
191
  }
184
192
  /** Connect to remote signer */
@@ -3,23 +3,21 @@ import { NostrConnectSigner } from "./nostr-connect-signer.js";
3
3
  import { SimpleSigner } from "./simple-signer.js";
4
4
  describe("NostrConnectSigner", () => {
5
5
  describe("connection", () => {
6
- it("should call onSubOpen with filters", async () => {
6
+ it("should call subscription method with filters", async () => {
7
7
  const relays = ["wss://relay.signer.com"];
8
- const onSubOpen = vi.fn(async () => { });
9
- const onSubClose = vi.fn(async () => { });
10
- const onPublishEvent = vi.fn(async () => { });
8
+ const subscription = vi.fn().mockReturnValue({ subscribe: vi.fn() });
9
+ const publish = vi.fn(async () => { });
11
10
  const client = new SimpleSigner();
12
11
  const remote = new SimpleSigner();
13
12
  const signer = new NostrConnectSigner({
14
- onSubOpen,
15
- onSubClose,
16
- onPublishEvent,
17
13
  relays,
18
14
  remote: await remote.getPublicKey(),
19
15
  signer: client,
16
+ subscriptionMethod: subscription,
17
+ publishMethod: publish,
20
18
  });
21
19
  signer.connect();
22
- expect(onSubOpen).toHaveBeenCalledWith([{ "#p": [await client.getPublicKey()], kinds: [24133] }], relays, expect.any(Function));
20
+ expect(subscription).toHaveBeenCalledWith([{ "#p": [await client.getPublicKey()], kinds: [24133] }], relays);
23
21
  });
24
22
  });
25
23
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-signers",
3
- "version": "0.0.0-next-20250315140539",
3
+ "version": "0.0.0-next-20250323180355",
4
4
  "description": "Signer classes for applesauce",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "@noble/hashes": "^1.7.1",
37
37
  "@noble/secp256k1": "^1.7.1",
38
38
  "@scure/base": "^1.2.4",
39
- "applesauce-core": "0.0.0-next-20250315140539",
39
+ "applesauce-core": "^0.12.0",
40
40
  "debug": "^4.4.0",
41
41
  "nanoid": "^5.0.9",
42
42
  "nostr-tools": "^2.10.4"