applesauce-signers 0.0.0-next-20250315140539 → 0.0.0-next-20250323173930

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,22 @@ 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>;
77
69
  };
70
+ interface Unsubscribable {
71
+ unsubscribe(): void;
72
+ }
73
+ interface Observer<T> {
74
+ next: (value: T) => void;
75
+ error: (err: any) => void;
76
+ complete: () => void;
77
+ }
78
+ type Subscribable<T extends unknown> = {
79
+ subscribe: (observer: Partial<Observer<T>>) => Unsubscribable;
80
+ };
81
+ export type NostrSubscriptionMethod = (filters: Filter[], relays: string[]) => Subscribable<NostrEvent>;
82
+ export type NostrPublishMethod = (event: NostrEvent, relays: string[]) => void | Promise<void>;
78
83
  export type NostrConnectAppMetadata = {
79
84
  name?: string;
80
85
  image?: string;
@@ -82,12 +87,10 @@ export type NostrConnectAppMetadata = {
82
87
  permissions?: string[];
83
88
  };
84
89
  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
90
  /** A method that is called when an event needs to be published */
90
- onPublishEvent?: (event: NostrEvent, relays: string[]) => Promise<void>;
91
+ protected publish: NostrPublishMethod;
92
+ /** The active nostr subscription */
93
+ protected subscription: NostrSubscriptionMethod;
91
94
  protected log: import("debug").Debugger;
92
95
  /** The local client signer */
93
96
  signer: SimpleSigner;
@@ -102,6 +105,7 @@ export declare class NostrConnectSigner implements Nip07Interface {
102
105
  remote?: string;
103
106
  /** Client pubkey */
104
107
  get clientPubkey(): string;
108
+ /** A method for handling "auth" requests */
105
109
  onAuth: (url: string) => Promise<void>;
106
110
  verifyEvent: typeof verifyEvent;
107
111
  /** A secret used when initiating a connection from the client side */
@@ -114,7 +118,9 @@ export declare class NostrConnectSigner implements Nip07Interface {
114
118
  encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
115
119
  decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
116
120
  } | undefined;
117
- constructor(opts: NostrConnectConnectionMethods & NostrConnectSignerOptions);
121
+ constructor(subscription: NostrSubscriptionMethod, publish: NostrPublishMethod, opts: NostrConnectSignerOptions);
122
+ /** The currently active REQ subscription */
123
+ protected req?: Unsubscribable;
118
124
  /** Open the connection */
119
125
  open(): Promise<void>;
120
126
  /** Close the connection */
@@ -155,7 +161,7 @@ export declare class NostrConnectSigner implements Nip07Interface {
155
161
  /** Builds an array of signing permissions for event kinds */
156
162
  static buildSigningPermissions(kinds: number[]): string[];
157
163
  /** Create a {@link NostrConnectSigner} from a bunker:// URI */
158
- static fromBunkerURI(uri: string, options: NostrConnectConnectionMethods & {
164
+ static fromBunkerURI(uri: string, subscription: NostrSubscriptionMethod, publish: NostrPublishMethod, options: Omit<NostrConnectSignerOptions, "relays"> & {
159
165
  permissions?: string[];
160
166
  signer?: SimpleSigner;
161
167
  }): 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
+ publish;
38
+ /** The active nostr subscription */
39
+ subscription;
44
40
  log = logger.extend("NostrConnectSigner");
45
41
  /** The local client signer */
46
42
  signer;
@@ -57,19 +53,19 @@ 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;
66
- constructor(opts) {
63
+ constructor(subscription, publish, opts) {
67
64
  this.relays = opts.relays;
68
65
  this.pubkey = opts.pubkey;
69
66
  this.remote = opts.remote;
70
- this.onSubOpen = opts.onSubOpen;
71
- this.onSubClose = opts.onSubClose;
72
- this.onPublishEvent = opts.onPublishEvent;
67
+ this.subscription = subscription;
68
+ this.publish = publish;
73
69
  if (opts.onAuth)
74
70
  this.onAuth = opts.onAuth;
75
71
  this.signer = opts?.signer || new SimpleSigner();
@@ -82,6 +78,8 @@ export class NostrConnectSigner {
82
78
  decrypt: this.nip44Decrypt.bind(this),
83
79
  };
84
80
  }
81
+ /** The currently active REQ subscription */
82
+ req;
85
83
  /** Open the connection */
86
84
  async open() {
87
85
  if (this.subscriptionOpen)
@@ -89,19 +87,21 @@ export class NostrConnectSigner {
89
87
  this.subscriptionOpen = true;
90
88
  const pubkey = await this.signer.getPublicKey();
91
89
  // Setup subscription
92
- await this.onSubOpen?.([
90
+ this.req = this.subscription([
93
91
  {
94
92
  kinds: [kinds.NostrConnect],
95
93
  "#p": [pubkey],
96
94
  },
97
- ], this.relays, this.handleEvent.bind(this));
95
+ ], this.relays).subscribe({
96
+ next: (event) => this.handleEvent(event),
97
+ });
98
98
  this.log("Opened", this.relays);
99
99
  }
100
100
  /** Close the connection */
101
101
  async close() {
102
102
  this.subscriptionOpen = false;
103
103
  this.isConnected = false;
104
- await this.onSubClose?.();
104
+ this.req?.unsubscribe();
105
105
  this.log("Closed");
106
106
  }
107
107
  requests = new Map();
@@ -178,7 +178,7 @@ export class NostrConnectSigner {
178
178
  this.log(`Sending request ${id} (${method}) ${JSON.stringify(params)}`);
179
179
  const p = createDefer();
180
180
  this.requests.set(id, p);
181
- await this.onPublishEvent?.(event, this.relays);
181
+ await this.publish?.(event, this.relays);
182
182
  return p;
183
183
  }
184
184
  /** Connect to remote signer */
@@ -318,9 +318,9 @@ export class NostrConnectSigner {
318
318
  return [Permission.GetPublicKey, ...kinds.map((k) => `${Permission.SignEvent}:${k}`)];
319
319
  }
320
320
  /** Create a {@link NostrConnectSigner} from a bunker:// URI */
321
- static async fromBunkerURI(uri, options) {
321
+ static async fromBunkerURI(uri, subscription, publish, options) {
322
322
  const { remote, relays, secret } = NostrConnectSigner.parseBunkerURI(uri);
323
- const client = new NostrConnectSigner({ relays, remote, ...options });
323
+ const client = new NostrConnectSigner(subscription, publish, { relays, remote, ...options });
324
324
  await client.connect(secret, options.permissions);
325
325
  return client;
326
326
  }
@@ -3,23 +3,19 @@ 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
- const signer = new NostrConnectSigner({
14
- onSubOpen,
15
- onSubClose,
16
- onPublishEvent,
12
+ const signer = new NostrConnectSigner(subscription, publish, {
17
13
  relays,
18
14
  remote: await remote.getPublicKey(),
19
15
  signer: client,
20
16
  });
21
17
  signer.connect();
22
- expect(onSubOpen).toHaveBeenCalledWith([{ "#p": [await client.getPublicKey()], kinds: [24133] }], relays, expect.any(Function));
18
+ expect(subscription).toHaveBeenCalledWith([{ "#p": [await client.getPublicKey()], kinds: [24133] }], relays);
23
19
  });
24
20
  });
25
21
  });
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-20250323173930",
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"