applesauce-accounts 1.0.0 → 2.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.
- package/dist/account.d.ts +2 -0
- package/dist/account.js +3 -0
- package/dist/accounts/readonly-account.d.ts +1 -0
- package/dist/accounts/readonly-account.js +3 -1
- package/dist/accounts/simple-account.d.ts +3 -1
- package/dist/accounts/simple-account.js +6 -5
- package/dist/proxy-signer.d.ts +1 -0
- package/dist/proxy-signer.js +1 -0
- package/dist/types.d.ts +9 -0
- package/package.json +4 -4
- package/dist/__tests__/account.test.d.ts +0 -1
- package/dist/__tests__/account.test.js +0 -125
- package/dist/__tests__/manager.test.d.ts +0 -1
- package/dist/__tests__/manager.test.js +0 -65
package/dist/account.d.ts
CHANGED
|
@@ -2,8 +2,10 @@ import { Nip07Interface } from "applesauce-signers";
|
|
|
2
2
|
import { BehaviorSubject } from "rxjs";
|
|
3
3
|
import { NostrEvent } from "nostr-tools";
|
|
4
4
|
import { EventTemplate, IAccount, SerializedAccount } from "./types.js";
|
|
5
|
+
/** An error thrown when a signer is used with the wrong pubkey */
|
|
5
6
|
export declare class SignerMismatchError extends Error {
|
|
6
7
|
}
|
|
8
|
+
/** A base class for all accounts */
|
|
7
9
|
export declare class BaseAccount<Signer extends Nip07Interface, SignerData, Metadata extends unknown> implements IAccount<Signer, SignerData, Metadata> {
|
|
8
10
|
pubkey: string;
|
|
9
11
|
signer: Signer;
|
package/dist/account.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { nanoid } from "nanoid";
|
|
2
2
|
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
/** Wraps a promise in an abort signal */
|
|
3
4
|
function wrapInSignal(promise, signal) {
|
|
4
5
|
return new Promise((res, rej) => {
|
|
5
6
|
signal.throwIfAborted();
|
|
@@ -21,8 +22,10 @@ function wrapInSignal(promise, signal) {
|
|
|
21
22
|
});
|
|
22
23
|
});
|
|
23
24
|
}
|
|
25
|
+
/** An error thrown when a signer is used with the wrong pubkey */
|
|
24
26
|
export class SignerMismatchError extends Error {
|
|
25
27
|
}
|
|
28
|
+
/** A base class for all accounts */
|
|
26
29
|
export class BaseAccount {
|
|
27
30
|
pubkey;
|
|
28
31
|
signer;
|
|
@@ -6,5 +6,6 @@ export declare class ReadonlyAccount<Metadata extends unknown> extends BaseAccou
|
|
|
6
6
|
static readonly type = "readonly";
|
|
7
7
|
toJSON(): SerializedAccount<void, Metadata>;
|
|
8
8
|
static fromJSON<Metadata extends unknown>(json: SerializedAccount<void, Metadata>): ReadonlyAccount<Metadata>;
|
|
9
|
+
/** Creates a ReadonlyAccount from a hex public key or NIP-19 npub */
|
|
9
10
|
static fromPubkey(pubkey: string): ReadonlyAccount<unknown>;
|
|
10
11
|
}
|
|
@@ -12,7 +12,9 @@ export class ReadonlyAccount extends BaseAccount {
|
|
|
12
12
|
const account = new ReadonlyAccount(json.pubkey, new ReadonlySigner(json.pubkey));
|
|
13
13
|
return super.loadCommonFields(account, json);
|
|
14
14
|
}
|
|
15
|
+
/** Creates a ReadonlyAccount from a hex public key or NIP-19 npub */
|
|
15
16
|
static fromPubkey(pubkey) {
|
|
16
|
-
|
|
17
|
+
const signer = ReadonlySigner.fromPubkey(pubkey);
|
|
18
|
+
return new ReadonlyAccount(signer.getPublicKey(), signer);
|
|
17
19
|
}
|
|
18
20
|
}
|
|
@@ -8,6 +8,8 @@ export declare class SimpleAccount<Metadata extends unknown> extends BaseAccount
|
|
|
8
8
|
static readonly type = "nsec";
|
|
9
9
|
toJSON(): SerializedAccount<SimpleAccountSignerData, Metadata>;
|
|
10
10
|
static fromJSON<Metadata extends unknown>(json: SerializedAccount<SimpleAccountSignerData, Metadata>): SimpleAccount<Metadata>;
|
|
11
|
-
|
|
11
|
+
/** Creates a SimpleAccount from a hex private key or NIP-19 nsec */
|
|
12
|
+
static fromKey<Metadata extends unknown>(privateKey: Uint8Array | string): SimpleAccount<Metadata>;
|
|
13
|
+
/** Creates a new SimpleAccount with a random private key */
|
|
12
14
|
static generateNew<Metadata extends unknown>(): SimpleAccount<Metadata>;
|
|
13
15
|
}
|
|
@@ -14,12 +14,13 @@ export class SimpleAccount extends BaseAccount {
|
|
|
14
14
|
const account = new SimpleAccount(json.pubkey, new SimpleSigner(key));
|
|
15
15
|
return super.loadCommonFields(account, json);
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const pubkey = getPublicKey(key);
|
|
21
|
-
return new SimpleAccount(pubkey,
|
|
17
|
+
/** Creates a SimpleAccount from a hex private key or NIP-19 nsec */
|
|
18
|
+
static fromKey(privateKey) {
|
|
19
|
+
const signer = SimpleSigner.fromKey(privateKey);
|
|
20
|
+
const pubkey = getPublicKey(signer.key);
|
|
21
|
+
return new SimpleAccount(pubkey, signer);
|
|
22
22
|
}
|
|
23
|
+
/** Creates a new SimpleAccount with a random private key */
|
|
23
24
|
static generateNew() {
|
|
24
25
|
const key = generateSecretKey();
|
|
25
26
|
return SimpleAccount.fromKey(key);
|
package/dist/proxy-signer.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Nip07Interface } from "applesauce-signers";
|
|
2
2
|
import { EventTemplate, NostrEvent } from "nostr-tools";
|
|
3
3
|
import { Observable } from "rxjs";
|
|
4
|
+
/** A signer class that proxies requests to another signer that isn't created yet */
|
|
4
5
|
export declare class ProxySigner<T extends Nip07Interface> implements Nip07Interface {
|
|
5
6
|
protected upstream: Observable<T | undefined>;
|
|
6
7
|
protected error?: string | undefined;
|
package/dist/proxy-signer.js
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { Nip07Interface } from "applesauce-signers";
|
|
2
|
+
import { NostrEvent } from "nostr-tools";
|
|
2
3
|
export type EventTemplate = {
|
|
3
4
|
kind: number;
|
|
4
5
|
content: string;
|
|
5
6
|
tags: string[][];
|
|
6
7
|
created_at: number;
|
|
7
8
|
};
|
|
9
|
+
/** A type for serializing an account */
|
|
8
10
|
export type SerializedAccount<SignerData, Metadata extends unknown> = {
|
|
9
11
|
/** Internal account ID */
|
|
10
12
|
id: string;
|
|
@@ -17,6 +19,7 @@ export type SerializedAccount<SignerData, Metadata extends unknown> = {
|
|
|
17
19
|
/** Extra application specific account metadata */
|
|
18
20
|
metadata?: Metadata;
|
|
19
21
|
};
|
|
22
|
+
/** An interface for an account */
|
|
20
23
|
export interface IAccount<Signer extends Nip07Interface = Nip07Interface, SignerData = any, Metadata extends unknown = any> extends Nip07Interface {
|
|
21
24
|
id: string;
|
|
22
25
|
name?: string;
|
|
@@ -27,8 +30,14 @@ export interface IAccount<Signer extends Nip07Interface = Nip07Interface, Signer
|
|
|
27
30
|
disableQueue?: boolean;
|
|
28
31
|
toJSON(): SerializedAccount<SignerData, Metadata>;
|
|
29
32
|
}
|
|
33
|
+
/** A constructor for an account */
|
|
30
34
|
export interface IAccountConstructor<Signer extends Nip07Interface, SignerData, Metadata extends unknown> {
|
|
31
35
|
readonly type: string;
|
|
32
36
|
new (pubkey: string, signer: Signer): IAccount<Signer, SignerData, Metadata>;
|
|
33
37
|
fromJSON(json: SerializedAccount<SignerData, Metadata>): IAccount<Signer, SignerData, Metadata>;
|
|
34
38
|
}
|
|
39
|
+
/** An interface for caching decrypted content of events */
|
|
40
|
+
export interface DecryptionCache {
|
|
41
|
+
getContent(event: NostrEvent): Promise<string>;
|
|
42
|
+
setContent(event: NostrEvent, content: string): Promise<void>;
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-accounts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A simple nostr account management system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@noble/hashes": "^1.7.1",
|
|
36
|
-
"applesauce-signers": "^
|
|
36
|
+
"applesauce-signers": "^2.0.0",
|
|
37
37
|
"nanoid": "^5.1.5",
|
|
38
|
-
"nostr-tools": "^2.
|
|
38
|
+
"nostr-tools": "^2.13",
|
|
39
39
|
"rxjs": "^7.8.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"typescript": "^5.8.3",
|
|
43
|
-
"vitest": "^3.
|
|
43
|
+
"vitest": "^3.2.3"
|
|
44
44
|
},
|
|
45
45
|
"funding": {
|
|
46
46
|
"type": "lightning",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,125 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import { AccountManager } from "../manager.js";
|
|
3
|
-
import { SimpleAccount } from "../accounts/simple-account.js";
|
|
4
|
-
import { generateSecretKey, getPublicKey } from "nostr-tools";
|
|
5
|
-
import { bytesToHex } from "@noble/hashes/utils";
|
|
6
|
-
let manager;
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
manager = new AccountManager();
|
|
9
|
-
});
|
|
10
|
-
describe("toJSON", () => {
|
|
11
|
-
it("should return an array of serialized accounts", () => {
|
|
12
|
-
manager.addAccount(SimpleAccount.fromKey(generateSecretKey()));
|
|
13
|
-
manager.setAccountMetadata(manager.accounts[0], { name: "testing" });
|
|
14
|
-
expect(manager.toJSON()).toEqual([
|
|
15
|
-
{
|
|
16
|
-
id: expect.any(String),
|
|
17
|
-
type: "nsec",
|
|
18
|
-
pubkey: expect.any(String),
|
|
19
|
-
metadata: { name: "testing" },
|
|
20
|
-
signer: { key: expect.any(String) },
|
|
21
|
-
},
|
|
22
|
-
]);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
describe("fromJSON", () => {
|
|
26
|
-
it("should recreate accounts", () => {
|
|
27
|
-
const key = generateSecretKey();
|
|
28
|
-
const json = [
|
|
29
|
-
{
|
|
30
|
-
id: "custom-id",
|
|
31
|
-
type: "nsec",
|
|
32
|
-
pubkey: getPublicKey(key),
|
|
33
|
-
metadata: { name: "testing" },
|
|
34
|
-
signer: { key: bytesToHex(key) },
|
|
35
|
-
},
|
|
36
|
-
];
|
|
37
|
-
manager.registerType(SimpleAccount);
|
|
38
|
-
manager.fromJSON(json);
|
|
39
|
-
expect(manager.getAccount("custom-id")).toBeInstanceOf(SimpleAccount);
|
|
40
|
-
expect(manager.getAccountForPubkey(getPublicKey(key))).toBeInstanceOf(SimpleAccount);
|
|
41
|
-
expect(manager.getAccountMetadata("custom-id")).toEqual({ name: "testing" });
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
describe("signer", () => {
|
|
45
|
-
it("should proxy active account", async () => {
|
|
46
|
-
const account = SimpleAccount.generateNew();
|
|
47
|
-
manager.addAccount(account);
|
|
48
|
-
manager.setActive(account);
|
|
49
|
-
expect(await manager.signer.getPublicKey()).toBe(getPublicKey(account.signer.key));
|
|
50
|
-
});
|
|
51
|
-
it("should throw if there is no active account", () => {
|
|
52
|
-
expect(() => {
|
|
53
|
-
manager.signer.getPublicKey();
|
|
54
|
-
}).toThrow("No active account");
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
describe("removeAccount", () => {
|
|
58
|
-
it("should clear active account if removed account was active", () => {
|
|
59
|
-
const account = SimpleAccount.generateNew();
|
|
60
|
-
manager.addAccount(account);
|
|
61
|
-
manager.setActive(account);
|
|
62
|
-
manager.removeAccount(account);
|
|
63
|
-
expect(manager.active).toBeUndefined();
|
|
64
|
-
});
|
|
65
|
-
});
|