applesauce-accounts 0.10.0 → 0.12.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.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
1
  # applesauce-accounts
2
2
 
3
- A simple nostr account management system
3
+ A simple nostr account management system built on top of [applesauce-signers](https://hzrd149.github.io/applesauce/signers/installation.html)
4
+
5
+ See [documentation](https://hzrd149.github.io/applesauce/signers/signers.html)
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,125 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ import { SimpleSigner } from "applesauce-signers";
3
+ import { finalizeEvent, generateSecretKey } from "nostr-tools";
4
+ import { BaseAccount } from "../account.js";
5
+ import { SimpleAccount } from "../accounts/simple-account.js";
6
+ describe("BaseAccount", () => {
7
+ describe("request queue", () => {
8
+ let signer;
9
+ beforeEach(() => {
10
+ signer = new SimpleSigner();
11
+ });
12
+ it("should queue signing requests by default", async () => {
13
+ const account = new BaseAccount(await signer.getPublicKey(), signer);
14
+ let resolve = [];
15
+ vi.spyOn(signer, "signEvent").mockImplementation(() => {
16
+ return new Promise((res) => {
17
+ resolve.push(() => res(finalizeEvent({ kind: 1, content: "mock", created_at: 0, tags: [] }, signer.key)));
18
+ });
19
+ });
20
+ // make two signing requests
21
+ expect(account.signEvent({ kind: 1, content: "first", created_at: 0, tags: [] })).toEqual(expect.any(Promise));
22
+ expect(account.signEvent({ kind: 1, content: "second", created_at: 0, tags: [] })).toEqual(expect.any(Promise));
23
+ expect(signer.signEvent).toHaveBeenCalledOnce();
24
+ expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "first" }));
25
+ // resolve first
26
+ resolve.shift()?.();
27
+ // wait next tick
28
+ await new Promise((res) => setTimeout(res, 0));
29
+ expect(signer.signEvent).toHaveBeenCalledTimes(2);
30
+ expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "second" }));
31
+ // resolve second
32
+ resolve.shift()?.();
33
+ // wait next tick
34
+ await new Promise((res) => setTimeout(res, 0));
35
+ expect(Reflect.get(account, "queueLength")).toBe(0);
36
+ expect(Reflect.get(account, "lock")).toBeNull();
37
+ });
38
+ it("should cancel queue if request throws", () => { });
39
+ it("should not use queueing if its disabled", async () => {
40
+ const account = new BaseAccount(await signer.getPublicKey(), signer);
41
+ account.disableQueue = true;
42
+ let resolve = [];
43
+ vi.spyOn(signer, "signEvent").mockImplementation(() => {
44
+ return new Promise((res) => {
45
+ resolve.push(() => res(finalizeEvent({ kind: 1, content: "mock", created_at: 0, tags: [] }, signer.key)));
46
+ });
47
+ });
48
+ // make two signing requests
49
+ account.signEvent({ kind: 1, content: "first", created_at: 0, tags: [] });
50
+ account.signEvent({ kind: 1, content: "second", created_at: 0, tags: [] });
51
+ expect(Reflect.get(account, "lock")).toBeNull();
52
+ expect(signer.signEvent).toHaveBeenCalledTimes(2);
53
+ expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "first" }));
54
+ expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "second" }));
55
+ // resolve both
56
+ resolve.shift()?.();
57
+ resolve.shift()?.();
58
+ });
59
+ });
60
+ describe("type", () => {
61
+ it("should return static account type", () => {
62
+ const account = SimpleAccount.fromKey(generateSecretKey());
63
+ expect(account.type).toBe("nsec");
64
+ });
65
+ });
66
+ describe("nip04 and nip44", () => {
67
+ it("should return undefined when signer does not support nip04/nip44", () => {
68
+ const signer = {
69
+ getPublicKey: () => "test-pubkey",
70
+ signEvent: () => ({ id: "", pubkey: "test-pubkey", created_at: 0, kind: 1, tags: [], content: "", sig: "" }),
71
+ };
72
+ const account = new BaseAccount("test-pubkey", signer);
73
+ expect(account.nip04).toBeUndefined();
74
+ expect(account.nip44).toBeUndefined();
75
+ });
76
+ it("should return nip04/nip44 interface when signer supports them", async () => {
77
+ const signer = {
78
+ getPublicKey: () => "test-pubkey",
79
+ signEvent: () => ({ id: "", pubkey: "test-pubkey", created_at: 0, kind: 1, tags: [], content: "", sig: "" }),
80
+ nip04: {
81
+ encrypt: async () => "encrypted",
82
+ decrypt: async () => "decrypted",
83
+ },
84
+ nip44: {
85
+ encrypt: async () => "encrypted",
86
+ decrypt: async () => "decrypted",
87
+ },
88
+ };
89
+ const account = new BaseAccount("test-pubkey", signer);
90
+ expect(account.nip04).toBeDefined();
91
+ expect(account.nip44).toBeDefined();
92
+ const nip04Result = await account.nip04.encrypt("pubkey", "test");
93
+ expect(nip04Result).toBe("encrypted");
94
+ const nip44Result = await account.nip44.encrypt("pubkey", "test");
95
+ expect(nip44Result).toBe("encrypted");
96
+ });
97
+ it("should reflect changes in signer nip04/nip44 support", () => {
98
+ const signer = {
99
+ getPublicKey: () => "test-pubkey",
100
+ signEvent: () => ({ id: "", pubkey: "test-pubkey", created_at: 0, kind: 1, tags: [], content: "", sig: "" }),
101
+ };
102
+ const account = new BaseAccount("test-pubkey", signer);
103
+ expect(account.nip04).toBeUndefined();
104
+ expect(account.nip44).toBeUndefined();
105
+ // Add nip04 support
106
+ signer.nip04 = {
107
+ encrypt: async () => "encrypted",
108
+ decrypt: async () => "decrypted",
109
+ };
110
+ expect(account.nip04).toBeDefined();
111
+ expect(account.nip44).toBeUndefined();
112
+ // Add nip44 support
113
+ signer.nip44 = {
114
+ encrypt: async () => "encrypted",
115
+ decrypt: async () => "decrypted",
116
+ };
117
+ expect(account.nip04).toBeDefined();
118
+ expect(account.nip44).toBeDefined();
119
+ // Remove nip04 support
120
+ signer.nip04 = undefined;
121
+ expect(account.nip04).toBeUndefined();
122
+ expect(account.nip44).toBeDefined();
123
+ });
124
+ });
125
+ });
package/dist/account.d.ts CHANGED
@@ -1,31 +1,37 @@
1
- import { Nip07Interface } from "applesauce-signer";
2
- import { EventTemplate } from "nostr-tools";
3
- import { IAccount, SerializedAccount } from "./types.js";
1
+ import { Nip07Interface } from "applesauce-signers";
2
+ import { BehaviorSubject } from "rxjs";
3
+ import { NostrEvent } from "nostr-tools";
4
+ import { EventTemplate, IAccount, SerializedAccount } from "./types.js";
4
5
  export declare class SignerMismatchError extends Error {
5
6
  }
6
- export declare class AccountLockedError extends Error {
7
- }
8
- export declare class BaseAccount<T extends string, S> implements IAccount<T, S> {
7
+ export declare class BaseAccount<Signer extends Nip07Interface, SignerData, Metadata extends unknown> implements IAccount<Signer, SignerData, Metadata> {
9
8
  pubkey: string;
10
- signer: Nip07Interface;
11
- name?: string;
12
- locked: boolean;
13
- nip04?: {
14
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
15
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
16
- } | undefined;
17
- nip44?: {
18
- encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
19
- decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
20
- } | undefined;
21
- constructor(pubkey: string, signer: Nip07Interface);
22
- unlock(): Promise<boolean>;
23
- lock(): void;
24
- toJSON(): SerializedAccount<T, S>;
25
- /** throws if account is locked */
26
- protected checkLocked(): void;
9
+ signer: Signer;
10
+ id: string;
11
+ get type(): string;
12
+ /** Disable request queueing */
13
+ disableQueue?: boolean;
14
+ metadata$: BehaviorSubject<Metadata | undefined>;
15
+ get metadata(): Metadata | undefined;
16
+ set metadata(metadata: Metadata);
17
+ get nip04(): Nip07Interface["nip04"] | undefined;
18
+ get nip44(): Nip07Interface["nip44"] | undefined;
19
+ constructor(pubkey: string, signer: Signer);
20
+ toJSON(): SerializedAccount<SignerData, Metadata>;
21
+ /** Adds the common fields to the serialized output of a toJSON method */
22
+ protected saveCommonFields(json: Omit<SerializedAccount<SignerData, Metadata>, "id" | "type" | "metadata" | "pubkey">): SerializedAccount<SignerData, Metadata>;
23
+ /** Sets an accounts id and metadata. NOTE: This should only be used in fromJSON methods */
24
+ static loadCommonFields<T extends IAccount>(account: T, json: SerializedAccount<any, any>): T;
27
25
  /** Gets the pubkey from the signer */
28
- getPublicKey(): Promise<string>;
26
+ getPublicKey(): string | Promise<string>;
29
27
  /** sign the event and make sure its signed with the correct pubkey */
30
- signEvent(template: EventTemplate): Promise<import("nostr-tools").Event>;
28
+ signEvent(template: EventTemplate): Promise<NostrEvent> | NostrEvent;
29
+ /** Aborts all pending requests in the queue */
30
+ abortQueue(reason: Error): void;
31
+ /** internal queue */
32
+ protected queueLength: number;
33
+ protected lock: Promise<any> | null;
34
+ protected abort: AbortController | null;
35
+ protected reduceQueue(): void;
36
+ protected waitForLock<T>(fn: () => Promise<T> | T): Promise<T> | T;
31
37
  }
package/dist/account.js CHANGED
@@ -1,77 +1,168 @@
1
- // errors
2
- export class SignerMismatchError extends Error {
1
+ import { nanoid } from "nanoid";
2
+ import { BehaviorSubject } from "rxjs";
3
+ function wrapInSignal(promise, signal) {
4
+ return new Promise((res, rej) => {
5
+ signal.throwIfAborted();
6
+ let done = false;
7
+ // reject promise if abort signal is triggered
8
+ signal.addEventListener("abort", () => {
9
+ if (!done)
10
+ rej(signal.reason || undefined);
11
+ done = true;
12
+ });
13
+ return promise.then((v) => {
14
+ if (!done)
15
+ res(v);
16
+ done = true;
17
+ }, (err) => {
18
+ if (!done)
19
+ rej(err);
20
+ done = true;
21
+ });
22
+ });
3
23
  }
4
- export class AccountLockedError extends Error {
24
+ export class SignerMismatchError extends Error {
5
25
  }
6
26
  export class BaseAccount {
7
27
  pubkey;
8
28
  signer;
9
- name;
10
- locked = true;
11
- // encryption interfaces
12
- nip04;
13
- nip44;
29
+ id = nanoid(8);
30
+ get type() {
31
+ const cls = Reflect.getPrototypeOf(this).constructor;
32
+ return cls.type;
33
+ }
34
+ /** Disable request queueing */
35
+ disableQueue;
36
+ metadata$ = new BehaviorSubject(undefined);
37
+ get metadata() {
38
+ return this.metadata$.value;
39
+ }
40
+ set metadata(metadata) {
41
+ this.metadata$.next(metadata);
42
+ }
43
+ get nip04() {
44
+ if (!this.signer.nip04)
45
+ return undefined;
46
+ return {
47
+ encrypt: (pubkey, plaintext) => {
48
+ return this.waitForLock(() => this.signer.nip04.encrypt(pubkey, plaintext));
49
+ },
50
+ decrypt: (pubkey, plaintext) => {
51
+ return this.waitForLock(() => this.signer.nip04.decrypt(pubkey, plaintext));
52
+ },
53
+ };
54
+ }
55
+ get nip44() {
56
+ if (!this.signer.nip44)
57
+ return undefined;
58
+ return {
59
+ encrypt: (pubkey, plaintext) => {
60
+ return this.waitForLock(() => this.signer.nip44.encrypt(pubkey, plaintext));
61
+ },
62
+ decrypt: (pubkey, plaintext) => {
63
+ return this.waitForLock(() => this.signer.nip44.decrypt(pubkey, plaintext));
64
+ },
65
+ };
66
+ }
14
67
  constructor(pubkey, signer) {
15
68
  this.pubkey = pubkey;
16
69
  this.signer = signer;
17
- // setup encryption interfaces to check if account is locked
18
- if (this.signer.nip04) {
19
- this.nip04 = {
20
- encrypt: (pubkey, plaintext) => {
21
- this.checkLocked();
22
- return this.signer.nip04.encrypt(pubkey, plaintext);
23
- },
24
- decrypt: (pubkey, plaintext) => {
25
- this.checkLocked();
26
- return this.signer.nip04.decrypt(pubkey, plaintext);
27
- },
28
- };
29
- }
30
- if (this.signer.nip44) {
31
- this.nip44 = {
32
- encrypt: (pubkey, plaintext) => {
33
- this.checkLocked();
34
- return this.signer.nip44.encrypt(pubkey, plaintext);
35
- },
36
- decrypt: (pubkey, plaintext) => {
37
- this.checkLocked();
38
- return this.signer.nip44.decrypt(pubkey, plaintext);
39
- },
40
- };
41
- }
42
- }
43
- async unlock() {
44
- this.locked = false;
45
- return true;
46
- }
47
- lock() {
48
- this.locked = true;
49
70
  }
50
71
  // This should be overwritten by a sub class
51
72
  toJSON() {
52
73
  throw new Error("Not implemented");
53
74
  }
54
- /** throws if account is locked */
55
- checkLocked() {
56
- if (this.locked)
57
- throw new AccountLockedError("Account is locked");
75
+ /** Adds the common fields to the serialized output of a toJSON method */
76
+ saveCommonFields(json) {
77
+ return { ...json, id: this.id, pubkey: this.pubkey, metadata: this.metadata, type: this.type };
78
+ }
79
+ /** Sets an accounts id and metadata. NOTE: This should only be used in fromJSON methods */
80
+ static loadCommonFields(account, json) {
81
+ if (json.id)
82
+ account.id = json.id;
83
+ if (json.metadata)
84
+ account.metadata = json.metadata;
85
+ return account;
58
86
  }
59
87
  /** Gets the pubkey from the signer */
60
- async getPublicKey() {
61
- this.checkLocked();
62
- const signerKey = await this.signer.getPublicKey();
63
- if (this.pubkey !== signerKey)
64
- throw new Error("Account signer mismatch");
65
- return this.pubkey;
88
+ getPublicKey() {
89
+ const result = this.signer.getPublicKey();
90
+ if (result instanceof Promise)
91
+ return result.then((pubkey) => {
92
+ if (this.pubkey !== pubkey)
93
+ throw new SignerMismatchError("Account signer mismatch");
94
+ return pubkey;
95
+ });
96
+ else {
97
+ if (this.pubkey !== result)
98
+ throw new SignerMismatchError("Account signer mismatch");
99
+ return result;
100
+ }
66
101
  }
67
102
  /** sign the event and make sure its signed with the correct pubkey */
68
- async signEvent(template) {
69
- this.checkLocked();
103
+ signEvent(template) {
70
104
  if (!Reflect.has(template, "pubkey"))
71
105
  Reflect.set(template, "pubkey", this.pubkey);
72
- const signed = await this.signer.signEvent(template);
73
- if (signed.pubkey !== this.pubkey)
74
- throw new SignerMismatchError("Signer signed with wrong pubkey");
75
- return signed;
106
+ return this.waitForLock(() => {
107
+ const result = this.signer.signEvent(template);
108
+ if (result instanceof Promise)
109
+ return result.then((signed) => {
110
+ if (signed.pubkey !== this.pubkey)
111
+ throw new SignerMismatchError("Signer signed with wrong pubkey");
112
+ return signed;
113
+ });
114
+ else {
115
+ if (result.pubkey !== this.pubkey)
116
+ throw new SignerMismatchError("Signer signed with wrong pubkey");
117
+ return result;
118
+ }
119
+ });
120
+ }
121
+ /** Aborts all pending requests in the queue */
122
+ abortQueue(reason) {
123
+ if (this.abort)
124
+ this.abort.abort(reason);
125
+ }
126
+ /** internal queue */
127
+ queueLength = 0;
128
+ lock = null;
129
+ abort = null;
130
+ reduceQueue() {
131
+ // shorten the queue
132
+ this.queueLength--;
133
+ // if this was the last request, remove the lock
134
+ if (this.queueLength === 0) {
135
+ this.lock = null;
136
+ this.abort = null;
137
+ }
138
+ }
139
+ waitForLock(fn) {
140
+ if (this.disableQueue)
141
+ return fn();
142
+ // if there is already a pending request, wait for it
143
+ if (this.lock && this.abort) {
144
+ // create a new promise that runs after the lock
145
+ const p = wrapInSignal(this.lock.then(() => {
146
+ // if the abort signal is triggered, don't call the signer
147
+ this.abort?.signal.throwIfAborted();
148
+ return fn();
149
+ }), this.abort.signal);
150
+ // set the lock the new promise that ignores errors
151
+ this.lock = p.catch(() => { }).finally(this.reduceQueue.bind(this));
152
+ this.queueLength++;
153
+ return p;
154
+ }
155
+ else {
156
+ const result = fn();
157
+ // if the result is async, set the new lock
158
+ if (result instanceof Promise) {
159
+ this.abort = new AbortController();
160
+ const p = wrapInSignal(result, this.abort.signal);
161
+ // set the lock the new promise that ignores errors
162
+ this.lock = p.catch(() => { }).finally(this.reduceQueue.bind(this));
163
+ this.queueLength = 1;
164
+ }
165
+ return result;
166
+ }
76
167
  }
77
168
  }
@@ -1,10 +1,9 @@
1
- import { AmberClipboardSigner } from "applesauce-signer/signers/amber-clipboard-signer";
1
+ import { AmberClipboardSigner } from "applesauce-signers/signers/amber-clipboard-signer";
2
2
  import { BaseAccount } from "../account.js";
3
- import { IAccount, SerializedAccount } from "../types.js";
3
+ import { SerializedAccount } from "../types.js";
4
4
  /** An account for the amber clipboard api */
5
- export declare class AmberClipboardAccount extends BaseAccount<"amber-clipboard", void> {
6
- signer: AmberClipboardSigner;
7
- constructor(pubkey: string, signer: AmberClipboardSigner);
8
- toJSON(): SerializedAccount<"amber-clipboard", void>;
9
- static fromJSON(json: SerializedAccount<"amber-clipboard", void>): IAccount<"amber-clipboard", void>;
5
+ export declare class AmberClipboardAccount<Metadata extends unknown> extends BaseAccount<AmberClipboardSigner, void, Metadata> {
6
+ static readonly type = "amber-clipboard";
7
+ toJSON(): SerializedAccount<void, Metadata>;
8
+ static fromJSON<Metadata extends unknown>(json: SerializedAccount<void, Metadata>): AmberClipboardAccount<Metadata>;
10
9
  }
@@ -1,16 +1,15 @@
1
- import { AmberClipboardSigner } from "applesauce-signer/signers/amber-clipboard-signer";
1
+ import { AmberClipboardSigner } from "applesauce-signers/signers/amber-clipboard-signer";
2
2
  import { BaseAccount } from "../account.js";
3
3
  /** An account for the amber clipboard api */
4
4
  export class AmberClipboardAccount extends BaseAccount {
5
- signer;
6
- constructor(pubkey, signer) {
7
- super(pubkey, signer);
8
- this.signer = signer;
9
- }
5
+ static type = "amber-clipboard";
10
6
  toJSON() {
11
- return { type: "amber-clipboard", pubkey: this.pubkey, name: this.name, signer: void 0 };
7
+ return super.saveCommonFields({
8
+ signer: undefined,
9
+ });
12
10
  }
13
11
  static fromJSON(json) {
14
- return new AmberClipboardAccount(json.pubkey, new AmberClipboardSigner());
12
+ const account = new AmberClipboardAccount(json.pubkey, new AmberClipboardSigner());
13
+ return super.loadCommonFields(account, json);
15
14
  }
16
15
  }
@@ -0,0 +1,3 @@
1
+ import { AccountManager } from "../manager.js";
2
+ /** Registers the most common account types to a account manager */
3
+ export declare function registerCommonAccountTypes(manager: AccountManager): void;
@@ -0,0 +1,13 @@
1
+ import { ExtensionAccount } from "./extension-account.js";
2
+ import { NostrConnectAccount } from "./nostr-connect-account.js";
3
+ import { PasswordAccount } from "./password-account.js";
4
+ import { ReadonlyAccount } from "./readonly-account.js";
5
+ import { SimpleAccount } from "./simple-account.js";
6
+ /** Registers the most common account types to a account manager */
7
+ export function registerCommonAccountTypes(manager) {
8
+ manager.registerType(ExtensionAccount);
9
+ manager.registerType(PasswordAccount);
10
+ manager.registerType(ReadonlyAccount);
11
+ manager.registerType(SimpleAccount);
12
+ manager.registerType(NostrConnectAccount);
13
+ }
@@ -1,7 +1,10 @@
1
+ import { ExtensionSigner } from "applesauce-signers/signers/extension-signer";
1
2
  import { BaseAccount } from "../account.js";
2
3
  import { SerializedAccount } from "../types.js";
3
- export default class ExtensionAccount extends BaseAccount<"extension", void> {
4
- constructor(pubkey: string);
5
- toJSON(): SerializedAccount<"extension", void>;
6
- static fromJSON(json: SerializedAccount<"extension", void>): ExtensionAccount;
4
+ export declare class ExtensionAccount<Metadata extends unknown> extends BaseAccount<ExtensionSigner, void, Metadata> {
5
+ signer: ExtensionSigner;
6
+ static readonly type = "extension";
7
+ constructor(pubkey: string, signer: ExtensionSigner);
8
+ toJSON(): SerializedAccount<void, Metadata>;
9
+ static fromJSON<Metadata extends unknown>(json: SerializedAccount<void, Metadata>): ExtensionAccount<Metadata>;
7
10
  }
@@ -1,13 +1,19 @@
1
- import { ExtensionSigner } from "applesauce-signer/signers/extension-signer";
1
+ import { ExtensionSigner } from "applesauce-signers/signers/extension-signer";
2
2
  import { BaseAccount } from "../account.js";
3
- export default class ExtensionAccount extends BaseAccount {
4
- constructor(pubkey) {
5
- super(pubkey, new ExtensionSigner());
3
+ export class ExtensionAccount extends BaseAccount {
4
+ signer;
5
+ static type = "extension";
6
+ constructor(pubkey, signer) {
7
+ super(pubkey, signer || new ExtensionSigner());
8
+ this.signer = signer;
6
9
  }
7
10
  toJSON() {
8
- return { type: "extension", pubkey: this.pubkey, signer: undefined };
11
+ return super.saveCommonFields({
12
+ signer: undefined,
13
+ });
9
14
  }
10
15
  static fromJSON(json) {
11
- return new ExtensionAccount(json.pubkey);
16
+ const account = new ExtensionAccount(json.pubkey, new ExtensionSigner());
17
+ return super.loadCommonFields(account, json);
12
18
  }
13
19
  }
@@ -4,3 +4,5 @@ export * from "./password-account.js";
4
4
  export * from "./readonly-account.js";
5
5
  export * from "./serial-port-account.js";
6
6
  export * from "./simple-account.js";
7
+ export * from "./nostr-connect-account.js";
8
+ export * from "./common.js";
@@ -4,3 +4,5 @@ export * from "./password-account.js";
4
4
  export * from "./readonly-account.js";
5
5
  export * from "./serial-port-account.js";
6
6
  export * from "./simple-account.js";
7
+ export * from "./nostr-connect-account.js";
8
+ export * from "./common.js";
@@ -0,0 +1,16 @@
1
+ import { NostrConnectConnectionMethods, NostrConnectSigner } from "applesauce-signers";
2
+ import { BaseAccount } from "../account.js";
3
+ import { SerializedAccount } from "../types.js";
4
+ export type NostrConnectAccountSignerData = {
5
+ clientKey: string;
6
+ remote: string;
7
+ relays: string[];
8
+ };
9
+ /** An account type for NIP-46 signers */
10
+ export declare class NostrConnectAccount<Metadata extends unknown> extends BaseAccount<NostrConnectSigner, NostrConnectAccountSignerData, Metadata> {
11
+ static readonly type = "nostr-connect";
12
+ toJSON(): SerializedAccount<NostrConnectAccountSignerData, Metadata>;
13
+ /** This is called when NostrConnectAccount.fromJSON needs new connection methods for NostrConnectSigner */
14
+ static createConnectionMethods(): NostrConnectConnectionMethods;
15
+ static fromJSON<Metadata extends unknown>(json: SerializedAccount<NostrConnectAccountSignerData, Metadata>, connection?: NostrConnectConnectionMethods): NostrConnectAccount<Metadata>;
16
+ }
@@ -0,0 +1,34 @@
1
+ import { NostrConnectSigner, SimpleSigner } from "applesauce-signers";
2
+ import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
3
+ import { BaseAccount } from "../account.js";
4
+ /** An account type for NIP-46 signers */
5
+ export class NostrConnectAccount extends BaseAccount {
6
+ static type = "nostr-connect";
7
+ toJSON() {
8
+ if (!this.signer.remote)
9
+ throw new Error("Cant save NostrConnectAccount when not initialized");
10
+ return super.saveCommonFields({
11
+ signer: {
12
+ clientKey: bytesToHex(this.signer.signer.key),
13
+ remote: this.signer.remote,
14
+ relays: this.signer.relays,
15
+ },
16
+ });
17
+ }
18
+ /** This is called when NostrConnectAccount.fromJSON needs new connection methods for NostrConnectSigner */
19
+ static createConnectionMethods() {
20
+ throw new Error("Cant create NostrConnectAccount without either passing in connection methods or setting NostrConnectAccount.createConnectionMethods");
21
+ }
22
+ static fromJSON(json, connection) {
23
+ connection = connection || NostrConnectAccount.createConnectionMethods();
24
+ const signer = new NostrConnectSigner({
25
+ ...connection,
26
+ relays: json.signer.relays,
27
+ pubkey: json.pubkey,
28
+ remote: json.signer.remote,
29
+ signer: new SimpleSigner(hexToBytes(json.signer.clientKey)),
30
+ });
31
+ const account = new NostrConnectAccount(json.pubkey, signer);
32
+ return super.loadCommonFields(account, json);
33
+ }
34
+ }
@@ -1,15 +1,21 @@
1
- import { PasswordSigner } from "applesauce-signer/signers/password-signer";
1
+ import { PasswordSigner } from "applesauce-signers/signers/password-signer";
2
2
  import { BaseAccount } from "../account.js";
3
3
  import { SerializedAccount } from "../types.js";
4
- type SignerData = {
4
+ export type PasswordAccountSignerData = {
5
5
  ncryptsec: string;
6
6
  };
7
- export default class PasswordAccount extends BaseAccount<"ncryptsec", SignerData> {
8
- signer: PasswordSigner;
9
- constructor(pubkey: string, signer: PasswordSigner);
10
- unlock(): Promise<boolean>;
11
- toJSON(): SerializedAccount<"ncryptsec", SignerData>;
12
- static fromJSON(json: SerializedAccount<"ncryptsec", SignerData>): PasswordAccount;
13
- static fromNcryptsec(pubkey: string, ncryptsec: string): PasswordAccount;
7
+ export declare class PasswordAccount<Metadata extends unknown> extends BaseAccount<PasswordSigner, PasswordAccountSignerData, Metadata> {
8
+ static readonly type = "ncryptsec";
9
+ get unlocked(): boolean;
10
+ /** called when PasswordAccount.unlock is called without a password */
11
+ static requestUnlockPassword(_account: PasswordAccount<any>): Promise<string>;
12
+ /**
13
+ * Attempt to unlock the signer with a password
14
+ * @throws
15
+ */
16
+ unlock(password?: string): Promise<void>;
17
+ toJSON(): SerializedAccount<PasswordAccountSignerData, Metadata>;
18
+ static fromJSON<Metadata extends unknown>(json: SerializedAccount<PasswordAccountSignerData, Metadata>): PasswordAccount<Metadata>;
19
+ /** Creates a new PasswordAccount from a ncryptsec string */
20
+ static fromNcryptsec<Metadata extends unknown>(pubkey: string, ncryptsec: string): PasswordAccount<Metadata>;
14
21
  }
15
- export {};