applesauce-actions 0.0.0-next-20250425210455 → 0.0.0-next-20250428223834

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,5 +1,42 @@
1
1
  # Applesauce Actions
2
2
 
3
- A collection of pre-built actions nostr clients can use. built on top of `applesauce-core` and `applesauce-factory`
3
+ A collection of pre-built actions nostr clients can use. Built on top of `applesauce-core` and `applesauce-factory`.
4
4
 
5
5
  [Documentation](https://hzrd149.github.io/applesauce/typedoc/modules/applesauce_actions.html)
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install applesauce-actions
11
+ ```
12
+
13
+ ## Overview
14
+
15
+ Actions are common pre-built async operations that apps can perform. They use:
16
+
17
+ - `EventStore` for access to known nostr events
18
+ - `EventFactory` to build and sign new nostr events
19
+ - A `publish` method to publish or save the resulting events
20
+
21
+ The package provides an `ActionHub` class that combines these components into a single manager for easier action execution.
22
+
23
+ ## Basic Usage
24
+
25
+ ```typescript
26
+ import { ActionHub } from "applesauce-actions";
27
+ import { FollowUser } from "applesauce-actions/actions";
28
+
29
+ async function publishEvent(event: NostrEvent) {
30
+ await relayPool.publish(event, ["wss://relay.example.com"]);
31
+ }
32
+
33
+ // Create an action hub with your event store, factory and publish method
34
+ const hub = new ActionHub(eventStore, eventFactory, publishEvent);
35
+
36
+ // Example: Follow a user
37
+ await hub
38
+ .exec(FollowUser, "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")
39
+ .forEach((event) => publishEvent(event));
40
+ ```
41
+
42
+ For more detailed documentation and examples, visit the [full documentation](https://hzrd149.github.io/applesauce/overview/actions.html).
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,93 @@
1
+ import { EventStore } from "applesauce-core";
2
+ import { BLOSSOM_SERVER_LIST_KIND } from "applesauce-core/helpers/blossom";
3
+ import { EventFactory } from "applesauce-factory";
4
+ import { firstValueFrom, lastValueFrom } from "rxjs";
5
+ import { toArray } from "rxjs/operators";
6
+ import { beforeEach, describe, expect, it } from "vitest";
7
+ import { FakeUser } from "../../__tests__/fake-user.js";
8
+ import { ActionHub } from "../../action-hub.js";
9
+ import { AddBlossomServer, NewBlossomServers, RemoveBlossomServer, SetDefaultBlossomServer } from "../blossom.js";
10
+ const user = new FakeUser();
11
+ let events;
12
+ let factory;
13
+ let hub;
14
+ beforeEach(() => {
15
+ events = new EventStore();
16
+ factory = new EventFactory({ signer: user });
17
+ hub = new ActionHub(events, factory);
18
+ });
19
+ describe("NewBlossomServers", () => {
20
+ it("should publish a kind 10063 blossom server list", async () => {
21
+ const result = await lastValueFrom(hub.exec(NewBlossomServers, ["https://cdn.example.com/"]).pipe(toArray()));
22
+ expect(result[0]).toMatchObject({ kind: BLOSSOM_SERVER_LIST_KIND, tags: [["server", "https://cdn.example.com/"]] });
23
+ });
24
+ it("should throw if a blossom servers event already exists", async () => {
25
+ // Create the initial event
26
+ await hub.exec(NewBlossomServers, ["https://cdn.example.com/"]).forEach((e) => events.add(e));
27
+ // Attempt to create another one
28
+ await expect(lastValueFrom(hub.exec(NewBlossomServers, ["https://other.example.com/"]).pipe(toArray()))).rejects.toThrow("Blossom servers event already exists");
29
+ });
30
+ });
31
+ describe("AddBlossomServer", () => {
32
+ beforeEach(async () => {
33
+ // Create an initial empty server list
34
+ await hub.exec(NewBlossomServers, ["https://cdn.example.com/"]).forEach((e) => events.add(e));
35
+ });
36
+ it("should add a single server to the list", async () => {
37
+ const result = await firstValueFrom(hub.exec(AddBlossomServer, "https://other.example.com/"));
38
+ expect(result).toMatchObject({
39
+ kind: BLOSSOM_SERVER_LIST_KIND,
40
+ tags: expect.arrayContaining([["server", expect.stringContaining("other.example.com")]]),
41
+ });
42
+ });
43
+ it("should add multiple servers to the list", async () => {
44
+ const result = await firstValueFrom(hub.exec(AddBlossomServer, ["https://other.example.com/", "https://other2.example.com/"]));
45
+ expect(result).toMatchObject({
46
+ kind: BLOSSOM_SERVER_LIST_KIND,
47
+ tags: expect.arrayContaining([
48
+ ["server", expect.stringContaining("other.example.com")],
49
+ ["server", expect.stringContaining("other2.example.com")],
50
+ ]),
51
+ });
52
+ });
53
+ });
54
+ describe("RemoveBlossomServer", () => {
55
+ beforeEach(async () => {
56
+ // Create an initial server list with two servers
57
+ await hub
58
+ .exec(NewBlossomServers, ["https://cdn.example.com/", "https://other.example.com/"])
59
+ .forEach((e) => events.add(e));
60
+ });
61
+ it("should remove a server from the list", async () => {
62
+ const result = await firstValueFrom(hub.exec(RemoveBlossomServer, "https://cdn.example.com/"));
63
+ expect(result).toMatchObject({
64
+ kind: BLOSSOM_SERVER_LIST_KIND,
65
+ tags: expect.arrayContaining([["server", expect.stringContaining("other.example.com")]]),
66
+ });
67
+ });
68
+ it("should remove multiple servers from the list", async () => {
69
+ const result = await firstValueFrom(hub.exec(RemoveBlossomServer, ["https://cdn.example.com/", "https://other.example.com/"]));
70
+ expect(result).toMatchObject({
71
+ kind: BLOSSOM_SERVER_LIST_KIND,
72
+ tags: [],
73
+ });
74
+ });
75
+ });
76
+ describe("SetDefaultBlossomServer", () => {
77
+ beforeEach(async () => {
78
+ // Create an initial server list with two servers
79
+ await hub
80
+ .exec(NewBlossomServers, ["https://cdn.example.com/", "https://other.example.com/"])
81
+ .forEach((e) => events.add(e));
82
+ });
83
+ it("should move the specified server to the top of the list", async () => {
84
+ const result = await firstValueFrom(hub.exec(SetDefaultBlossomServer, "https://other.example.com/"));
85
+ expect(result).toMatchObject({
86
+ kind: BLOSSOM_SERVER_LIST_KIND,
87
+ tags: expect.arrayContaining([
88
+ ["server", expect.stringContaining("other.example.com")],
89
+ ["server", expect.stringContaining("cdn.example.com")],
90
+ ]),
91
+ });
92
+ });
93
+ });
@@ -0,0 +1,10 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a relay to the 10006 blocked relays event */
3
+ export declare function AddBlockedRelay(relay: string | string[], hidden?: boolean): Action;
4
+ /** An action that removes a relay from the 10006 blocked relays event */
5
+ export declare function RemoveBlockedRelay(relay: string | string[], hidden?: boolean): Action;
6
+ /** Creates a new blocked relays event */
7
+ export declare function NewBlockedRelays(relays?: string[] | {
8
+ public?: string[];
9
+ hidden?: string[];
10
+ }): Action;
@@ -0,0 +1,48 @@
1
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag/relay";
3
+ import { kinds } from "nostr-tools";
4
+ function getBlockedRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(kinds.BlockedRelaysList, self);
6
+ if (!event)
7
+ throw new Error("Can't find blocked relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10006 blocked relays event */
11
+ export function AddBlockedRelay(relay, hidden = false) {
12
+ return async function* ({ events, factory, self }) {
13
+ const blocked = getBlockedRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(blocked, hidden ? { hidden: operation } : operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10006 blocked relays event */
20
+ export function RemoveBlockedRelay(relay, hidden = false) {
21
+ return async function* ({ events, factory, self }) {
22
+ const blocked = getBlockedRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(blocked, hidden ? { hidden: operation } : operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** Creates a new blocked relays event */
29
+ export function NewBlockedRelays(relays) {
30
+ return async function* ({ events, factory, self }) {
31
+ const blocked = events.getReplaceable(kinds.BlockedRelaysList, self);
32
+ if (blocked)
33
+ throw new Error("Blocked relays event already exists");
34
+ let publicOperations = [];
35
+ let hiddenOperations = [];
36
+ if (Array.isArray(relays)) {
37
+ publicOperations.push(...relays.map((r) => addRelayTag(r)));
38
+ }
39
+ else {
40
+ if (relays?.public)
41
+ publicOperations.push(...(relays?.public ?? []).map((r) => addRelayTag(r)));
42
+ if (relays?.hidden)
43
+ hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
44
+ }
45
+ const draft = await factory.build({ kind: kinds.BlockedRelaysList }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
46
+ yield await factory.sign(draft);
47
+ };
48
+ }
@@ -0,0 +1,9 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a server to the Blossom servers event */
3
+ export declare function AddBlossomServer(server: string | URL | (string | URL)[]): Action;
4
+ /** An action that removes a server from the Blossom servers event */
5
+ export declare function RemoveBlossomServer(server: string | URL | (string | URL)[]): Action;
6
+ /** Makes a specific Blossom server the default server (move it to the top of the list) */
7
+ export declare function SetDefaultBlossomServer(server: string | URL): Action;
8
+ /** Creates a new Blossom servers event */
9
+ export declare function NewBlossomServers(servers?: (string | URL)[]): Action;
@@ -0,0 +1,52 @@
1
+ import { BLOSSOM_SERVER_LIST_KIND } from "applesauce-core/helpers/blossom";
2
+ import { modifyPublicTags } from "applesauce-factory/operations/event";
3
+ import { addBlossomServerTag, removeBlossomServerTag } from "applesauce-factory/operations/tag/blossom";
4
+ function getBlossomServersEvent(events, self) {
5
+ const event = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
6
+ if (!event)
7
+ throw new Error("Can't find Blossom servers event");
8
+ return event;
9
+ }
10
+ /** An action that adds a server to the Blossom servers event */
11
+ export function AddBlossomServer(server) {
12
+ return async function* ({ events, factory, self }) {
13
+ const servers = getBlossomServersEvent(events, self);
14
+ const operation = Array.isArray(server) ? server.map((s) => addBlossomServerTag(s)) : addBlossomServerTag(server);
15
+ const draft = await factory.modifyTags(servers, operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a server from the Blossom servers event */
20
+ export function RemoveBlossomServer(server) {
21
+ return async function* ({ events, factory, self }) {
22
+ const servers = getBlossomServersEvent(events, self);
23
+ const operation = Array.isArray(server)
24
+ ? server.map((s) => removeBlossomServerTag(s))
25
+ : removeBlossomServerTag(server);
26
+ const draft = await factory.modifyTags(servers, operation);
27
+ yield await factory.sign(draft);
28
+ };
29
+ }
30
+ /** Makes a specific Blossom server the default server (move it to the top of the list) */
31
+ export function SetDefaultBlossomServer(server) {
32
+ return async function* ({ events, factory, self }) {
33
+ const servers = getBlossomServersEvent(events, self);
34
+ const prependTag = (tag) => (tags) => [tag, ...tags];
35
+ const draft = await factory.modifyTags(servers, [
36
+ removeBlossomServerTag(server),
37
+ prependTag(["server", String(server)]),
38
+ ]);
39
+ yield await factory.sign(draft);
40
+ };
41
+ }
42
+ /** Creates a new Blossom servers event */
43
+ export function NewBlossomServers(servers) {
44
+ return async function* ({ events, factory, self }) {
45
+ const existing = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
46
+ if (existing)
47
+ throw new Error("Blossom servers event already exists");
48
+ const operations = servers ? servers.map((s) => addBlossomServerTag(s)) : [];
49
+ const draft = await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, modifyPublicTags(...operations));
50
+ yield await factory.sign(draft);
51
+ };
52
+ }
@@ -0,0 +1,7 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a relay to the 10050 DM relays event */
3
+ export declare function AddDMRelay(relay: string | string[]): Action;
4
+ /** An action that removes a relay from the 10050 DM relays event */
5
+ export declare function RemoveDMRelay(relay: string | string[]): Action;
6
+ /** Creates a new DM relays event */
7
+ export declare function NewDMRelays(relays?: string[]): Action;
@@ -0,0 +1,38 @@
1
+ import { modifyPublicTags } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag/relay";
3
+ import { kinds } from "nostr-tools";
4
+ function getDMRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(kinds.DirectMessageRelaysList, self);
6
+ if (!event)
7
+ throw new Error("Can't find DM relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10050 DM relays event */
11
+ export function AddDMRelay(relay) {
12
+ return async function* ({ events, factory, self }) {
13
+ const dmRelays = getDMRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(dmRelays, operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10050 DM relays event */
20
+ export function RemoveDMRelay(relay) {
21
+ return async function* ({ events, factory, self }) {
22
+ const dmRelays = getDMRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(dmRelays, operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** Creates a new DM relays event */
29
+ export function NewDMRelays(relays) {
30
+ return async function* ({ events, factory, self }) {
31
+ const dmRelays = events.getReplaceable(kinds.DirectMessageRelaysList, self);
32
+ if (dmRelays)
33
+ throw new Error("DM relays event already exists");
34
+ const operations = relays?.map((r) => addRelayTag(r)) ?? [];
35
+ const draft = await factory.build({ kind: kinds.DirectMessageRelaysList }, modifyPublicTags(...operations));
36
+ yield await factory.sign(draft);
37
+ };
38
+ }
@@ -0,0 +1,18 @@
1
+ import { AddressPointer } from "nostr-tools/nip19";
2
+ import { Action } from "../action-hub.js";
3
+ /** An action that adds a relay to the 10012 favorite relays event */
4
+ export declare function AddFavoriteRelay(relay: string | string[], hidden?: boolean): Action;
5
+ /** An action that removes a relay from the 10012 favorite relays event */
6
+ export declare function RemoveFavoriteRelay(relay: string | string[], hidden?: boolean): Action;
7
+ /** An action that adds a relay set to the 10012 favorite relays event */
8
+ export declare function AddFavoriteRelaySet(addr: AddressPointer[] | AddressPointer, hidden?: boolean): Action;
9
+ /** An action that removes a relay set from the 10012 favorite relays event */
10
+ export declare function RemoveFavoriteRelaySet(addr: AddressPointer[] | AddressPointer, hidden?: boolean): Action;
11
+ /** Creates a new favorite relays event */
12
+ export declare function NewFavoriteRelays(relays?: string[] | {
13
+ public?: string[];
14
+ hidden?: string[];
15
+ }, sets?: AddressPointer[] | {
16
+ public?: AddressPointer[];
17
+ hidden?: AddressPointer[];
18
+ }): Action;
@@ -0,0 +1,75 @@
1
+ import { FAVORITE_RELAYS_KIND } from "applesauce-core/helpers/lists";
2
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations/event";
3
+ import { addCoordinateTag, addRelayTag, removeCoordinateTag, removeRelayTag } from "applesauce-factory/operations/tag";
4
+ function getFavoriteRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(FAVORITE_RELAYS_KIND, self);
6
+ if (!event)
7
+ throw new Error("Can't find favorite relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10012 favorite relays event */
11
+ export function AddFavoriteRelay(relay, hidden = false) {
12
+ return async function* ({ events, factory, self }) {
13
+ const favorites = getFavoriteRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10012 favorite relays event */
20
+ export function RemoveFavoriteRelay(relay, hidden = false) {
21
+ return async function* ({ events, factory, self }) {
22
+ const favorites = getFavoriteRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** An action that adds a relay set to the 10012 favorite relays event */
29
+ export function AddFavoriteRelaySet(addr, hidden = false) {
30
+ return async function* ({ events, factory, self }) {
31
+ const favorites = getFavoriteRelaysEvent(events, self);
32
+ const operation = Array.isArray(addr) ? addr.map((a) => addCoordinateTag(a)) : addCoordinateTag(addr);
33
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
34
+ yield await factory.sign(draft);
35
+ };
36
+ }
37
+ /** An action that removes a relay set from the 10012 favorite relays event */
38
+ export function RemoveFavoriteRelaySet(addr, hidden = false) {
39
+ return async function* ({ events, factory, self }) {
40
+ const favorites = getFavoriteRelaysEvent(events, self);
41
+ const operation = Array.isArray(addr) ? addr.map((a) => removeCoordinateTag(a)) : removeCoordinateTag(addr);
42
+ const draft = await factory.modifyTags(favorites, hidden ? { hidden: operation } : operation);
43
+ yield await factory.sign(draft);
44
+ };
45
+ }
46
+ /** Creates a new favorite relays event */
47
+ export function NewFavoriteRelays(relays, sets) {
48
+ return async function* ({ events, factory, self }) {
49
+ const favorites = events.getReplaceable(FAVORITE_RELAYS_KIND, self);
50
+ if (favorites)
51
+ throw new Error("Favorite relays event already exists");
52
+ let publicOperations = [];
53
+ let hiddenOperations = [];
54
+ if (Array.isArray(relays)) {
55
+ publicOperations.push(...relays.map((r) => addRelayTag(r)));
56
+ }
57
+ else {
58
+ if (relays?.public)
59
+ publicOperations.push(...(relays?.public ?? []).map((r) => addRelayTag(r)));
60
+ if (relays?.hidden)
61
+ hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
62
+ }
63
+ if (Array.isArray(sets)) {
64
+ publicOperations.push(...sets.map((s) => addCoordinateTag(s)));
65
+ }
66
+ else {
67
+ if (sets?.public)
68
+ publicOperations.push(...(sets?.public ?? []).map((s) => addCoordinateTag(s)));
69
+ if (sets?.hidden)
70
+ hiddenOperations.push(...(sets?.hidden ?? []).map((s) => addCoordinateTag(s)));
71
+ }
72
+ const draft = await factory.build({ kind: FAVORITE_RELAYS_KIND }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
73
+ yield await factory.sign(draft);
74
+ };
75
+ }
@@ -0,0 +1,2 @@
1
+ import { Action } from "../action-hub.js";
2
+ export declare function AddFavoriteRelay(relay: string): Action;
@@ -0,0 +1,14 @@
1
+ import { kinds } from "nostr-tools";
2
+ function getFollowSetEvent(events, self, identifier) {
3
+ const set = typeof identifier === "string" ? events.getReplaceable(kinds.Followsets, self, identifier) : identifier;
4
+ if (!set)
5
+ throw new Error("Can't find follow set");
6
+ if (set.kind !== kinds.FavoriteRelays)
7
+ throw new Error("Event is not a favorite relays");
8
+ return set;
9
+ }
10
+ export function AddFavoriteRelay(relay) {
11
+ return async function* ({ events, factory, self }) {
12
+ const favoriteRelays = events.getReplaceable(kinds.FavoriteRelays, self);
13
+ };
14
+ }
@@ -1,7 +1,13 @@
1
+ export * from "./blocked-relays.js";
2
+ export * from "./blossom.js";
1
3
  export * from "./bookmarks.js";
2
4
  export * from "./contacts.js";
5
+ export * from "./dm-relays.js";
6
+ export * from "./favorite-relays.js";
3
7
  export * from "./follow-sets.js";
4
8
  export * from "./mailboxes.js";
9
+ export * from "./mute.js";
5
10
  export * from "./pins.js";
6
11
  export * from "./profile.js";
7
- export * from "./mute.js";
12
+ export * from "./relay-sets.js";
13
+ export * from "./search-relays.js";
@@ -1,7 +1,13 @@
1
+ export * from "./blocked-relays.js";
2
+ export * from "./blossom.js";
1
3
  export * from "./bookmarks.js";
2
4
  export * from "./contacts.js";
5
+ export * from "./dm-relays.js";
6
+ export * from "./favorite-relays.js";
3
7
  export * from "./follow-sets.js";
4
8
  export * from "./mailboxes.js";
9
+ export * from "./mute.js";
5
10
  export * from "./pins.js";
6
11
  export * from "./profile.js";
7
- export * from "./mute.js";
12
+ export * from "./relay-sets.js";
13
+ export * from "./search-relays.js";
@@ -0,0 +1,19 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { Action } from "../action-hub.js";
3
+ /** An action that adds a relay to a relay set*/
4
+ export declare function AddRelayToRelaySet(relay: string | string[], identifier: NostrEvent | string, hidden?: boolean): Action;
5
+ /** An action that removes a relay from a relay set */
6
+ export declare function RemoveRelayFromRelaySet(relay: string | string[], identifier: NostrEvent | string, hidden?: boolean): Action;
7
+ /** An action that creates a new relay set */
8
+ export declare function CreateRelaySet(title: string, options?: {
9
+ description?: string;
10
+ image?: string;
11
+ public?: string[];
12
+ hidden?: string[];
13
+ }): Action;
14
+ /** An action that updates the title, description, or image of a relay set */
15
+ export declare function UpdateRelaySetInformation(identifier: string, info: {
16
+ title?: string;
17
+ description?: string;
18
+ image?: string;
19
+ }): Action;
@@ -0,0 +1,44 @@
1
+ import { modifyHiddenTags, modifyPublicTags, setListDescription, setListImage, setListTitle, } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag";
3
+ import { kinds } from "nostr-tools";
4
+ function getRelaySetEvent(events, self, identifier) {
5
+ const set = typeof identifier === "string" ? events.getReplaceable(kinds.Relaysets, self, identifier) : identifier;
6
+ if (!set)
7
+ throw new Error("Can't find relay set");
8
+ if (set.kind !== kinds.Relaysets)
9
+ throw new Error("Event is not a relay set");
10
+ return set;
11
+ }
12
+ /** An action that adds a relay to a relay set*/
13
+ export function AddRelayToRelaySet(relay, identifier, hidden = false) {
14
+ return async function* ({ events, factory, self }) {
15
+ const relays = getRelaySetEvent(events, self, identifier);
16
+ const operations = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
17
+ const draft = await factory.modifyTags(relays, hidden ? { hidden: operations } : operations);
18
+ yield await factory.sign(draft);
19
+ };
20
+ }
21
+ /** An action that removes a relay from a relay set */
22
+ export function RemoveRelayFromRelaySet(relay, identifier, hidden = false) {
23
+ return async function* ({ events, factory, self }) {
24
+ const relays = getRelaySetEvent(events, self, identifier);
25
+ const operations = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
26
+ const draft = await factory.modifyTags(relays, hidden ? { hidden: operations } : operations);
27
+ yield await factory.sign(draft);
28
+ };
29
+ }
30
+ /** An action that creates a new relay set */
31
+ export function CreateRelaySet(title, options) {
32
+ return async function* ({ factory }) {
33
+ const draft = await factory.build({ kind: kinds.Relaysets }, setListTitle(title), options?.description ? setListDescription(options.description) : undefined, options?.image ? setListImage(options.image) : undefined, options?.public ? modifyPublicTags(...options.public.map((r) => addRelayTag(r))) : undefined, options?.hidden ? modifyHiddenTags(...options.hidden.map((r) => addRelayTag(r))) : undefined);
34
+ yield await factory.sign(draft);
35
+ };
36
+ }
37
+ /** An action that updates the title, description, or image of a relay set */
38
+ export function UpdateRelaySetInformation(identifier, info) {
39
+ return async function* ({ events, factory, self }) {
40
+ const relays = getRelaySetEvent(events, self, identifier);
41
+ const draft = await factory.modify(relays, info?.title ? setListTitle(info.title) : undefined, info?.description ? setListDescription(info.description) : undefined, info?.image ? setListImage(info.image) : undefined);
42
+ yield await factory.sign(draft);
43
+ };
44
+ }
@@ -0,0 +1,10 @@
1
+ import { Action } from "../action-hub.js";
2
+ /** An action that adds a relay to the 10007 search relays event */
3
+ export declare function AddSearchRelay(relay: string | string[], hidden?: boolean): Action;
4
+ /** An action that removes a relay from the 10007 search relays event */
5
+ export declare function RemoveSearchRelay(relay: string | string[], hidden?: boolean): Action;
6
+ /** Creates a new search relays event */
7
+ export declare function NewSearchRelays(relays?: string[] | {
8
+ public?: string[];
9
+ hidden?: string[];
10
+ }): Action;
@@ -0,0 +1,48 @@
1
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-factory/operations/event";
2
+ import { addRelayTag, removeRelayTag } from "applesauce-factory/operations/tag/relay";
3
+ import { kinds } from "nostr-tools";
4
+ function getSearchRelaysEvent(events, self) {
5
+ const event = events.getReplaceable(kinds.SearchRelaysList, self);
6
+ if (!event)
7
+ throw new Error("Can't find search relays event");
8
+ return event;
9
+ }
10
+ /** An action that adds a relay to the 10007 search relays event */
11
+ export function AddSearchRelay(relay, hidden = false) {
12
+ return async function* ({ events, factory, self }) {
13
+ const search = getSearchRelaysEvent(events, self);
14
+ const operation = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
15
+ const draft = await factory.modifyTags(search, hidden ? { hidden: operation } : operation);
16
+ yield await factory.sign(draft);
17
+ };
18
+ }
19
+ /** An action that removes a relay from the 10007 search relays event */
20
+ export function RemoveSearchRelay(relay, hidden = false) {
21
+ return async function* ({ events, factory, self }) {
22
+ const search = getSearchRelaysEvent(events, self);
23
+ const operation = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
24
+ const draft = await factory.modifyTags(search, hidden ? { hidden: operation } : operation);
25
+ yield await factory.sign(draft);
26
+ };
27
+ }
28
+ /** Creates a new search relays event */
29
+ export function NewSearchRelays(relays) {
30
+ return async function* ({ events, factory, self }) {
31
+ const search = events.getReplaceable(kinds.SearchRelaysList, self);
32
+ if (search)
33
+ throw new Error("Search relays event already exists");
34
+ let publicOperations = [];
35
+ let hiddenOperations = [];
36
+ if (Array.isArray(relays)) {
37
+ publicOperations.push(...relays.map((r) => addRelayTag(r)));
38
+ }
39
+ else {
40
+ if (relays?.public)
41
+ publicOperations.push(...(relays?.public ?? []).map((r) => addRelayTag(r)));
42
+ if (relays?.hidden)
43
+ hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
44
+ }
45
+ const draft = await factory.build({ kind: kinds.SearchRelaysList }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
46
+ yield await factory.sign(draft);
47
+ };
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-actions",
3
- "version": "0.0.0-next-20250425210455",
3
+ "version": "0.0.0-next-20250428223834",
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-20250425210455",
36
- "applesauce-factory": "0.0.0-next-20250425210455",
35
+ "applesauce-core": "0.0.0-next-20250428223834",
36
+ "applesauce-factory": "0.0.0-next-20250428223834",
37
37
  "nostr-tools": "^2.10.4",
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-20250425210455",
43
+ "applesauce-signers": "0.0.0-next-20250428223834",
44
44
  "nanoid": "^5.1.5",
45
45
  "typescript": "^5.8.3",
46
46
  "vitest": "^3.1.1"