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
package/README.md CHANGED
@@ -18,12 +18,12 @@ Actions are common pre-built async operations that apps can perform. They use:
18
18
  - `EventFactory` to build and sign new nostr events
19
19
  - A `publish` method to publish or save the resulting events
20
20
 
21
- The package provides an `ActionHub` class that combines these components into a single manager for easier action execution.
21
+ The package provides an `ActionRunner` class that combines these components into a single manager for easier action execution.
22
22
 
23
23
  ## Basic Usage
24
24
 
25
25
  ```typescript
26
- import { ActionHub } from "applesauce-actions";
26
+ import { ActionRunner } from "applesauce-actions";
27
27
  import { FollowUser } from "applesauce-actions/actions";
28
28
 
29
29
  async function publishEvent(event: NostrEvent) {
@@ -31,7 +31,7 @@ async function publishEvent(event: NostrEvent) {
31
31
  }
32
32
 
33
33
  // Create an action hub with your event store, factory and publish method
34
- const hub = new ActionHub(eventStore, eventFactory, publishEvent);
34
+ const hub = new ActionRunner(eventStore, eventFactory, publishEvent);
35
35
 
36
36
  // Example: Follow a user
37
37
  await hub
@@ -0,0 +1,53 @@
1
+ import { User } from "applesauce-common/casts/user";
2
+ import { EventFactory, EventSigner } from "applesauce-core/event-factory";
3
+ import { EventModels, IEventStoreActions, IEventStoreRead, IEventStoreStreams, IEventSubscriptions } from "applesauce-core/event-store";
4
+ import { EventTemplate, NostrEvent, UnsignedEvent } from "applesauce-core/helpers/event";
5
+ import { Observable } from "rxjs";
6
+ /** A callback used to tell the upstream app to publish an event */
7
+ export type PublishMethod = (event: NostrEvent, relays?: string[]) => void | Promise<any> | Observable<any>;
8
+ type UpstreamPool = PublishMethod | {
9
+ publish: PublishMethod;
10
+ };
11
+ /** The context that is passed to actions for them to use to preform actions */
12
+ export type ActionContext = {
13
+ /** The event store to load events from */
14
+ events: IEventStoreRead & IEventStoreStreams & IEventSubscriptions & EventModels;
15
+ /** The pubkey of the signer in the event factory */
16
+ self: string;
17
+ /** The {@link User} cast that is signing the events */
18
+ user: User;
19
+ /** The event signer used to sign events */
20
+ signer?: EventSigner;
21
+ /** The event factory used to build and modify events */
22
+ factory: EventFactory;
23
+ /** Sign an event using the event factory */
24
+ sign: (draft: EventTemplate | UnsignedEvent) => Promise<NostrEvent>;
25
+ /** The method to publish events to an optional list of relays */
26
+ publish: (event: NostrEvent | NostrEvent[], relays?: string[]) => Promise<void>;
27
+ /** Run a sub-action within the current action context and return the events */
28
+ run: <Args extends Array<any>>(builder: ActionBuilder<Args>, ...args: Args) => Promise<void>;
29
+ };
30
+ /** An action that can be run in a context to preform an action */
31
+ export type Action = (context: ActionContext) => Promise<void>;
32
+ /** A function that takes arguments and returns an action */
33
+ export type ActionBuilder<Args extends Array<any>> = (...args: Args) => Action;
34
+ /** The main class that runs actions */
35
+ export declare class ActionRunner {
36
+ events: IEventStoreRead & IEventStoreStreams & IEventSubscriptions & IEventStoreActions & EventModels;
37
+ factory: EventFactory;
38
+ private publishMethod?;
39
+ /** Whether to save all events created by actions to the event store */
40
+ saveToStore: boolean;
41
+ constructor(events: IEventStoreRead & IEventStoreStreams & IEventSubscriptions & IEventStoreActions & EventModels, factory: EventFactory, publishMethod?: UpstreamPool | undefined);
42
+ protected context: ActionContext | undefined;
43
+ protected getContext(): Promise<ActionContext>;
44
+ /** Internal method for publishing events to relays */
45
+ publish(event: NostrEvent | NostrEvent[], relays?: string[]): Promise<void>;
46
+ /** Run an action and publish events using the publish method */
47
+ run<Args extends Array<any>>(builder: ActionBuilder<Args>, ...args: Args): Promise<void>;
48
+ /** Run an action without publishing the events */
49
+ exec<Args extends Array<any>>(builder: ActionBuilder<Args>, ...args: Args): Observable<NostrEvent>;
50
+ }
51
+ /** @deprecated Use ActionRunner instead */
52
+ export declare const ActionHub: typeof ActionRunner;
53
+ export {};
@@ -0,0 +1,91 @@
1
+ import { castUser } from "applesauce-common/casts/user";
2
+ import { from, identity, isObservable, lastValueFrom, Observable, switchMap, tap } from "rxjs";
3
+ /** The main class that runs actions */
4
+ export class ActionRunner {
5
+ events;
6
+ factory;
7
+ publishMethod;
8
+ /** Whether to save all events created by actions to the event store */
9
+ saveToStore = true;
10
+ constructor(events, factory, publishMethod) {
11
+ this.events = events;
12
+ this.factory = factory;
13
+ this.publishMethod = publishMethod;
14
+ }
15
+ context = undefined;
16
+ async getContext() {
17
+ if (this.context)
18
+ return this.context;
19
+ else {
20
+ if (!this.factory.context.signer)
21
+ throw new Error("Missing signer");
22
+ const self = await this.factory.context.signer.getPublicKey();
23
+ const user = castUser(self, this.events);
24
+ this.context = {
25
+ self,
26
+ user,
27
+ events: this.events,
28
+ signer: this.factory.context.signer,
29
+ factory: this.factory,
30
+ publish: this.publish.bind(this),
31
+ run: this.run.bind(this),
32
+ sign: this.factory.sign.bind(this.factory),
33
+ };
34
+ return this.context;
35
+ }
36
+ }
37
+ /** Internal method for publishing events to relays */
38
+ async publish(event, relays) {
39
+ if (!this.publishMethod)
40
+ throw new Error("Missing publish method, use ActionRunner.exec");
41
+ // Unwrap array of events to publish
42
+ if (Array.isArray(event)) {
43
+ await Promise.all(event.map((e) => this.publish(e, relays)));
44
+ return;
45
+ }
46
+ if (this.publishMethod) {
47
+ let result;
48
+ if ("publish" in this.publishMethod)
49
+ result = this.publishMethod.publish(event, relays);
50
+ else if (typeof this.publishMethod === "function")
51
+ result = this.publishMethod(event, relays);
52
+ else
53
+ throw new Error("Invalid publish method");
54
+ if (isObservable(result)) {
55
+ await lastValueFrom(result);
56
+ }
57
+ else if (result instanceof Promise) {
58
+ await result;
59
+ }
60
+ // Optionally save the event to the store
61
+ if (this.saveToStore)
62
+ this.events.add(event);
63
+ }
64
+ }
65
+ /** Run an action and publish events using the publish method */
66
+ async run(builder, ...args) {
67
+ // wait for action to complete and group events
68
+ const context = await this.getContext();
69
+ await builder(...args)(context);
70
+ }
71
+ /** Run an action without publishing the events */
72
+ exec(builder, ...args) {
73
+ return from(this.getContext()).pipe(
74
+ // Run the action
75
+ switchMap((ctx) => {
76
+ return new Observable((subscriber) => {
77
+ const context = {
78
+ ...ctx,
79
+ publish: async (event) => Array.isArray(event) ? event.forEach((e) => subscriber.next(e)) : subscriber.next(event),
80
+ run: (builder, ...args) => builder(...args)(context),
81
+ };
82
+ builder(...args)(context).then(() => subscriber.complete(), (err) => subscriber.error(err));
83
+ });
84
+ }),
85
+ // NOTE: its necessary to add a tap() here because we are overwriting the publish method above
86
+ // Optionally save all events to the store
87
+ this.saveToStore ? tap((event) => this.events.add(event)) : identity);
88
+ }
89
+ }
90
+ /** @deprecated Use ActionRunner instead */
91
+ export const ActionHub = ActionRunner;
@@ -1,2 +1,2 @@
1
- import { Action } from "../action-hub.js";
1
+ import { Action } from "../action-runner.js";
2
2
  export declare function UpdateAppData<T>(identifier: string, data: T): Action;
@@ -2,12 +2,12 @@ import { AppDataBlueprint } from "applesauce-common/blueprints/app-data";
2
2
  import { APP_DATA_KIND, getAppDataEncryption } from "applesauce-common/helpers/app-data";
3
3
  import * as AppData from "applesauce-common/operations/app-data";
4
4
  export function UpdateAppData(identifier, data) {
5
- return async function* ({ self, factory, events }) {
5
+ return async ({ self, factory, events, user, publish, sign }) => {
6
6
  const event = events.getReplaceable(APP_DATA_KIND, self, identifier);
7
7
  const encryption = !!event && getAppDataEncryption(event);
8
- const draft = event
9
- ? await factory.modify(event, AppData.setContent(data, encryption))
10
- : await factory.create(AppDataBlueprint, identifier, data, encryption);
11
- yield await factory.sign(draft);
8
+ const signed = event
9
+ ? await factory.modify(event, AppData.setContent(data, encryption)).then(sign)
10
+ : await factory.create(AppDataBlueprint, identifier, data, encryption).then(sign);
11
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
12
12
  };
13
13
  }
@@ -1,10 +1,5 @@
1
- import { Action } from "../action-hub.js";
1
+ import { Action } from "../action-runner.js";
2
2
  /** An action that adds a relay to the 10006 blocked relays event */
3
3
  export declare function AddBlockedRelay(relay: string | string[], hidden?: boolean): Action;
4
4
  /** An action that removes a relay from the 10006 blocked relays event */
5
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;
@@ -1,48 +1,42 @@
1
+ import { firstValueFrom } from "applesauce-core";
1
2
  import { kinds } from "applesauce-core/helpers/event";
2
3
  import { modifyHiddenTags, modifyPublicTags } from "applesauce-core/operations";
3
4
  import { addRelayTag, removeRelayTag } from "applesauce-core/operations/tag/relay";
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;
5
+ import { of, timeout } from "rxjs";
6
+ // Action to generally modify the blocked relays event
7
+ function ModifyBlockedRelaysEvent(operations) {
8
+ return async ({ events, factory, user, publish, sign }) => {
9
+ const [event, outboxes] = await Promise.all([
10
+ firstValueFrom(events
11
+ .replaceable(kinds.BlockedRelaysList, user.pubkey)
12
+ .pipe(timeout({ first: 1000, with: () => of(undefined) }))),
13
+ user.outboxes$.$first(1000, undefined),
14
+ ]);
15
+ // Modify or build new event
16
+ const signed = event
17
+ ? await factory.modify(event, ...operations).then(sign)
18
+ : await factory.build({ kind: kinds.BlockedRelaysList }, ...operations).then(sign);
19
+ // Publish the event to the user's outboxes
20
+ await publish(signed, outboxes);
21
+ };
9
22
  }
10
23
  /** An action that adds a relay to the 10006 blocked relays event */
11
24
  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);
25
+ return async ({ run }) => {
26
+ const tagOperations = Array.isArray(relay)
27
+ ? relay.map((r) => addRelayTag(r))
28
+ : [addRelayTag(relay)];
29
+ const operation = hidden ? modifyHiddenTags(...tagOperations) : modifyPublicTags(...tagOperations);
30
+ await run(ModifyBlockedRelaysEvent, [operation]);
17
31
  };
18
32
  }
19
33
  /** An action that removes a relay from the 10006 blocked relays event */
20
34
  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);
35
+ return async ({ run }) => {
36
+ const tagOperations = Array.isArray(relay)
37
+ ? relay.map((r) => removeRelayTag(r))
38
+ : [removeRelayTag(relay)];
39
+ const operation = hidden ? modifyHiddenTags(...tagOperations) : modifyPublicTags(...tagOperations);
40
+ await run(ModifyBlockedRelaysEvent, [operation]);
47
41
  };
48
42
  }
@@ -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 server to the Blossom servers event */
3
3
  export declare function AddBlossomServer(server: string | URL | (string | URL)[]): Action;
4
4
  /** An action that removes a server from the Blossom servers event */
@@ -1,51 +1,44 @@
1
1
  import { BLOSSOM_SERVER_LIST_KIND } from "applesauce-common/helpers/blossom";
2
2
  import { addBlossomServer, removeBlossomServer } from "applesauce-common/operations/blossom";
3
- /** An action that adds a server to the Blossom servers event */
4
- export function AddBlossomServer(server) {
5
- return async function* ({ events, factory, self }) {
6
- const servers = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
7
- const operations = Array.isArray(server) ? server.map((s) => addBlossomServer(s)) : [addBlossomServer(server)];
3
+ // Action to modify or create a new Blossom servers event
4
+ function ModifyBlossomServersEvent(operations) {
5
+ return async ({ factory, user, publish, sign }) => {
6
+ const [event, outboxes] = await Promise.all([
7
+ user.replaceable(BLOSSOM_SERVER_LIST_KIND).$first(1000, undefined),
8
+ user.outboxes$.$first(1000, undefined),
9
+ ]);
8
10
  // Modify or build new event
9
- const draft = servers
10
- ? await factory.modify(servers, ...operations)
11
- : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
12
- yield await factory.sign(draft);
11
+ const signed = event
12
+ ? await factory.modify(event, ...operations).then(sign)
13
+ : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations).then(sign);
14
+ // Publish the event to the user's outboxes
15
+ await publish(signed, outboxes);
13
16
  };
14
17
  }
18
+ /** An action that adds a server to the Blossom servers event */
19
+ export function AddBlossomServer(server) {
20
+ return ModifyBlossomServersEvent(Array.isArray(server) ? server.map((s) => addBlossomServer(s)) : [addBlossomServer(server)]);
21
+ }
15
22
  /** An action that removes a server from the Blossom servers event */
16
23
  export function RemoveBlossomServer(server) {
17
- return async function* ({ events, factory, self }) {
18
- const servers = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
19
- const operations = Array.isArray(server)
20
- ? server.map((s) => removeBlossomServer(s))
21
- : [removeBlossomServer(server)];
22
- // Modify or build new event
23
- const draft = servers
24
- ? await factory.modify(servers, ...operations)
25
- : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
26
- yield await factory.sign(draft);
27
- };
24
+ return ModifyBlossomServersEvent(Array.isArray(server) ? server.map((s) => removeBlossomServer(s)) : [removeBlossomServer(server)]);
25
+ }
26
+ // Small event operation to prepend a tag to the events tags
27
+ function prependTag(tag) {
28
+ return (draft) => ({ ...draft, tags: [tag, ...draft.tags] });
28
29
  }
29
30
  /** Makes a specific Blossom server the default server (move it to the top of the list) */
30
31
  export function SetDefaultBlossomServer(server) {
31
- return async function* ({ events, factory, self }) {
32
- const servers = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
33
- const prependTag = (tag) => (draft) => ({ ...draft, tags: [tag, ...draft.tags] });
34
- const operations = [removeBlossomServer(server), prependTag(["server", String(server)])];
35
- const draft = servers
36
- ? await factory.modify(servers, ...operations)
37
- : await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
38
- yield await factory.sign(draft);
39
- };
32
+ return ModifyBlossomServersEvent([removeBlossomServer(server), prependTag(["server", String(server)])]);
40
33
  }
41
34
  /** Creates a new Blossom servers event */
42
35
  export function NewBlossomServers(servers) {
43
- return async function* ({ events, factory, self }) {
36
+ return async ({ events, factory, self, user, publish, sign }) => {
44
37
  const existing = events.getReplaceable(BLOSSOM_SERVER_LIST_KIND, self);
45
38
  if (existing)
46
39
  throw new Error("Blossom servers event already exists");
47
40
  const operations = servers ? servers.map((s) => addBlossomServer(s)) : [];
48
- const draft = await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations);
49
- yield await factory.sign(draft);
41
+ const signed = await factory.build({ kind: BLOSSOM_SERVER_LIST_KIND }, ...operations).then(sign);
42
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
50
43
  };
51
44
  }
@@ -1,19 +1,20 @@
1
+ import { AddressPointer, EventPointer } from "applesauce-core/helpers";
1
2
  import { NostrEvent } from "applesauce-core/helpers/event";
2
- import { Action } from "../action-hub.js";
3
+ import { Action } from "../action-runner.js";
3
4
  /**
4
5
  * An action that adds a note or article to the bookmark list or a bookmark set
5
6
  * @param event the event to bookmark
6
7
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
7
8
  * @param hidden set to true to add to hidden bookmarks
8
9
  */
9
- export declare function BookmarkEvent(event: NostrEvent, identifier?: string | NostrEvent, hidden?: boolean): Action;
10
+ export declare function BookmarkEvent(event: NostrEvent | EventPointer | AddressPointer, identifier?: string | NostrEvent, hidden?: boolean): Action;
10
11
  /**
11
12
  * An action that removes a note or article from the bookmark list or bookmark set
12
13
  * @param event the event to remove from bookmarks
13
14
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
14
15
  * @param hidden set to true to remove from hidden bookmarks
15
16
  */
16
- export declare function UnbookmarkEvent(event: NostrEvent, identifier?: string | NostrEvent, hidden?: boolean): Action;
17
+ export declare function UnbookmarkEvent(event: NostrEvent | EventPointer | AddressPointer, identifier?: string | NostrEvent, hidden?: boolean): Action;
17
18
  /** An action that creates a new bookmark list for a user */
18
19
  export declare function CreateBookmarkList(bookmarks?: NostrEvent[]): Action;
19
20
  /** An action that creates a new bookmark set for a user */
@@ -1,40 +1,55 @@
1
1
  import * as List from "applesauce-common/operations/list";
2
2
  import { addEventBookmarkTag, removeEventBookmarkTag } from "applesauce-common/operations/tag/bookmarks";
3
- import { kinds } from "applesauce-core/helpers/event";
3
+ import { getReplaceableIdentifier, kinds } from "applesauce-core/helpers/event";
4
4
  import { modifyHiddenTags, modifyPublicTags } from "applesauce-core/operations";
5
+ function ModifyBookmarkSetEvent(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.Bookmarksets, 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.Bookmarksets }, operation).then(sign);
17
+ // Publish the event to the user's outboxes
18
+ await publish(signed, outboxes);
19
+ };
20
+ }
21
+ function ModifyBookmarkListEvent(operations, hidden = false) {
22
+ return async ({ factory, user, publish, sign }) => {
23
+ const [event, outboxes] = await Promise.all([
24
+ user.replaceable(kinds.BookmarkList).$first(1000, undefined),
25
+ user.outboxes$.$first(1000, undefined),
26
+ ]);
27
+ const operation = hidden ? modifyHiddenTags(...operations) : modifyPublicTags(...operations);
28
+ // Modify or build new event
29
+ const signed = event
30
+ ? await factory.modify(event, operation).then(sign)
31
+ : await factory.build({ kind: kinds.BookmarkList }, operation).then(sign);
32
+ // Publish the event to the user's outboxes
33
+ await publish(signed, outboxes);
34
+ };
35
+ }
5
36
  /**
6
37
  * An action that adds a note or article to the bookmark list or a bookmark set
7
38
  * @param event the event to bookmark
8
39
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
9
40
  * @param hidden set to true to add to hidden bookmarks
10
41
  */
11
- export function BookmarkEvent(event, identifier, hidden = false) {
12
- return async function* ({ events, factory, self }) {
13
- let draft;
14
- const operation = addEventBookmarkTag(event);
15
- if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
16
- const list = typeof identifier === "string" ? events.getReplaceable(kinds.Bookmarksets, self, identifier) : identifier;
17
- if (list && list.kind !== kinds.Bookmarksets)
18
- throw new Error("Event is not a bookmark set");
19
- // Modify or build new event bookmark set
20
- draft = list
21
- ? await factory.modifyTags(list, hidden ? { hidden: operation } : operation)
22
- : await factory.build({ kind: kinds.Bookmarksets }, hidden ? modifyHiddenTags(operation) : modifyPublicTags(operation));
23
- }
24
- else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
25
- const list = identifier ? identifier : events.getReplaceable(kinds.BookmarkList, self);
26
- if (list && list.kind !== kinds.BookmarkList)
27
- throw new Error("Event is not a bookmark list");
28
- // Modify or build new event bookmark list
29
- draft = list
30
- ? await factory.modifyTags(list, hidden ? { hidden: operation } : operation)
31
- : await factory.build({ kind: kinds.BookmarkList }, hidden ? modifyHiddenTags(operation) : modifyPublicTags(operation));
32
- }
33
- else {
34
- throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
35
- }
36
- yield await factory.sign(draft);
37
- };
42
+ export function BookmarkEvent(event, identifier, hidden) {
43
+ const operation = addEventBookmarkTag(event);
44
+ if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
45
+ return ModifyBookmarkSetEvent([operation], identifier, hidden);
46
+ }
47
+ else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
48
+ return ModifyBookmarkListEvent([operation], hidden);
49
+ }
50
+ else {
51
+ throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
52
+ }
38
53
  }
39
54
  /**
40
55
  * An action that removes a note or article from the bookmark list or bookmark set
@@ -42,42 +57,36 @@ export function BookmarkEvent(event, identifier, hidden = false) {
42
57
  * @param identifier the "d" tag of the bookmark set or `undefined` for the default bookmark list
43
58
  * @param hidden set to true to remove from hidden bookmarks
44
59
  */
45
- export function UnbookmarkEvent(event, identifier, hidden = false) {
46
- return async function* ({ events, factory, self }) {
47
- let list;
48
- const operation = removeEventBookmarkTag(event);
49
- if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
50
- list = typeof identifier === "string" ? events.getReplaceable(kinds.Bookmarksets, self, identifier) : identifier;
51
- }
52
- else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
53
- list = identifier ? identifier : events.getReplaceable(kinds.BookmarkList, self);
54
- if (!list)
55
- return;
56
- }
57
- else {
58
- throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
59
- }
60
- // If no list is found, return
61
- if (!list)
62
- return;
63
- const draft = await factory.modifyTags(list, hidden ? { hidden: operation } : operation);
64
- yield await factory.sign(draft);
65
- };
60
+ export function UnbookmarkEvent(event, identifier, hidden) {
61
+ const operation = removeEventBookmarkTag(event);
62
+ if (typeof identifier === "string" || identifier?.kind === kinds.Bookmarksets) {
63
+ return ModifyBookmarkSetEvent([operation], identifier, hidden);
64
+ }
65
+ else if (identifier === undefined || identifier?.kind === kinds.BookmarkList) {
66
+ return ModifyBookmarkListEvent([operation], hidden);
67
+ }
68
+ else {
69
+ throw new Error(`Event kind ${identifier.kind} is not a bookmark list or bookmark set`);
70
+ }
66
71
  }
67
72
  /** An action that creates a new bookmark list for a user */
68
73
  export function CreateBookmarkList(bookmarks) {
69
- return async function* ({ events, factory, self }) {
74
+ return async ({ events, factory, self, user, publish, sign }) => {
70
75
  const existing = events.getReplaceable(kinds.BookmarkList, self);
71
76
  if (existing)
72
77
  throw new Error("Bookmark list already exists");
73
- const draft = await factory.build({ kind: kinds.BookmarkList }, bookmarks ? modifyPublicTags(...bookmarks.map(addEventBookmarkTag)) : undefined);
74
- yield await factory.sign(draft);
78
+ const signed = await factory
79
+ .build({ kind: kinds.BookmarkList }, bookmarks ? modifyPublicTags(...bookmarks.map(addEventBookmarkTag)) : undefined)
80
+ .then(sign);
81
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
75
82
  };
76
83
  }
77
84
  /** An action that creates a new bookmark set for a user */
78
85
  export function CreateBookmarkSet(title, description, additional) {
79
- return async function* ({ factory }) {
80
- const draft = await factory.build({ kind: kinds.BookmarkList }, List.setTitle(title), List.setDescription(description), additional.image ? List.setImage(additional.image) : undefined, additional.public ? modifyPublicTags(...additional.public.map(addEventBookmarkTag)) : undefined, additional.hidden ? modifyHiddenTags(...additional.hidden.map(addEventBookmarkTag)) : undefined);
81
- yield await factory.sign(draft);
86
+ return async ({ factory, user, publish, sign }) => {
87
+ const signed = await factory
88
+ .build({ kind: kinds.BookmarkList }, List.setTitle(title), List.setDescription(description), additional.image ? List.setImage(additional.image) : undefined, additional.public ? modifyPublicTags(...additional.public.map(addEventBookmarkTag)) : undefined, additional.hidden ? modifyHiddenTags(...additional.hidden.map(addEventBookmarkTag)) : undefined)
89
+ .then(sign);
90
+ await publish(signed, await user.outboxes$.$first(1000, undefined));
82
91
  };
83
92
  }
@@ -1,6 +1,9 @@
1
+ import { AddressPointer } from "applesauce-core/helpers";
1
2
  import { NostrEvent } from "applesauce-core/helpers/event";
2
- import { Action } from "../action-hub.js";
3
+ import { Action } from "../action-runner.js";
3
4
  /** Adds a calendar event to a calendar */
4
- export declare function AddEventToCalendar(calendar: NostrEvent, event: NostrEvent): Action;
5
+ export declare function AddEventToCalendar(calendar: NostrEvent | AddressPointer, event: NostrEvent | AddressPointer): Action;
5
6
  /** Removes a calendar event from a calendar */
6
- export declare function RemoveEventFromCalendar(calendar: NostrEvent, event: NostrEvent): Action;
7
+ export declare function RemoveEventFromCalendar(calendar: NostrEvent | AddressPointer, event: NostrEvent | AddressPointer): Action;
8
+ /** Creates a new calendar event with title and events */
9
+ export declare function CreateCalendar(title: string, events?: (NostrEvent | AddressPointer)[]): Action;
@@ -1,25 +1,39 @@
1
1
  import { DATE_BASED_CALENDAR_EVENT_KIND, TIME_BASED_CALENDAR_EVENT_KIND, } from "applesauce-common/helpers/calendar-event";
2
- import { kinds } from "applesauce-core/helpers/event";
3
2
  import * as Calendar from "applesauce-common/operations/calendar";
3
+ import { isEvent, kinds } from "applesauce-core/helpers/event";
4
+ import { firstValueFrom, of, timeout } from "rxjs";
5
+ function ModifyCalendarEvent(calendar, operations) {
6
+ return async ({ factory, user, publish, events, sign }) => {
7
+ const [event, outboxes] = await Promise.all([
8
+ isEvent(calendar)
9
+ ? Promise.resolve(calendar)
10
+ : firstValueFrom(events.replaceable(kinds.Calendar, user.pubkey).pipe(timeout({ first: 1000, with: () => of(undefined) }))),
11
+ user.outboxes$.$first(1000, undefined),
12
+ ]);
13
+ if (!event)
14
+ throw new Error("Calendar event not found");
15
+ const signed = await factory.modify(event, ...operations).then(sign);
16
+ await publish(signed, outboxes);
17
+ };
18
+ }
4
19
  /** Adds a calendar event to a calendar */
5
20
  export function AddEventToCalendar(calendar, event) {
6
- if (calendar.kind !== kinds.Calendar)
7
- throw new Error("Calendar is not a calendar event");
8
21
  if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
9
22
  throw new Error("Event is not a calendar event");
10
- return async function* ({ factory }) {
11
- const draft = await factory.modify(calendar, Calendar.addEvent(event));
12
- return await factory.sign(draft);
13
- };
23
+ return ModifyCalendarEvent(calendar, [Calendar.addEvent(event)]);
14
24
  }
15
25
  /** Removes a calendar event from a calendar */
16
26
  export function RemoveEventFromCalendar(calendar, event) {
17
- if (calendar.kind !== kinds.Calendar)
18
- throw new Error("Calendar is not a calendar event");
19
27
  if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
20
28
  throw new Error("Event is not a calendar event");
21
- return async function* ({ factory }) {
22
- const draft = await factory.modify(calendar, Calendar.removeEvent(event));
23
- return await factory.sign(draft);
29
+ return ModifyCalendarEvent(calendar, [Calendar.removeEvent(event)]);
30
+ }
31
+ /** Creates a new calendar event with title and events */
32
+ export function CreateCalendar(title, events) {
33
+ return async ({ factory, sign, publish, user }) => {
34
+ const event = await factory
35
+ .build({ kind: kinds.Calendar }, Calendar.setTitle(title), ...(events?.map((event) => Calendar.addEvent(event)) ?? []))
36
+ .then(sign);
37
+ await publish(event, await user.outboxes$.$first(1000, undefined));
24
38
  };
25
39
  }
@@ -0,0 +1,10 @@
1
+ import { CommentBlueprintOptions } from "applesauce-common/blueprints";
2
+ import { CommentPointer } from "applesauce-common/helpers/comment";
3
+ import { NostrEvent } from "applesauce-core/helpers/event";
4
+ import { Action } from "../action-runner.js";
5
+ /**
6
+ * Creates a comment on an event or CommentPointer and publishes it to:
7
+ * - The parent event's author's inboxes (if the author exists and pubkey is available)
8
+ * - The current user's outboxes
9
+ */
10
+ export declare function CreateComment(parent: NostrEvent | CommentPointer, content: string, options?: CommentBlueprintOptions): Action;