applesauce-actions 0.0.0-next-20251209200210 → 0.0.0-next-20251231055351

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.
Files changed (50) hide show
  1. package/README.md +3 -3
  2. package/dist/action-runner.d.ts +53 -0
  3. package/dist/action-runner.js +91 -0
  4. package/dist/actions/app-data.d.ts +1 -1
  5. package/dist/actions/app-data.js +5 -5
  6. package/dist/actions/blocked-relays.d.ts +1 -6
  7. package/dist/actions/blocked-relays.js +30 -36
  8. package/dist/actions/blossom.d.ts +1 -1
  9. package/dist/actions/blossom.js +25 -32
  10. package/dist/actions/bookmarks.d.ts +4 -3
  11. package/dist/actions/bookmarks.js +64 -55
  12. package/dist/actions/calendar.d.ts +6 -3
  13. package/dist/actions/calendar.js +26 -12
  14. package/dist/actions/comment.d.ts +10 -0
  15. package/dist/actions/comment.js +43 -0
  16. package/dist/actions/contacts.d.ts +3 -3
  17. package/dist/actions/contacts.js +27 -27
  18. package/dist/actions/direct-message-relays.d.ts +7 -0
  19. package/dist/actions/direct-message-relays.js +47 -0
  20. package/dist/actions/favorite-relays.d.ts +1 -1
  21. package/dist/actions/favorite-relays.js +34 -33
  22. package/dist/actions/follow-sets.d.ts +1 -3
  23. package/dist/actions/follow-sets.js +32 -31
  24. package/dist/actions/index.d.ts +2 -1
  25. package/dist/actions/index.js +2 -1
  26. package/dist/actions/legacy-messages.d.ts +2 -2
  27. package/dist/actions/legacy-messages.js +24 -9
  28. package/dist/actions/lists.d.ts +1 -1
  29. package/dist/actions/lists.js +5 -3
  30. package/dist/actions/mailboxes.d.ts +1 -1
  31. package/dist/actions/mailboxes.js +31 -29
  32. package/dist/actions/mute.d.ts +1 -1
  33. package/dist/actions/mute.js +32 -60
  34. package/dist/actions/pins.d.ts +1 -3
  35. package/dist/actions/pins.js +20 -26
  36. package/dist/actions/profile.d.ts +1 -1
  37. package/dist/actions/profile.js +14 -9
  38. package/dist/actions/relay-sets.d.ts +1 -1
  39. package/dist/actions/relay-sets.js +36 -29
  40. package/dist/actions/search-relays.d.ts +1 -1
  41. package/dist/actions/search-relays.js +19 -19
  42. package/dist/actions/wrapped-messages.d.ts +4 -2
  43. package/dist/actions/wrapped-messages.js +35 -14
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.js +1 -1
  46. package/package.json +4 -4
  47. package/dist/action-hub.d.ts +0 -35
  48. package/dist/action-hub.js +0 -51
  49. package/dist/actions/dm-relays.d.ts +0 -7
  50. package/dist/actions/dm-relays.js +0 -38
@@ -1,6 +1,6 @@
1
1
  import { NostrEvent } from "applesauce-core/helpers/event";
2
2
  import { EventPointer } from "applesauce-core/helpers/pointers";
3
- import { Action } from "../action-hub.js";
3
+ import { Action } from "../action-runner.js";
4
4
  /** An action that adds a pubkey to the mute list */
5
5
  export declare function MuteUser(pubkey: string, hidden?: boolean): Action;
6
6
  /** Removes a pubkey from the mute list */
@@ -1,79 +1,51 @@
1
1
  import { kinds } from "applesauce-core/helpers/event";
2
+ import { modifyHiddenTags, modifyPublicTags } from "applesauce-core/operations";
2
3
  import { addEventPointerTag, addNameValueTag, addProfilePointerTag, removeEventPointerTag, removeNameValueTag, removeProfilePointerTag, } from "applesauce-core/operations/tag/common";
3
- function ensureMuteList(events, self) {
4
- const mute = events.getReplaceable(kinds.Mutelist, self);
5
- if (!mute)
6
- throw new Error("No mute list found");
7
- return mute;
4
+ function ModifyMuteEvent(operations, hidden = false) {
5
+ return async ({ factory, user, publish, sign }) => {
6
+ const [event, outboxes] = await Promise.all([
7
+ user.replaceable(kinds.Mutelist).$first(1000, undefined),
8
+ user.outboxes$.$first(1000, undefined),
9
+ ]);
10
+ // Create the event operation
11
+ const operation = hidden ? modifyHiddenTags(...operations) : modifyPublicTags(...operations);
12
+ // Modify or build new event
13
+ const signed = event
14
+ ? await factory.modify(event, operation).then(sign)
15
+ : await factory.build({ kind: kinds.Mutelist }, operation).then(sign);
16
+ // Publish the event to the user's outboxes
17
+ await publish(signed, outboxes);
18
+ };
8
19
  }
9
20
  /** An action that adds a pubkey to the mute list */
10
- export function MuteUser(pubkey, hidden = false) {
11
- return async function* ({ events, factory, self }) {
12
- const mute = ensureMuteList(events, self);
13
- const operation = addProfilePointerTag(pubkey);
14
- const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
15
- yield await factory.sign(draft);
16
- };
21
+ export function MuteUser(pubkey, hidden) {
22
+ return ModifyMuteEvent([addProfilePointerTag(pubkey)], hidden);
17
23
  }
18
24
  /** Removes a pubkey from the mute list */
19
- export function UnmuteUser(pubkey, hidden = false) {
20
- return async function* ({ events, factory, self }) {
21
- const mute = ensureMuteList(events, self);
22
- const draft = await factory.modifyTags(mute, hidden ? { hidden: removeProfilePointerTag(pubkey) } : removeProfilePointerTag(pubkey));
23
- yield await factory.sign(draft);
24
- };
25
+ export function UnmuteUser(pubkey, hidden) {
26
+ return ModifyMuteEvent([removeProfilePointerTag(pubkey)], hidden);
25
27
  }
26
28
  /** Add a thread to the mute list */
27
- export function MuteThread(thread, hidden = false) {
28
- return async function* ({ events, factory, self }) {
29
- const mute = ensureMuteList(events, self);
30
- const operation = addEventPointerTag(thread);
31
- const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
32
- yield await factory.sign(draft);
33
- };
29
+ export function MuteThread(thread, hidden) {
30
+ return ModifyMuteEvent([addEventPointerTag(thread)], hidden);
34
31
  }
35
32
  /** Removes a thread from the mute list */
36
- export function UnmuteThread(thread, hidden = false) {
37
- return async function* ({ events, factory, self }) {
38
- const mute = ensureMuteList(events, self);
39
- const operation = removeEventPointerTag(thread);
40
- const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
41
- yield await factory.sign(draft);
42
- };
33
+ export function UnmuteThread(thread, hidden) {
34
+ return ModifyMuteEvent([removeEventPointerTag(thread)], hidden);
43
35
  }
44
36
  /** Add a word to the mute list */
45
- export function MuteWord(word, hidden = false) {
46
- return async function* ({ events, factory, self }) {
47
- const mute = ensureMuteList(events, self);
48
- const operation = addNameValueTag(["word", word.toLocaleLowerCase()], true);
49
- const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
50
- yield await factory.sign(draft);
51
- };
37
+ export function MuteWord(word, hidden) {
38
+ return ModifyMuteEvent([addNameValueTag(["word", word.toLocaleLowerCase()], true)], hidden);
52
39
  }
53
40
  /** Removes a word from the mute list */
54
- export function UnmuteWord(word, hidden = false) {
55
- return async function* ({ events, factory, self }) {
56
- const mute = ensureMuteList(events, self);
57
- const operation = removeNameValueTag(["word", word.toLocaleLowerCase()]);
58
- const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
59
- yield await factory.sign(draft);
60
- };
41
+ export function UnmuteWord(word, hidden) {
42
+ return ModifyMuteEvent([removeNameValueTag(["word", word.toLocaleLowerCase()])], hidden);
61
43
  }
62
44
  /** Add a hashtag to the mute list */
63
- export function MuteHashtag(hashtag, hidden = false) {
64
- return async function* ({ events, factory, self }) {
65
- const mute = ensureMuteList(events, self);
66
- const operation = addNameValueTag(["t", hashtag.toLocaleLowerCase()], true);
67
- const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
68
- yield await factory.sign(draft);
69
- };
45
+ export function MuteHashtag(hashtag, hidden) {
46
+ return ModifyMuteEvent([addNameValueTag(["t", hashtag.toLocaleLowerCase()], true)], hidden);
70
47
  }
71
48
  /** Removes a hashtag from the mute list */
72
- export function UnmuteHashtag(hashtag, hidden = false) {
73
- return async function* ({ events, factory, self }) {
74
- const mute = ensureMuteList(events, self);
75
- const operation = removeNameValueTag(["t", hashtag.toLocaleLowerCase()]);
76
- const draft = await factory.modifyTags(mute, hidden ? { hidden: operation } : operation);
77
- yield await factory.sign(draft);
78
- };
49
+ export function UnmuteHashtag(hashtag, hidden) {
50
+ return ModifyMuteEvent([removeNameValueTag(["t", hashtag.toLocaleLowerCase()])], hidden);
79
51
  }
@@ -1,9 +1,7 @@
1
1
  import { NostrEvent } from "applesauce-core/helpers/event";
2
- import { Action } from "../action-hub.js";
2
+ import { Action } from "../action-runner.js";
3
3
  export declare const ALLOWED_PIN_KINDS: number[];
4
4
  /** An action that pins a note to the users pin list */
5
5
  export declare function PinNote(note: NostrEvent): Action;
6
6
  /** An action that removes an event from the users pin list */
7
7
  export declare function UnpinNote(note: NostrEvent): Action;
8
- /** An action that creates a new pin list for a user */
9
- export declare function CreatePinList(pins?: NostrEvent[]): Action;
@@ -1,38 +1,32 @@
1
1
  import { isReplaceable, kinds } from "applesauce-core/helpers/event";
2
2
  import { addAddressPointerTag, addEventPointerTag, removeAddressPointerTag, removeEventPointerTag, } from "applesauce-core/operations/tag/common";
3
3
  import { modifyPublicTags } from "applesauce-core/operations/tags";
4
+ function ModifyPinListEvent(operations) {
5
+ return async ({ factory, user, publish, sign }) => {
6
+ const [event, outboxes] = await Promise.all([
7
+ user.replaceable(kinds.Pinlist).$first(1000, undefined),
8
+ user.outboxes$.$first(1000, undefined),
9
+ ]);
10
+ // Create the event operation
11
+ const operation = modifyPublicTags(...operations);
12
+ // Modify or build new event
13
+ const signed = event
14
+ ? await factory.modify(event, operation).then(sign)
15
+ : await factory.build({ kind: kinds.Pinlist }, operation).then(sign);
16
+ // Publish the event to the user's outboxes
17
+ await publish(signed, outboxes);
18
+ };
19
+ }
4
20
  export const ALLOWED_PIN_KINDS = [kinds.ShortTextNote, kinds.LongFormArticle];
5
21
  /** An action that pins a note to the users pin list */
6
22
  export function PinNote(note) {
7
23
  if (!ALLOWED_PIN_KINDS.includes(note.kind))
8
24
  throw new Error(`Event kind ${note.kind} can not be pinned`);
9
- return async function* ({ events, factory, self }) {
10
- const pins = events.getReplaceable(kinds.Pinlist, self);
11
- const operation = isReplaceable(note.kind) ? addAddressPointerTag(note) : addEventPointerTag(note.id);
12
- const draft = pins
13
- ? await factory.modifyTags(pins, operation)
14
- : await factory.build({ kind: kinds.Pinlist }, modifyPublicTags(operation));
15
- yield await factory.sign(draft);
16
- };
25
+ return ModifyPinListEvent([isReplaceable(note.kind) ? addAddressPointerTag(note) : addEventPointerTag(note.id)]);
17
26
  }
18
27
  /** An action that removes an event from the users pin list */
19
28
  export function UnpinNote(note) {
20
- return async function* ({ events, factory, self }) {
21
- const pins = events.getReplaceable(kinds.Pinlist, self);
22
- if (!pins)
23
- return;
24
- const operation = isReplaceable(note.kind) ? removeAddressPointerTag(note) : removeEventPointerTag(note.id);
25
- const draft = await factory.modifyTags(pins, operation);
26
- yield await factory.sign(draft);
27
- };
28
- }
29
- /** An action that creates a new pin list for a user */
30
- export function CreatePinList(pins = []) {
31
- return async function* ({ events, factory, self }) {
32
- const existing = events.getReplaceable(kinds.Pinlist, self);
33
- if (existing)
34
- throw new Error("Pin list already exists");
35
- const draft = await factory.build({ kind: kinds.Pinlist }, modifyPublicTags(...pins.map((event) => addEventPointerTag(event.id))));
36
- yield await factory.sign(draft);
37
- };
29
+ return ModifyPinListEvent([
30
+ isReplaceable(note.kind) ? removeAddressPointerTag(note) : removeEventPointerTag(note.id),
31
+ ]);
38
32
  }
@@ -1,5 +1,5 @@
1
1
  import { ProfileContent } from "applesauce-core/helpers/profile";
2
- import { Action } from "../action-hub.js";
2
+ import { Action } from "../action-runner.js";
3
3
  /** An action that creates a new kind 0 profile event for a user */
4
4
  export declare function CreateProfile(content: ProfileContent): Action;
5
5
  /** An action that updates a kind 0 profile evnet for a user */
@@ -2,21 +2,26 @@ import { kinds } from "applesauce-core/helpers/event";
2
2
  import * as Profile from "applesauce-core/operations/profile";
3
3
  /** An action that creates a new kind 0 profile event for a user */
4
4
  export function CreateProfile(content) {
5
- return async function* ({ events, factory, self }) {
5
+ return async ({ events, factory, self, publish, sign }) => {
6
6
  const metadata = events.getReplaceable(kinds.Metadata, self);
7
7
  if (metadata)
8
8
  throw new Error("Profile already exists");
9
- const draft = await factory.build({ kind: kinds.Metadata }, Profile.setProfile(content));
10
- yield await factory.sign(draft);
9
+ const signed = await factory.build({ kind: kinds.Metadata }, Profile.setProfile(content)).then(sign);
10
+ // No outboxes to publish to since this is probably a new user
11
+ await publish(signed);
11
12
  };
12
13
  }
13
14
  /** An action that updates a kind 0 profile evnet for a user */
14
15
  export function UpdateProfile(content) {
15
- return async function* ({ events, factory, self }) {
16
- const metadata = events.getReplaceable(kinds.Metadata, self);
17
- if (!metadata)
18
- throw new Error("Profile does not exists");
19
- const draft = await factory.modify(metadata, Profile.updateProfile(content));
20
- yield await factory.sign(draft);
16
+ return async ({ factory, user, publish, sign }) => {
17
+ // Load the profile and outboxes in parallel
18
+ const [profile, outboxes] = await Promise.all([
19
+ user.profile$.$first(1000, undefined),
20
+ user.outboxes$.$first(1000, undefined),
21
+ ]);
22
+ if (!profile)
23
+ throw new Error("Unable to find profile metadata");
24
+ const signed = await factory.modify(profile.event, Profile.updateProfile(content)).then(sign);
25
+ await publish(signed, outboxes);
21
26
  };
22
27
  }
@@ -1,5 +1,5 @@
1
1
  import { NostrEvent } from "applesauce-core/helpers/event";
2
- import { Action } from "../action-hub.js";
2
+ import { Action } from "../action-runner.js";
3
3
  /** An action that adds a relay to a relay set*/
4
4
  export declare function AddRelayToRelaySet(relay: string | string[], identifier: NostrEvent | string, hidden?: boolean): Action;
5
5
  /** An action that removes a relay from a relay set */
@@ -1,45 +1,52 @@
1
1
  import * as List from "applesauce-common/operations/list";
2
- import { kinds } from "applesauce-core/helpers/event";
2
+ import { getReplaceableIdentifier, kinds } from "applesauce-core/helpers/event";
3
3
  import { modifyHiddenTags, modifyPublicTags } from "applesauce-core/operations";
4
4
  import { addRelayTag, removeRelayTag } from "applesauce-core/operations/tag/relay";
5
- function getRelaySetEvent(events, self, identifier) {
6
- const set = typeof identifier === "string" ? events.getReplaceable(kinds.Relaysets, self, identifier) : identifier;
7
- if (!set)
8
- throw new Error("Can't find relay set");
9
- if (set.kind !== kinds.Relaysets)
10
- throw new Error("Event is not a relay set");
11
- return set;
5
+ function ModifyRelaySetEvent(operations, set, hidden = false) {
6
+ const identifier = typeof set === "string" ? set : getReplaceableIdentifier(set);
7
+ return async ({ factory, user, publish, sign }) => {
8
+ const [event, outboxes] = await Promise.all([
9
+ user.replaceable(kinds.Relaysets, identifier).$first(1000, undefined),
10
+ user.outboxes$.$first(1000, undefined),
11
+ ]);
12
+ const operation = hidden ? modifyHiddenTags(...operations) : modifyPublicTags(...operations);
13
+ // Modify or build new event
14
+ const signed = event
15
+ ? await factory.modify(event, operation).then(sign)
16
+ : await factory.build({ kind: kinds.Relaysets }, operation).then(sign);
17
+ // Publish the event to the user's outboxes
18
+ await publish(signed, outboxes);
19
+ };
12
20
  }
13
21
  /** An action that adds a relay to a relay set*/
14
- export function AddRelayToRelaySet(relay, identifier, hidden = false) {
15
- return async function* ({ events, factory, self }) {
16
- const relays = getRelaySetEvent(events, self, identifier);
17
- const operations = Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : addRelayTag(relay);
18
- const draft = await factory.modifyTags(relays, hidden ? { hidden: operations } : operations);
19
- yield await factory.sign(draft);
20
- };
22
+ export function AddRelayToRelaySet(relay, identifier, hidden) {
23
+ return ModifyRelaySetEvent(Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : [addRelayTag(relay)], identifier, hidden);
21
24
  }
22
25
  /** An action that removes a relay from a relay set */
23
- export function RemoveRelayFromRelaySet(relay, identifier, hidden = false) {
24
- return async function* ({ events, factory, self }) {
25
- const relays = getRelaySetEvent(events, self, identifier);
26
- const operations = Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : removeRelayTag(relay);
27
- const draft = await factory.modifyTags(relays, hidden ? { hidden: operations } : operations);
28
- yield await factory.sign(draft);
29
- };
26
+ export function RemoveRelayFromRelaySet(relay, identifier, hidden) {
27
+ return ModifyRelaySetEvent(Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : [removeRelayTag(relay)], identifier, hidden);
30
28
  }
31
29
  /** An action that creates a new relay set */
32
30
  export function CreateRelaySet(title, options) {
33
- return async function* ({ factory }) {
34
- const draft = await factory.build({ kind: kinds.Relaysets }, List.setTitle(title), options?.description ? List.setDescription(options.description) : undefined, options?.image ? List.setImage(options.image) : undefined, options?.public ? modifyPublicTags(...options.public.map((r) => addRelayTag(r))) : undefined, options?.hidden ? modifyHiddenTags(...options.hidden.map((r) => addRelayTag(r))) : undefined);
35
- yield await factory.sign(draft);
31
+ return async ({ factory, user, publish, sign }) => {
32
+ const signed = await factory
33
+ .build({ kind: kinds.Relaysets }, List.setTitle(title), options?.description ? List.setDescription(options.description) : undefined, options?.image ? List.setImage(options.image) : undefined, options?.public ? modifyPublicTags(...options.public.map((r) => addRelayTag(r))) : undefined, options?.hidden ? modifyHiddenTags(...options.hidden.map((r) => addRelayTag(r))) : undefined)
34
+ .then(sign);
35
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
36
36
  };
37
37
  }
38
38
  /** An action that updates the title, description, or image of a relay set */
39
39
  export function UpdateRelaySetInformation(identifier, info) {
40
- return async function* ({ events, factory, self }) {
41
- const relays = getRelaySetEvent(events, self, identifier);
42
- const draft = await factory.modify(relays, info?.title ? List.setTitle(info.title) : undefined, info?.description ? List.setDescription(info.description) : undefined, info?.image ? List.setImage(info.image) : undefined);
43
- yield await factory.sign(draft);
40
+ return async ({ factory, sign, user, publish }) => {
41
+ const [event, outboxes] = await Promise.all([
42
+ user.replaceable(kinds.Relaysets, identifier).$first(1000, undefined),
43
+ user.outboxes$.$first(1000, undefined),
44
+ ]);
45
+ if (!event)
46
+ throw new Error("Relay set not found");
47
+ const signed = await factory
48
+ .modify(event, info?.title ? List.setTitle(info.title) : undefined, info?.description ? List.setDescription(info.description) : undefined, info?.image ? List.setImage(info.image) : undefined)
49
+ .then(sign);
50
+ await publish(signed, outboxes);
44
51
  };
45
52
  }
@@ -1,4 +1,4 @@
1
- import { Action } from "../action-hub.js";
1
+ import { Action } from "../action-runner.js";
2
2
  /** An action that adds a relay to the 10007 search relays event */
3
3
  export declare function AddSearchRelay(relay: string | string[], hidden?: boolean): Action;
4
4
  /** An action that removes a relay from the 10007 search relays event */
@@ -1,33 +1,32 @@
1
1
  import { kinds } from "applesauce-core/helpers/event";
2
2
  import { modifyHiddenTags, modifyPublicTags } from "applesauce-core/operations";
3
3
  import { addRelayTag, removeRelayTag } from "applesauce-core/operations/tag/relay";
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;
4
+ function ModifySearchRelaysEvent(operations, hidden = false) {
5
+ return async ({ factory, user, publish, sign }) => {
6
+ const [event, outboxes] = await Promise.all([
7
+ user.replaceable(kinds.SearchRelaysList).$first(1000, undefined),
8
+ user.outboxes$.$first(1000, undefined),
9
+ ]);
10
+ const operation = hidden ? modifyHiddenTags(...operations) : modifyPublicTags(...operations);
11
+ // Modify or build new event
12
+ const signed = event
13
+ ? await factory.modify(event, operation).then(sign)
14
+ : await factory.build({ kind: kinds.SearchRelaysList }, operation).then(sign);
15
+ // Publish the event to the user's outboxes
16
+ await publish(signed, outboxes);
17
+ };
9
18
  }
10
19
  /** An action that adds a relay to the 10007 search relays event */
11
20
  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
- };
21
+ return ModifySearchRelaysEvent(Array.isArray(relay) ? relay.map((r) => addRelayTag(r)) : [addRelayTag(relay)], hidden);
18
22
  }
19
23
  /** An action that removes a relay from the 10007 search relays event */
20
24
  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
- };
25
+ return ModifySearchRelaysEvent(Array.isArray(relay) ? relay.map((r) => removeRelayTag(r)) : [removeRelayTag(relay)], hidden);
27
26
  }
28
27
  /** Creates a new search relays event */
29
28
  export function NewSearchRelays(relays) {
30
- return async function* ({ events, factory, self }) {
29
+ return async ({ events, factory, self, user, publish }) => {
31
30
  const search = events.getReplaceable(kinds.SearchRelaysList, self);
32
31
  if (search)
33
32
  throw new Error("Search relays event already exists");
@@ -43,6 +42,7 @@ export function NewSearchRelays(relays) {
43
42
  hiddenOperations.push(...(relays?.hidden ?? []).map((r) => addRelayTag(r)));
44
43
  }
45
44
  const draft = await factory.build({ kind: kinds.SearchRelaysList }, publicOperations.length ? modifyPublicTags(...publicOperations) : undefined, hiddenOperations.length ? modifyHiddenTags(...hiddenOperations) : undefined);
46
- yield await factory.sign(draft);
45
+ const signed = await factory.sign(draft);
46
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
47
47
  };
48
48
  }
@@ -1,7 +1,9 @@
1
- import { Rumor } from "applesauce-common/helpers/gift-wrap";
2
1
  import { WrappedMessageBlueprintOptions } from "applesauce-common/blueprints";
2
+ import { Rumor } from "applesauce-common/helpers/gift-wrap";
3
3
  import { GiftWrapOptions } from "applesauce-common/operations/gift-wrap";
4
- import { Action } from "../action-hub.js";
4
+ import { Action } from "../action-runner.js";
5
+ /** Gift wraps a message to a list of participants and publishes it to their inbox relays */
6
+ export declare function GiftWrapMessageToParticipants(message: Rumor, opts?: GiftWrapOptions): Action;
5
7
  /**
6
8
  * Sends a NIP-17 wrapped message to a conversation
7
9
  * @param participants - A conversation identifier, user pubkey, or a list of participant pubkeys
@@ -1,5 +1,34 @@
1
- import { getConversationParticipants } from "applesauce-common/helpers/messages";
2
1
  import { GiftWrapBlueprint, WrappedMessageBlueprint, WrappedMessageReplyBlueprint, } from "applesauce-common/blueprints";
2
+ import { castUser } from "applesauce-common/casts";
3
+ import { getConversationParticipants } from "applesauce-common/helpers/messages";
4
+ /** Gift wraps a message to a list of participants and publishes it to their inbox relays */
5
+ export function GiftWrapMessageToParticipants(message, opts) {
6
+ return async ({ factory, user, publish, events }) => {
7
+ // Get the pubkeys to send this message to and ensure the sender is included
8
+ const pubkeys = new Set(getConversationParticipants(message));
9
+ pubkeys.add(user.pubkey);
10
+ // Get all the users inbox relays
11
+ const inboxRelays = new Map();
12
+ await Promise.allSettled(Array.from(pubkeys).map(async (pubkey) => {
13
+ const receiver = castUser(pubkey, events);
14
+ // Use the dm relays or inboxes as the inbox relays for the participant
15
+ const relays = (await receiver.directMessageRelays$.$first(1_000, undefined)) ??
16
+ (await receiver.inboxes$.$first(1_000, undefined));
17
+ if (relays)
18
+ inboxRelays.set(pubkey, relays);
19
+ }));
20
+ // Create the gift wraps to send
21
+ const giftWraps = [];
22
+ for (const pubkey of pubkeys) {
23
+ giftWraps.push(await factory.create(GiftWrapBlueprint, pubkey, message, opts));
24
+ }
25
+ // Publish all gift wraps in parallel
26
+ await Promise.allSettled(giftWraps.map(async (giftWrap) => {
27
+ const relays = inboxRelays.get(giftWrap.pubkey);
28
+ await publish(giftWrap, relays);
29
+ }));
30
+ };
31
+ }
3
32
  /**
4
33
  * Sends a NIP-17 wrapped message to a conversation
5
34
  * @param participants - A conversation identifier, user pubkey, or a list of participant pubkeys
@@ -8,14 +37,10 @@ import { GiftWrapBlueprint, WrappedMessageBlueprint, WrappedMessageReplyBlueprin
8
37
  * @returns Signed gift wrapped messages to send
9
38
  */
10
39
  export function SendWrappedMessage(participants, message, opts) {
11
- return async function* ({ factory, self }) {
40
+ return async ({ factory, run }) => {
41
+ // Create the rumor of the message
12
42
  const rumor = await factory.create(WrappedMessageBlueprint, participants, message, opts);
13
- // Get the pubkeys to send this message to and ensure the sender is included
14
- const pubkeys = new Set(getConversationParticipants(rumor));
15
- pubkeys.add(self);
16
- for (const pubkey of pubkeys) {
17
- yield await factory.create(GiftWrapBlueprint, pubkey, rumor, opts);
18
- }
43
+ await run(GiftWrapMessageToParticipants, rumor, opts);
19
44
  };
20
45
  }
21
46
  /**
@@ -26,13 +51,9 @@ export function SendWrappedMessage(participants, message, opts) {
26
51
  * @returns Signed gift wrapped messages to send
27
52
  */
28
53
  export function ReplyToWrappedMessage(parent, message, opts) {
29
- return async function* ({ factory }) {
54
+ return async ({ factory, run }) => {
30
55
  // Create the reply message
31
56
  const rumor = await factory.create(WrappedMessageReplyBlueprint, parent, message, opts);
32
- // Get the pubkeys to send this message to (will include the sender)
33
- const pubkeys = getConversationParticipants(parent);
34
- for (const pubkey of pubkeys) {
35
- yield await factory.create(GiftWrapBlueprint, pubkey, rumor, opts);
36
- }
57
+ await run(GiftWrapMessageToParticipants, rumor, opts);
37
58
  };
38
59
  }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./action-hub.js";
1
+ export * from "./action-runner.js";
2
2
  export * as Actions from "./actions/index.js";
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./action-hub.js";
1
+ export * from "./action-runner.js";
2
2
  export * as Actions from "./actions/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-actions",
3
- "version": "0.0.0-next-20251209200210",
3
+ "version": "0.0.0-next-20251231055351",
4
4
  "description": "A package for performing common nostr actions",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,14 +32,14 @@
32
32
  }
33
33
  },
34
34
  "dependencies": {
35
- "applesauce-common": "0.0.0-next-20251209200210",
36
- "applesauce-core": "0.0.0-next-20251209200210",
35
+ "applesauce-common": "0.0.0-next-20251231055351",
36
+ "applesauce-core": "0.0.0-next-20251231055351",
37
37
  "rxjs": "^7.8.1"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@hirez_io/observer-spy": "^2.2.0",
41
41
  "@types/debug": "^4.1.12",
42
- "applesauce-signers": "0.0.0-next-20251209200210",
42
+ "applesauce-signers": "0.0.0-next-20251231055351",
43
43
  "nanoid": "^5.1.5",
44
44
  "rimraf": "^6.0.1",
45
45
  "typescript": "^5.8.3",
@@ -1,35 +0,0 @@
1
- import { Observable } from "rxjs";
2
- import { NostrEvent } from "applesauce-core/helpers/event";
3
- import { EventFactory } from "applesauce-core";
4
- import { IEventStoreActions, IEventStoreRead } from "applesauce-core";
5
- /** A callback used to tell the upstream app to publish an event */
6
- export type PublishMethod = (event: NostrEvent) => void | Promise<void>;
7
- /** The context that is passed to actions for them to use to preform actions */
8
- export type ActionContext = {
9
- /** The event store to load events from */
10
- events: IEventStoreRead;
11
- /** The pubkey of the signer in the event factory */
12
- self: string;
13
- /** The event factory used to build and modify events */
14
- factory: EventFactory;
15
- };
16
- /** An action that can be run in a context to preform an action */
17
- export type Action = (ctx: ActionContext) => Observable<NostrEvent> | AsyncGenerator<NostrEvent> | Generator<NostrEvent>;
18
- export type ActionConstructor<Args extends Array<any>> = (...args: Args) => Action;
19
- /** The main class that runs actions */
20
- export declare class ActionHub {
21
- events: IEventStoreRead & IEventStoreActions;
22
- factory: EventFactory;
23
- publish?: PublishMethod | undefined;
24
- /** Whether to save all events created by actions to the event store */
25
- saveToStore: boolean;
26
- constructor(events: IEventStoreRead & IEventStoreActions, factory: EventFactory, publish?: PublishMethod | undefined);
27
- protected context: ActionContext | undefined;
28
- protected getContext(): Promise<ActionContext>;
29
- /** Runs an action in a ActionContext and converts the result to an Observable */
30
- static runAction(ctx: ActionContext, action: Action): Observable<NostrEvent>;
31
- /** Run an action and publish events using the publish method */
32
- run<Args extends Array<any>>(Action: ActionConstructor<Args>, ...args: Args): Promise<void>;
33
- /** Run an action without publishing the events */
34
- exec<Args extends Array<any>>(Action: ActionConstructor<Args>, ...args: Args): Observable<NostrEvent>;
35
- }
@@ -1,51 +0,0 @@
1
- import { from, isObservable, lastValueFrom, switchMap, tap, toArray } from "rxjs";
2
- /** The main class that runs actions */
3
- export class ActionHub {
4
- events;
5
- factory;
6
- publish;
7
- /** Whether to save all events created by actions to the event store */
8
- saveToStore = true;
9
- constructor(events, factory, publish) {
10
- this.events = events;
11
- this.factory = factory;
12
- this.publish = publish;
13
- }
14
- context = undefined;
15
- async getContext() {
16
- if (this.context)
17
- return this.context;
18
- else {
19
- if (!this.factory.context.signer)
20
- throw new Error("Missing signer");
21
- const self = await this.factory.context.signer.getPublicKey();
22
- this.context = { self, events: this.events, factory: this.factory };
23
- return this.context;
24
- }
25
- }
26
- /** Runs an action in a ActionContext and converts the result to an Observable */
27
- static runAction(ctx, action) {
28
- const result = action(ctx);
29
- if (isObservable(result))
30
- return result;
31
- else
32
- return from(result);
33
- }
34
- /** Run an action and publish events using the publish method */
35
- async run(Action, ...args) {
36
- if (!this.publish)
37
- throw new Error("Missing publish method, use ActionHub.exec");
38
- // wait for action to complete and group events
39
- const events = await lastValueFrom(this.exec(Action, ...args).pipe(toArray()));
40
- // publish events
41
- for (const event of events)
42
- await this.publish(event);
43
- }
44
- /** Run an action without publishing the events */
45
- exec(Action, ...args) {
46
- return from(this.getContext()).pipe(switchMap((ctx) => {
47
- const action = Action(...args);
48
- return ActionHub.runAction(ctx, action);
49
- }), tap((event) => this.saveToStore && this.events.add(event)));
50
- }
51
- }
@@ -1,7 +0,0 @@
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;