applesauce-actions 0.0.0-next-20250526151506 → 0.0.0-next-20250609183606
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/__tests__/action-hub.test.js +7 -3
- package/dist/action-hub.d.ts +6 -4
- package/dist/action-hub.js +4 -2
- package/dist/actions/__tests__/exports.test.js +4 -0
- package/dist/actions/__tests__/mute.test.js +8 -1
- package/dist/actions/bookmarks.js +1 -1
- package/dist/actions/contacts.js +1 -1
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.js +2 -0
- package/dist/actions/legacy-messages.d.ts +7 -0
- package/dist/actions/legacy-messages.js +20 -0
- package/dist/actions/pins.js +1 -1
- package/dist/actions/wrapped-messages.d.ts +20 -0
- package/dist/actions/wrapped-messages.js +37 -0
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { from, Subject } from "rxjs";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { subscribeSpyTo } from "@hirez_io/observer-spy";
|
|
4
4
|
import { EventFactory } from "applesauce-factory";
|
|
5
5
|
import { EventStore } from "applesauce-core";
|
|
@@ -7,8 +7,12 @@ import { FakeUser } from "./fake-user.js";
|
|
|
7
7
|
import { ActionHub } from "../action-hub.js";
|
|
8
8
|
import { CreateProfile } from "../actions/profile.js";
|
|
9
9
|
const user = new FakeUser();
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
let events = new EventStore();
|
|
11
|
+
let factory = new EventFactory({ signer: user });
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
events = new EventStore();
|
|
14
|
+
factory = new EventFactory({ signer: user });
|
|
15
|
+
});
|
|
12
16
|
describe("runAction", () => {
|
|
13
17
|
it("should handle action that return observables", async () => {
|
|
14
18
|
const e = [user.note(), user.profile({ name: "testing" })];
|
package/dist/action-hub.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
2
|
import { NostrEvent } from "nostr-tools";
|
|
3
|
-
import { ISyncEventStore } from "applesauce-core/event-store";
|
|
4
3
|
import { EventFactory } from "applesauce-factory";
|
|
4
|
+
import { IEventStoreActions, IEventStoreRead } from "applesauce-core";
|
|
5
5
|
/**
|
|
6
6
|
* A callback used to tell the upstream app to publish an event
|
|
7
7
|
* @param label a label describing what
|
|
@@ -10,7 +10,7 @@ export type PublishMethod = (event: NostrEvent) => void | Promise<void>;
|
|
|
10
10
|
/** The context that is passed to actions for them to use to preform actions */
|
|
11
11
|
export type ActionContext = {
|
|
12
12
|
/** The event store to load events from */
|
|
13
|
-
events:
|
|
13
|
+
events: IEventStoreRead;
|
|
14
14
|
/** The pubkey of the signer in the event factory */
|
|
15
15
|
self: string;
|
|
16
16
|
/** The event factory used to build and modify events */
|
|
@@ -21,10 +21,12 @@ export type Action = (ctx: ActionContext) => Observable<NostrEvent> | AsyncGener
|
|
|
21
21
|
export type ActionConstructor<Args extends Array<any>> = (...args: Args) => Action;
|
|
22
22
|
/** The main class that runs actions */
|
|
23
23
|
export declare class ActionHub {
|
|
24
|
-
events:
|
|
24
|
+
events: IEventStoreRead & IEventStoreActions;
|
|
25
25
|
factory: EventFactory;
|
|
26
26
|
publish?: PublishMethod | undefined;
|
|
27
|
-
|
|
27
|
+
/** Whether to save all events created by actions to the event store */
|
|
28
|
+
saveToStore: boolean;
|
|
29
|
+
constructor(events: IEventStoreRead & IEventStoreActions, factory: EventFactory, publish?: PublishMethod | undefined);
|
|
28
30
|
protected context: ActionContext | undefined;
|
|
29
31
|
protected getContext(): Promise<ActionContext>;
|
|
30
32
|
/** Runs an action in a ActionContext and converts the result to an Observable */
|
package/dist/action-hub.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { from, isObservable, lastValueFrom, switchMap, toArray } from "rxjs";
|
|
1
|
+
import { from, isObservable, lastValueFrom, switchMap, tap, toArray } from "rxjs";
|
|
2
2
|
/** The main class that runs actions */
|
|
3
3
|
export class ActionHub {
|
|
4
4
|
events;
|
|
5
5
|
factory;
|
|
6
6
|
publish;
|
|
7
|
+
/** Whether to save all events created by actions to the event store */
|
|
8
|
+
saveToStore = true;
|
|
7
9
|
constructor(events, factory, publish) {
|
|
8
10
|
this.events = events;
|
|
9
11
|
this.factory = factory;
|
|
@@ -44,6 +46,6 @@ export class ActionHub {
|
|
|
44
46
|
return from(this.getContext()).pipe(switchMap((ctx) => {
|
|
45
47
|
const action = Action(...args);
|
|
46
48
|
return ActionHub.runAction(ctx, action);
|
|
47
|
-
}));
|
|
49
|
+
}), tap((event) => this.saveToStore && this.events.add(event)));
|
|
48
50
|
}
|
|
49
51
|
}
|
|
@@ -44,6 +44,10 @@ describe("exports", () => {
|
|
|
44
44
|
"RemoveRelayFromRelaySet",
|
|
45
45
|
"RemoveSearchRelay",
|
|
46
46
|
"RemoveUserFromFollowSet",
|
|
47
|
+
"ReplyToLegacyMessage",
|
|
48
|
+
"ReplyToWrappedMessage",
|
|
49
|
+
"SendLegacyMessage",
|
|
50
|
+
"SendWrappedMessage",
|
|
47
51
|
"SetDefaultBlossomServer",
|
|
48
52
|
"SetListMetadata",
|
|
49
53
|
"UnbookmarkEvent",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { EventStore } from "applesauce-core";
|
|
3
3
|
import { EventFactory } from "applesauce-factory";
|
|
4
4
|
import { kinds } from "nostr-tools";
|
|
@@ -24,6 +24,7 @@ beforeEach(() => {
|
|
|
24
24
|
created_at: Math.floor(Date.now() / 1000),
|
|
25
25
|
});
|
|
26
26
|
events.add(muteList);
|
|
27
|
+
vi.useFakeTimers();
|
|
27
28
|
});
|
|
28
29
|
describe("MuteThread", () => {
|
|
29
30
|
it("should add an event to public tags in mute list", async () => {
|
|
@@ -43,9 +44,12 @@ describe("MuteThread", () => {
|
|
|
43
44
|
});
|
|
44
45
|
describe("UnmuteThread", () => {
|
|
45
46
|
it("should remove an event from public tags in mute list", async () => {
|
|
47
|
+
vi.setSystemTime(new Date("2025-01-01T00:00:00Z"));
|
|
46
48
|
// First add the thread to mute list
|
|
47
49
|
const addSpy = subscribeSpyTo(hub.exec(MuteThread, testEventId), { expectErrors: false });
|
|
48
50
|
await addSpy.onComplete();
|
|
51
|
+
// Wait a second to ensure events have newer created_at
|
|
52
|
+
await vi.advanceTimersByTime(1000);
|
|
49
53
|
// Then unmute it
|
|
50
54
|
const spy = subscribeSpyTo(hub.exec(UnmuteThread, testEventId), { expectErrors: false });
|
|
51
55
|
await spy.onComplete();
|
|
@@ -54,9 +58,12 @@ describe("UnmuteThread", () => {
|
|
|
54
58
|
expect(mutedThings.threads).not.toContain(testEventId);
|
|
55
59
|
});
|
|
56
60
|
it("should remove an event from hidden tags in mute list", async () => {
|
|
61
|
+
vi.setSystemTime(new Date("2025-01-01T00:00:00Z"));
|
|
57
62
|
// First add the thread to hidden mute list
|
|
58
63
|
const addSpy = subscribeSpyTo(hub.exec(MuteThread, testEventId, true), { expectErrors: false });
|
|
59
64
|
await addSpy.onComplete();
|
|
65
|
+
// Wait a second to ensure events have newer created_at
|
|
66
|
+
await vi.advanceTimersByTime(2000);
|
|
60
67
|
// Then unmute it
|
|
61
68
|
const spy = subscribeSpyTo(hub.exec(UnmuteThread, testEventId, true), { expectErrors: false });
|
|
62
69
|
await spy.onComplete();
|
|
@@ -52,7 +52,7 @@ export function CreateBookmarkSet(title, description, additional) {
|
|
|
52
52
|
const existing = getBookmarkEvent(events, self);
|
|
53
53
|
if (existing)
|
|
54
54
|
throw new Error("Bookmark list already exists");
|
|
55
|
-
const draft = await factory.
|
|
55
|
+
const draft = await factory.build({ kind: kinds.BookmarkList }, setListTitle(title), setListDescription(description), additional.image ? setListImage(additional.image) : undefined, additional.public ? modifyPublicTags(...additional.public.map(addEventBookmarkTag)) : undefined, additional.hidden ? modifyHiddenTags(...additional.hidden.map(addEventBookmarkTag)) : undefined);
|
|
56
56
|
yield await factory.sign(draft);
|
|
57
57
|
};
|
|
58
58
|
}
|
package/dist/actions/contacts.js
CHANGED
|
@@ -29,7 +29,7 @@ export function NewContacts(pubkeys) {
|
|
|
29
29
|
const contacts = events.getReplaceable(kinds.Contacts, self);
|
|
30
30
|
if (contacts)
|
|
31
31
|
throw new Error("Contact list already exists");
|
|
32
|
-
const draft = await factory.
|
|
32
|
+
const draft = await factory.build({ kind: kinds.Contacts, tags: pubkeys?.map((p) => ["p", p]) });
|
|
33
33
|
yield await factory.sign(draft);
|
|
34
34
|
};
|
|
35
35
|
}
|
package/dist/actions/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./contacts.js";
|
|
|
5
5
|
export * from "./dm-relays.js";
|
|
6
6
|
export * from "./favorite-relays.js";
|
|
7
7
|
export * from "./follow-sets.js";
|
|
8
|
+
export * from "./legacy-messages.js";
|
|
8
9
|
export * from "./lists.js";
|
|
9
10
|
export * from "./mailboxes.js";
|
|
10
11
|
export * from "./mute.js";
|
|
@@ -12,3 +13,4 @@ export * from "./pins.js";
|
|
|
12
13
|
export * from "./profile.js";
|
|
13
14
|
export * from "./relay-sets.js";
|
|
14
15
|
export * from "./search-relays.js";
|
|
16
|
+
export * from "./wrapped-messages.js";
|
package/dist/actions/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./contacts.js";
|
|
|
5
5
|
export * from "./dm-relays.js";
|
|
6
6
|
export * from "./favorite-relays.js";
|
|
7
7
|
export * from "./follow-sets.js";
|
|
8
|
+
export * from "./legacy-messages.js";
|
|
8
9
|
export * from "./lists.js";
|
|
9
10
|
export * from "./mailboxes.js";
|
|
10
11
|
export * from "./mute.js";
|
|
@@ -12,3 +13,4 @@ export * from "./pins.js";
|
|
|
12
13
|
export * from "./profile.js";
|
|
13
14
|
export * from "./relay-sets.js";
|
|
14
15
|
export * from "./search-relays.js";
|
|
16
|
+
export * from "./wrapped-messages.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LegacyMessageBlueprintOptions } from "applesauce-factory/blueprints";
|
|
2
|
+
import { NostrEvent } from "nostr-tools";
|
|
3
|
+
import { Action } from "../action-hub.js";
|
|
4
|
+
/** Sends a legacy NIP-04 message to a recipient */
|
|
5
|
+
export declare function SendLegacyMessage(recipient: string, message: string, opts?: LegacyMessageBlueprintOptions): Action;
|
|
6
|
+
/** Send a reply to a legacy message */
|
|
7
|
+
export declare function ReplyToLegacyMessage(parent: NostrEvent, message: string, opts?: LegacyMessageBlueprintOptions): Action;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { LegacyMessageBlueprint, LegacyMessageReplyBlueprint, } from "applesauce-factory/blueprints";
|
|
2
|
+
import { kinds } from "nostr-tools";
|
|
3
|
+
/** Sends a legacy NIP-04 message to a recipient */
|
|
4
|
+
export function SendLegacyMessage(recipient, message, opts) {
|
|
5
|
+
return async function* ({ factory }) {
|
|
6
|
+
const draft = await factory.create(LegacyMessageBlueprint, recipient, message, opts);
|
|
7
|
+
// Return the signed message
|
|
8
|
+
yield await factory.sign(draft);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Send a reply to a legacy message */
|
|
12
|
+
export function ReplyToLegacyMessage(parent, message, opts) {
|
|
13
|
+
return async function* ({ factory }) {
|
|
14
|
+
if (parent.kind !== kinds.EncryptedDirectMessage)
|
|
15
|
+
throw new Error("Legacy messages can only reply to other legacy messages");
|
|
16
|
+
const draft = await factory.create(LegacyMessageReplyBlueprint, parent, message, opts);
|
|
17
|
+
// Return the signed message
|
|
18
|
+
yield await factory.sign(draft);
|
|
19
|
+
};
|
|
20
|
+
}
|
package/dist/actions/pins.js
CHANGED
|
@@ -27,7 +27,7 @@ export function CreatePinList(pins = []) {
|
|
|
27
27
|
const existing = events.getReplaceable(kinds.Pinlist, self);
|
|
28
28
|
if (existing)
|
|
29
29
|
throw new Error("Pin list already exists");
|
|
30
|
-
const draft = await factory.
|
|
30
|
+
const draft = await factory.build({ kind: kinds.Pinlist }, modifyPublicTags(...pins.map((event) => addEventTag(event.id))));
|
|
31
31
|
yield await factory.sign(draft);
|
|
32
32
|
};
|
|
33
33
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Rumor } from "applesauce-core/helpers";
|
|
2
|
+
import { WrappedMessageBlueprintOptions } from "applesauce-factory/blueprints";
|
|
3
|
+
import { GiftWrapOptions } from "applesauce-factory/operations/event";
|
|
4
|
+
import { Action } from "../action-hub.js";
|
|
5
|
+
/**
|
|
6
|
+
* Sends a NIP-17 wrapped message to a conversation
|
|
7
|
+
* @param participants - A conversation identifier, user pubkey, or a list of participant pubkeys
|
|
8
|
+
* @param message - The message to send
|
|
9
|
+
* @param opts - Options for the wrapped message and gift wrap
|
|
10
|
+
* @returns Signed gift wrapped messages to send
|
|
11
|
+
*/
|
|
12
|
+
export declare function SendWrappedMessage(participants: string | string[], message: string, opts?: WrappedMessageBlueprintOptions & GiftWrapOptions): Action;
|
|
13
|
+
/**
|
|
14
|
+
* Sends a NIP-17 reply to a wrapped message
|
|
15
|
+
* @param parent - The parent wrapped message
|
|
16
|
+
* @param message - The message to send
|
|
17
|
+
* @param opts - Options for the wrapped message and gift wrap
|
|
18
|
+
* @returns Signed gift wrapped messages to send
|
|
19
|
+
*/
|
|
20
|
+
export declare function ReplyToWrappedMessage(parent: Rumor, message: string, opts?: WrappedMessageBlueprintOptions & GiftWrapOptions): Action;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getConversationParticipants } from "applesauce-core/helpers";
|
|
2
|
+
import { GiftWrapBlueprint, WrappedMessageBlueprint, WrappedMessageReplyBlueprint, } from "applesauce-factory/blueprints";
|
|
3
|
+
/**
|
|
4
|
+
* Sends a NIP-17 wrapped message to a conversation
|
|
5
|
+
* @param participants - A conversation identifier, user pubkey, or a list of participant pubkeys
|
|
6
|
+
* @param message - The message to send
|
|
7
|
+
* @param opts - Options for the wrapped message and gift wrap
|
|
8
|
+
* @returns Signed gift wrapped messages to send
|
|
9
|
+
*/
|
|
10
|
+
export function SendWrappedMessage(participants, message, opts) {
|
|
11
|
+
return async function* ({ factory }) {
|
|
12
|
+
const rumor = await factory.create(WrappedMessageBlueprint, participants, message, opts);
|
|
13
|
+
// Get the pubkeys to send this message to (will include the sender)
|
|
14
|
+
const pubkeys = getConversationParticipants(rumor);
|
|
15
|
+
for (const pubkey of pubkeys) {
|
|
16
|
+
yield await factory.create(GiftWrapBlueprint, pubkey, rumor, opts);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Sends a NIP-17 reply to a wrapped message
|
|
22
|
+
* @param parent - The parent wrapped message
|
|
23
|
+
* @param message - The message to send
|
|
24
|
+
* @param opts - Options for the wrapped message and gift wrap
|
|
25
|
+
* @returns Signed gift wrapped messages to send
|
|
26
|
+
*/
|
|
27
|
+
export function ReplyToWrappedMessage(parent, message, opts) {
|
|
28
|
+
return async function* ({ factory }) {
|
|
29
|
+
// Create the reply message
|
|
30
|
+
const rumor = await factory.create(WrappedMessageReplyBlueprint, parent, message, opts);
|
|
31
|
+
// Get the pubkeys to send this message to (will include the sender)
|
|
32
|
+
const pubkeys = getConversationParticipants(parent);
|
|
33
|
+
for (const pubkey of pubkeys) {
|
|
34
|
+
yield await factory.create(GiftWrapBlueprint, pubkey, rumor, opts);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-actions",
|
|
3
|
-
"version": "0.0.0-next-
|
|
3
|
+
"version": "0.0.0-next-20250609183606",
|
|
4
4
|
"description": "A package for performing common nostr actions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"applesauce-core": "0.0.0-next-
|
|
36
|
-
"applesauce-factory": "0.0.0-next-
|
|
35
|
+
"applesauce-core": "0.0.0-next-20250609183606",
|
|
36
|
+
"applesauce-factory": "0.0.0-next-20250609183606",
|
|
37
37
|
"nostr-tools": "^2.13",
|
|
38
38
|
"rxjs": "^7.8.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@hirez_io/observer-spy": "^2.2.0",
|
|
42
42
|
"@types/debug": "^4.1.12",
|
|
43
|
-
"applesauce-signers": "0.0.0-next-
|
|
43
|
+
"applesauce-signers": "0.0.0-next-20250609183606",
|
|
44
44
|
"nanoid": "^5.1.5",
|
|
45
45
|
"typescript": "^5.8.3",
|
|
46
46
|
"vitest": "^3.1.1"
|