applesauce-accounts 0.0.0-next-20250125174815 → 0.0.0-next-20250125183855
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 +7 -5
- package/dist/account.js +50 -20
- package/dist/account.test.js +10 -7
- package/dist/manager.d.ts +2 -0
- package/dist/manager.js +6 -0
- package/dist/types.d.ts +1 -0
- package/package.json +2 -2
package/dist/account.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Nip07Interface } from "applesauce-signer";
|
|
2
|
-
import { EventTemplate, IAccount, SerializedAccount } from "./types.js";
|
|
3
2
|
import { BehaviorSubject } from "rxjs";
|
|
4
3
|
import { NostrEvent } from "nostr-tools";
|
|
4
|
+
import { EventTemplate, IAccount, SerializedAccount } from "./types.js";
|
|
5
5
|
export declare class SignerMismatchError extends Error {
|
|
6
6
|
}
|
|
7
7
|
export declare class BaseAccount<Signer extends Nip07Interface, SignerData, Metadata extends unknown> implements IAccount<Signer, SignerData, Metadata> {
|
|
8
8
|
pubkey: string;
|
|
9
9
|
signer: Signer;
|
|
10
10
|
id: string;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/** Disable request queueing */
|
|
12
|
+
disableQueue?: boolean;
|
|
13
13
|
metadata$: BehaviorSubject<Metadata | undefined>;
|
|
14
14
|
get metadata(): Metadata | undefined;
|
|
15
15
|
set metadata(metadata: Metadata);
|
|
@@ -27,10 +27,12 @@ export declare class BaseAccount<Signer extends Nip07Interface, SignerData, Meta
|
|
|
27
27
|
getPublicKey(): string | Promise<string>;
|
|
28
28
|
/** sign the event and make sure its signed with the correct pubkey */
|
|
29
29
|
signEvent(template: EventTemplate): Promise<NostrEvent> | NostrEvent;
|
|
30
|
-
/**
|
|
31
|
-
|
|
30
|
+
/** Aborts all pending requests in the queue */
|
|
31
|
+
abortQueue(reason: Error): void;
|
|
32
32
|
/** internal queue */
|
|
33
33
|
protected queueLength: number;
|
|
34
34
|
protected lock: Promise<any> | null;
|
|
35
|
+
protected abort: AbortController | null;
|
|
36
|
+
protected reduceQueue(): void;
|
|
35
37
|
protected waitForLock<T>(fn: () => Promise<T> | T): Promise<T> | T;
|
|
36
38
|
}
|
package/dist/account.js
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import { nanoid } from "nanoid";
|
|
2
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
|
+
});
|
|
23
|
+
}
|
|
3
24
|
export class SignerMismatchError extends Error {
|
|
4
25
|
}
|
|
5
26
|
export class BaseAccount {
|
|
6
27
|
pubkey;
|
|
7
28
|
signer;
|
|
8
29
|
id = nanoid(8);
|
|
9
|
-
/**
|
|
10
|
-
|
|
30
|
+
/** Disable request queueing */
|
|
31
|
+
disableQueue;
|
|
11
32
|
metadata$ = new BehaviorSubject(undefined);
|
|
12
33
|
get metadata() {
|
|
13
34
|
return this.metadata$.value;
|
|
@@ -81,31 +102,37 @@ export class BaseAccount {
|
|
|
81
102
|
}
|
|
82
103
|
});
|
|
83
104
|
}
|
|
84
|
-
/**
|
|
85
|
-
|
|
86
|
-
this.
|
|
87
|
-
|
|
105
|
+
/** Aborts all pending requests in the queue */
|
|
106
|
+
abortQueue(reason) {
|
|
107
|
+
if (this.abort)
|
|
108
|
+
this.abort.abort(reason);
|
|
88
109
|
}
|
|
89
110
|
/** internal queue */
|
|
90
111
|
queueLength = 0;
|
|
91
112
|
lock = null;
|
|
113
|
+
abort = null;
|
|
114
|
+
reduceQueue() {
|
|
115
|
+
// shorten the queue
|
|
116
|
+
this.queueLength--;
|
|
117
|
+
// if this was the last request, remove the lock
|
|
118
|
+
if (this.queueLength === 0) {
|
|
119
|
+
this.lock = null;
|
|
120
|
+
this.abort = null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
92
123
|
waitForLock(fn) {
|
|
93
|
-
if (
|
|
124
|
+
if (this.disableQueue)
|
|
94
125
|
return fn();
|
|
95
126
|
// if there is already a pending request, wait for it
|
|
96
|
-
if (this.lock) {
|
|
127
|
+
if (this.lock && this.abort) {
|
|
97
128
|
// create a new promise that runs after the lock
|
|
98
|
-
const p = this.lock
|
|
99
|
-
|
|
100
|
-
.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
this.lock = null;
|
|
106
|
-
});
|
|
107
|
-
// set the lock the new promise
|
|
108
|
-
this.lock = p;
|
|
129
|
+
const p = wrapInSignal(this.lock.then(() => {
|
|
130
|
+
// if the abort signal is triggered, don't call the signer
|
|
131
|
+
this.abort?.signal.throwIfAborted();
|
|
132
|
+
return fn();
|
|
133
|
+
}), this.abort.signal);
|
|
134
|
+
// set the lock the new promise that ignores errors
|
|
135
|
+
this.lock = p.catch(() => { }).finally(this.reduceQueue.bind(this));
|
|
109
136
|
this.queueLength++;
|
|
110
137
|
return p;
|
|
111
138
|
}
|
|
@@ -113,7 +140,10 @@ export class BaseAccount {
|
|
|
113
140
|
const result = fn();
|
|
114
141
|
// if the result is async, set the new lock
|
|
115
142
|
if (result instanceof Promise) {
|
|
116
|
-
this.
|
|
143
|
+
this.abort = new AbortController();
|
|
144
|
+
const p = wrapInSignal(result, this.abort.signal);
|
|
145
|
+
// set the lock the new promise that ignores errors
|
|
146
|
+
this.lock = p.catch(() => { }).finally(this.reduceQueue.bind(this));
|
|
117
147
|
this.queueLength = 1;
|
|
118
148
|
}
|
|
119
149
|
return result;
|
package/dist/account.test.js
CHANGED
|
@@ -17,8 +17,8 @@ describe("BaseAccount", () => {
|
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
19
|
// make two signing requests
|
|
20
|
-
account.signEvent({ kind: 1, content: "first", created_at: 0, tags: [] });
|
|
21
|
-
account.signEvent({ kind: 1, content: "second", created_at: 0, tags: [] });
|
|
20
|
+
expect(account.signEvent({ kind: 1, content: "first", created_at: 0, tags: [] })).toEqual(expect.any(Promise));
|
|
21
|
+
expect(account.signEvent({ kind: 1, content: "second", created_at: 0, tags: [] })).toEqual(expect.any(Promise));
|
|
22
22
|
expect(signer.signEvent).toHaveBeenCalledOnce();
|
|
23
23
|
expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "first" }));
|
|
24
24
|
// resolve first
|
|
@@ -29,10 +29,15 @@ describe("BaseAccount", () => {
|
|
|
29
29
|
expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "second" }));
|
|
30
30
|
// resolve second
|
|
31
31
|
resolve.shift()?.();
|
|
32
|
+
// wait next tick
|
|
33
|
+
await new Promise((res) => setTimeout(res, 0));
|
|
34
|
+
expect(Reflect.get(account, "queueLength")).toBe(0);
|
|
35
|
+
expect(Reflect.get(account, "lock")).toBeNull();
|
|
32
36
|
});
|
|
37
|
+
it("should cancel queue if request throws", () => { });
|
|
33
38
|
it("should not use queueing if its disabled", async () => {
|
|
34
39
|
const account = new BaseAccount(await signer.getPublicKey(), signer);
|
|
35
|
-
account.
|
|
40
|
+
account.disableQueue = false;
|
|
36
41
|
let resolve = [];
|
|
37
42
|
vi.spyOn(signer, "signEvent").mockImplementation(() => {
|
|
38
43
|
return new Promise((res) => {
|
|
@@ -42,14 +47,12 @@ describe("BaseAccount", () => {
|
|
|
42
47
|
// make two signing requests
|
|
43
48
|
account.signEvent({ kind: 1, content: "first", created_at: 0, tags: [] });
|
|
44
49
|
account.signEvent({ kind: 1, content: "second", created_at: 0, tags: [] });
|
|
50
|
+
expect(Reflect.get(account, "lock")).toBeNull();
|
|
45
51
|
expect(signer.signEvent).toHaveBeenCalledTimes(2);
|
|
46
52
|
expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "first" }));
|
|
47
53
|
expect(signer.signEvent).toHaveBeenCalledWith(expect.objectContaining({ content: "second" }));
|
|
48
|
-
// resolve
|
|
54
|
+
// resolve both
|
|
49
55
|
resolve.shift()?.();
|
|
50
|
-
// wait next tick
|
|
51
|
-
await new Promise((res) => setTimeout(res, 0));
|
|
52
|
-
// resolve second
|
|
53
56
|
resolve.shift()?.();
|
|
54
57
|
});
|
|
55
58
|
});
|
package/dist/manager.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare class AccountManager<Metadata extends unknown = any> {
|
|
|
7
7
|
get active(): IAccount<any, any, Metadata> | null;
|
|
8
8
|
accounts$: BehaviorSubject<IAccount<any, any, Metadata>[]>;
|
|
9
9
|
get accounts(): IAccount<any, any, Metadata>[];
|
|
10
|
+
/** Disable request queueing for any accounts added to this manager */
|
|
11
|
+
disableQueue?: boolean;
|
|
10
12
|
/** Add account type class */
|
|
11
13
|
registerType<S extends Nip07Interface>(accountType: IAccountConstructor<S, any, Metadata>): void;
|
|
12
14
|
/** Remove account type */
|
package/dist/manager.js
CHANGED
|
@@ -9,6 +9,8 @@ export class AccountManager {
|
|
|
9
9
|
get accounts() {
|
|
10
10
|
return this.accounts$.value;
|
|
11
11
|
}
|
|
12
|
+
/** Disable request queueing for any accounts added to this manager */
|
|
13
|
+
disableQueue;
|
|
12
14
|
// Account type CRUD
|
|
13
15
|
/** Add account type class */
|
|
14
16
|
registerType(accountType) {
|
|
@@ -44,6 +46,10 @@ export class AccountManager {
|
|
|
44
46
|
addAccount(account) {
|
|
45
47
|
if (this.getAccount(account.id))
|
|
46
48
|
return;
|
|
49
|
+
// copy the disableQueue flag only if its set
|
|
50
|
+
if (this.disableQueue !== undefined && account.disableQueue !== undefined) {
|
|
51
|
+
account.disableQueue = this.disableQueue;
|
|
52
|
+
}
|
|
47
53
|
this.accounts$.next({
|
|
48
54
|
...this.accounts$.value,
|
|
49
55
|
[account.id]: account,
|
package/dist/types.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export interface IAccount<Signer extends Nip07Interface, SignerData, Metadata ex
|
|
|
25
25
|
pubkey: string;
|
|
26
26
|
metadata?: Metadata;
|
|
27
27
|
signer: Signer;
|
|
28
|
+
disableQueue?: boolean;
|
|
28
29
|
toJSON(): SerializedAccount<SignerData, Metadata>;
|
|
29
30
|
}
|
|
30
31
|
export interface IAccountConstructor<Signer extends Nip07Interface, SignerData, Metadata extends unknown> {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-accounts",
|
|
3
|
-
"version": "0.0.0-next-
|
|
3
|
+
"version": "0.0.0-next-20250125183855",
|
|
4
4
|
"description": "A simple nostr account management system",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@noble/hashes": "^1.5.0",
|
|
35
|
-
"applesauce-signer": "0.0.0-next-
|
|
35
|
+
"applesauce-signer": "0.0.0-next-20250125183855",
|
|
36
36
|
"nanoid": "^5.0.9",
|
|
37
37
|
"nostr-tools": "^2.10.3",
|
|
38
38
|
"rxjs": "^7.8.1"
|