applesauce-common 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 (116) hide show
  1. package/README.md +45 -4
  2. package/dist/blueprints/__register__.d.ts +7 -0
  3. package/dist/blueprints/__register__.js +8 -0
  4. package/dist/blueprints/comment.d.ts +3 -2
  5. package/dist/blueprints/group-mangement.d.ts +25 -0
  6. package/dist/blueprints/group-mangement.js +40 -0
  7. package/dist/blueprints/index.d.ts +1 -0
  8. package/dist/blueprints/index.js +1 -0
  9. package/dist/blueprints/torrent.d.ts +7 -0
  10. package/dist/blueprints/torrent.js +5 -1
  11. package/dist/casts/article.d.ts +19 -0
  12. package/dist/casts/article.js +47 -0
  13. package/dist/casts/bookmarks.d.ts +35 -0
  14. package/dist/casts/bookmarks.js +91 -0
  15. package/dist/casts/cast.d.ts +30 -0
  16. package/dist/casts/cast.js +67 -0
  17. package/dist/casts/comment.d.ts +18 -0
  18. package/dist/casts/comment.js +54 -0
  19. package/dist/casts/groups.d.ts +19 -0
  20. package/dist/casts/groups.js +43 -0
  21. package/dist/casts/index.d.ts +18 -0
  22. package/dist/casts/index.js +18 -0
  23. package/dist/casts/mutes.d.ts +23 -0
  24. package/dist/casts/mutes.js +54 -0
  25. package/dist/casts/note.d.ts +25 -0
  26. package/dist/casts/note.js +76 -0
  27. package/dist/casts/profile.d.ts +24 -0
  28. package/dist/casts/profile.js +52 -0
  29. package/dist/casts/reaction.d.ts +17 -0
  30. package/dist/casts/reaction.js +46 -0
  31. package/dist/casts/relay-discovery.d.ts +29 -0
  32. package/dist/casts/relay-discovery.js +54 -0
  33. package/dist/casts/relay-lists.d.ts +33 -0
  34. package/dist/casts/relay-lists.js +72 -0
  35. package/dist/casts/relay-monitor.d.ts +21 -0
  36. package/dist/casts/relay-monitor.js +41 -0
  37. package/dist/casts/report.d.ts +31 -0
  38. package/dist/casts/report.js +74 -0
  39. package/dist/casts/share.d.ts +15 -0
  40. package/dist/casts/share.js +34 -0
  41. package/dist/casts/stream.d.ts +43 -0
  42. package/dist/casts/stream.js +116 -0
  43. package/dist/casts/torrent.d.ts +31 -0
  44. package/dist/casts/torrent.js +62 -0
  45. package/dist/casts/user.d.ts +40 -0
  46. package/dist/casts/user.js +181 -0
  47. package/dist/casts/zap.d.ts +17 -0
  48. package/dist/casts/zap.js +47 -0
  49. package/dist/helpers/bookmark.d.ts +18 -17
  50. package/dist/helpers/bookmark.js +36 -49
  51. package/dist/helpers/calendar-event.d.ts +7 -1
  52. package/dist/helpers/calendar-event.js +8 -10
  53. package/dist/helpers/channels.d.ts +1 -1
  54. package/dist/helpers/channels.js +5 -8
  55. package/dist/helpers/comment.d.ts +3 -1
  56. package/dist/helpers/comment.js +12 -2
  57. package/dist/helpers/encrypted-content-cache.js +23 -25
  58. package/dist/helpers/external-id.d.ts +32 -0
  59. package/dist/helpers/external-id.js +85 -0
  60. package/dist/helpers/file-metadata.d.ts +1 -4
  61. package/dist/helpers/file-metadata.js +1 -4
  62. package/dist/helpers/gift-wrap.js +11 -5
  63. package/dist/helpers/groups.d.ts +129 -7
  64. package/dist/helpers/groups.js +317 -15
  65. package/dist/helpers/index.d.ts +1 -1
  66. package/dist/helpers/index.js +1 -1
  67. package/dist/helpers/lists.d.ts +0 -1
  68. package/dist/helpers/lists.js +4 -5
  69. package/dist/helpers/mute.d.ts +14 -11
  70. package/dist/helpers/mute.js +9 -4
  71. package/dist/helpers/relay-list.d.ts +14 -0
  72. package/dist/helpers/relay-list.js +18 -0
  73. package/dist/helpers/reports.d.ts +4 -1
  74. package/dist/helpers/reports.js +14 -10
  75. package/dist/helpers/stream-chat.d.ts +4 -1
  76. package/dist/helpers/stream-chat.js +4 -1
  77. package/dist/index.d.ts +1 -0
  78. package/dist/index.js +1 -0
  79. package/dist/models/__register__.d.ts +5 -0
  80. package/dist/models/__register__.js +6 -0
  81. package/dist/models/blossom.d.ts +2 -2
  82. package/dist/models/blossom.js +1 -1
  83. package/dist/models/bookmarks.d.ts +3 -5
  84. package/dist/models/bookmarks.js +2 -10
  85. package/dist/models/channels.js +3 -9
  86. package/dist/models/comments.d.ts +3 -2
  87. package/dist/models/comments.js +19 -1
  88. package/dist/models/index.d.ts +3 -1
  89. package/dist/models/index.js +4 -1
  90. package/dist/models/mutes.d.ts +5 -5
  91. package/dist/models/{relays.js → relay-lists.js} +2 -1
  92. package/dist/models/shares.d.ts +3 -0
  93. package/dist/models/shares.js +5 -0
  94. package/dist/models/thread.js +30 -24
  95. package/dist/observable/cast-stream.d.ts +8 -0
  96. package/dist/observable/cast-stream.js +29 -0
  97. package/dist/observable/chainable.d.ts +50 -0
  98. package/dist/observable/chainable.js +79 -0
  99. package/dist/observable/index.d.ts +2 -0
  100. package/dist/observable/index.js +2 -0
  101. package/dist/operations/comment.d.ts +3 -2
  102. package/dist/operations/comment.js +19 -5
  103. package/dist/operations/group.d.ts +14 -1
  104. package/dist/operations/group.js +42 -4
  105. package/dist/operations/index.d.ts +1 -1
  106. package/dist/operations/index.js +1 -1
  107. package/dist/operations/tag/bookmarks.d.ts +3 -2
  108. package/dist/operations/tag/bookmarks.js +34 -14
  109. package/dist/operations/torrent.d.ts +2 -0
  110. package/dist/operations/torrent.js +4 -0
  111. package/dist/register.d.ts +2 -11
  112. package/dist/register.js +2 -11
  113. package/package.json +12 -2
  114. package/dist/helpers/mailboxes.d.ts +0 -7
  115. package/dist/helpers/mailboxes.js +0 -49
  116. /package/dist/models/{relays.d.ts → relay-lists.d.ts} +0 -0
@@ -0,0 +1,14 @@
1
+ import { kinds, KnownEvent, NostrEvent } from "applesauce-core/helpers/event";
2
+ export declare const FAVORITE_RELAYS_KIND = 10012;
3
+ export type FavoriteRelaysListEvent = KnownEvent<typeof FAVORITE_RELAYS_KIND>;
4
+ export type SearchRelaysListEvent = KnownEvent<typeof kinds.SearchRelaysList>;
5
+ export type BlockedRelaysListEvent = KnownEvent<typeof kinds.BlockedRelaysList>;
6
+ export type DMRelaysListEvent = KnownEvent<typeof kinds.DirectMessageRelaysList>;
7
+ /** Validates that an event is a valid favorite relays list (kind 10012) */
8
+ export declare function isValidFavoriteRelaysList(event: NostrEvent): event is FavoriteRelaysListEvent;
9
+ /** Validates that an event is a valid search relays list (kind 10007) */
10
+ export declare function isValidSearchRelaysList(event: NostrEvent): event is SearchRelaysListEvent;
11
+ /** Validates that an event is a valid blocked relays list (kind 10006) */
12
+ export declare function isValidBlockedRelaysList(event: NostrEvent): event is BlockedRelaysListEvent;
13
+ /** Validates that an event is a valid DM relays list (kind 10050) */
14
+ export declare function isValidDirectMessageRelaysList(event: NostrEvent): event is DMRelaysListEvent;
@@ -0,0 +1,18 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ export const FAVORITE_RELAYS_KIND = 10012;
3
+ /** Validates that an event is a valid favorite relays list (kind 10012) */
4
+ export function isValidFavoriteRelaysList(event) {
5
+ return event.kind === FAVORITE_RELAYS_KIND;
6
+ }
7
+ /** Validates that an event is a valid search relays list (kind 10007) */
8
+ export function isValidSearchRelaysList(event) {
9
+ return event.kind === kinds.SearchRelaysList;
10
+ }
11
+ /** Validates that an event is a valid blocked relays list (kind 10006) */
12
+ export function isValidBlockedRelaysList(event) {
13
+ return event.kind === kinds.BlockedRelaysList;
14
+ }
15
+ /** Validates that an event is a valid DM relays list (kind 10050) */
16
+ export function isValidDirectMessageRelaysList(event) {
17
+ return event.kind === kinds.DirectMessageRelaysList;
18
+ }
@@ -14,6 +14,7 @@ export type ReportedUser = {
14
14
  event: NostrEvent;
15
15
  pubkey: string;
16
16
  reason?: ReportReason;
17
+ comment?: string;
17
18
  };
18
19
  export type ReportedEvent = {
19
20
  type: "event";
@@ -25,4 +26,6 @@ export type ReportedEvent = {
25
26
  blobs?: string[];
26
27
  };
27
28
  /** Reads a report event as either a user or event report */
28
- export declare function getReported(report: NostrEvent): ReportedEvent | ReportedUser;
29
+ export declare function getReported(report: NostrEvent): ReportedEvent | ReportedUser | null;
30
+ /** Gets the server tags from a report event (for blob reports) */
31
+ export declare function getReportServers(report: NostrEvent): string[];
@@ -1,5 +1,5 @@
1
1
  import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
2
- import { isETag, isPTag } from "applesauce-core/helpers/tags";
2
+ import { isETag, isNameValueTag, isPTag } from "applesauce-core/helpers/tags";
3
3
  export const ParsedReportSymbol = Symbol("parsed-report");
4
4
  export var ReportReason;
5
5
  (function (ReportReason) {
@@ -14,25 +14,29 @@ export var ReportReason;
14
14
  /** Reads a report event as either a user or event report */
15
15
  export function getReported(report) {
16
16
  return getOrComputeCachedValue(report, ParsedReportSymbol, () => {
17
- const p = report.tags.find(isPTag);
18
- if (!p)
19
- throw new Error("Report missing p tag");
17
+ const pTag = report.tags.find(isPTag);
18
+ if (!pTag)
19
+ return null;
20
20
  const comment = report.content ? report.content.trim() : undefined;
21
- const e = report.tags.find(isETag);
21
+ const eTag = report.tags.find(isETag);
22
22
  // Event report
23
- if (e) {
23
+ if (eTag) {
24
24
  const blobs = report.tags.filter((t) => t[0] === "x" && t[1]).map((t) => t[1]);
25
25
  return {
26
26
  type: "event",
27
27
  event: report,
28
28
  comment,
29
- id: e[1],
30
- pubkey: p[1],
31
- reason: e[2],
29
+ id: eTag[1],
30
+ pubkey: pTag[1],
31
+ reason: eTag[2],
32
32
  blobs,
33
33
  };
34
34
  }
35
35
  // User report
36
- return { type: "user", event: report, comment, pubkey: p[1], reason: p[2] };
36
+ return { type: "user", event: report, comment, pubkey: pTag[1], reason: pTag[2] };
37
37
  });
38
38
  }
39
+ /** Gets the server tags from a report event (for blob reports) */
40
+ export function getReportServers(report) {
41
+ return report.tags.filter((t) => isNameValueTag(t, "server") && t[1]).map((t) => t[1]);
42
+ }
@@ -1,4 +1,7 @@
1
- import { NostrEvent } from "applesauce-core/helpers/event";
1
+ import { kinds, KnownEvent, NostrEvent } from "applesauce-core/helpers/event";
2
2
  import { AddressPointer } from "applesauce-core/helpers/pointers";
3
+ export type StreamChatMessageEvent = KnownEvent<kinds.LiveChatMessage>;
4
+ export declare function isValidStreamChatMessage(event: NostrEvent): event is StreamChatMessageEvent;
3
5
  /** Returns the pointer to the stream chat message stream */
6
+ export declare function getStreamChatMessageStream(message: StreamChatMessageEvent): AddressPointer;
4
7
  export declare function getStreamChatMessageStream(message: NostrEvent): AddressPointer | undefined;
@@ -1,6 +1,9 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
1
2
  import { getAddressPointerFromATag } from "applesauce-core/helpers/pointers";
2
3
  import { isATag } from "applesauce-core/helpers/tags";
3
- /** Returns the pointer to the stream chat message stream */
4
+ export function isValidStreamChatMessage(event) {
5
+ return event.kind === kinds.LiveChatMessage && getStreamChatMessageStream(event) !== undefined;
6
+ }
4
7
  export function getStreamChatMessageStream(message) {
5
8
  const tag = message.tags.find(isATag);
6
9
  if (!tag)
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export * as Models from "./models/index.js";
3
3
  export * as Operations from "./operations/index.js";
4
4
  export * as Blueprints from "./blueprints/index.js";
5
5
  export * as Observable from "./observable/index.js";
6
+ export * as Casts from "./casts/index.js";
6
7
  import "./register.js";
package/dist/index.js CHANGED
@@ -3,5 +3,6 @@ export * as Models from "./models/index.js";
3
3
  export * as Operations from "./operations/index.js";
4
4
  export * as Blueprints from "./blueprints/index.js";
5
5
  export * as Observable from "./observable/index.js";
6
+ export * as Casts from "./casts/index.js";
6
7
  // Register the common models and blueprints with the event store and event factory
7
8
  import "./register.js";
@@ -0,0 +1,5 @@
1
+ import "./blossom.js";
2
+ import "./mutes.js";
3
+ import "./reactions.js";
4
+ import "./comments.js";
5
+ import "./thread.js";
@@ -0,0 +1,6 @@
1
+ // Import models that should register with the event store
2
+ import "./blossom.js";
3
+ import "./mutes.js";
4
+ import "./reactions.js";
5
+ import "./comments.js";
6
+ import "./thread.js";
@@ -2,10 +2,10 @@ import { Model } from "applesauce-core/event-store";
2
2
  import { ProfilePointer } from "applesauce-core/helpers/pointers";
3
3
  import { type Observable } from "rxjs";
4
4
  /** A model that returns a users blossom servers */
5
- export declare function UserBlossomServersModel(user: string | ProfilePointer): Model<URL[]>;
5
+ export declare function UserBlossomServersModel(user: string | ProfilePointer): Model<URL[] | undefined>;
6
6
  declare module "applesauce-core/event-store" {
7
7
  interface EventModels {
8
8
  /** Subscribe to a users blossom servers */
9
- blossomServers(user: string | ProfilePointer): Observable<URL[]>;
9
+ blossomServers(user: string | ProfilePointer): Observable<URL[] | undefined>;
10
10
  }
11
11
  }
@@ -8,7 +8,7 @@ export function UserBlossomServersModel(user) {
8
8
  user = { pubkey: user };
9
9
  return (store) => store
10
10
  .replaceable({ kind: BLOSSOM_SERVER_LIST_KIND, pubkey: user.pubkey, relays: user.relays })
11
- .pipe(map((event) => (event ? getBlossomServersFromList(event) : [])));
11
+ .pipe(map((event) => event && getBlossomServersFromList(event)));
12
12
  }
13
13
  // Register this model with EventModels
14
14
  EventModels.prototype.blossomServers = function (user) {
@@ -1,8 +1,6 @@
1
1
  import { Model } from "applesauce-core/event-store";
2
- import { Bookmarks } from "../helpers/bookmark.js";
2
+ import { BookmarkPointer } from "../helpers/bookmark.js";
3
3
  /** A model that returns all the bookmarks of a user */
4
- export declare function UserBookmarkModel(pubkey: string): Model<Bookmarks | undefined>;
5
- /** A model that returns all the public bookmarks of a user */
6
- export declare function UserPublicBookmarkModel(pubkey: string): Model<Bookmarks | undefined>;
4
+ export declare function UserBookmarkModel(pubkey: string): Model<BookmarkPointer[] | undefined>;
7
5
  /** A model that returns all the hidden bookmarks of a user */
8
- export declare function UserHiddenBookmarkModel(pubkey: string): Model<Bookmarks | null | undefined>;
6
+ export declare function UserHiddenBookmarkModel(pubkey: string): Model<BookmarkPointer[] | null | undefined>;
@@ -1,18 +1,10 @@
1
1
  import { kinds } from "applesauce-core/helpers/event";
2
2
  import { watchEventUpdates } from "applesauce-core/observable";
3
3
  import { map } from "rxjs/operators";
4
- import { getBookmarks, getHiddenBookmarks, getPublicBookmarks } from "../helpers/bookmark.js";
4
+ import { getBookmarks, getHiddenBookmarks } from "../helpers/bookmark.js";
5
5
  /** A model that returns all the bookmarks of a user */
6
6
  export function UserBookmarkModel(pubkey) {
7
- return (events) => events.replaceable(kinds.Mutelist, pubkey).pipe(
8
- // listen for event updates (hidden tags unlocked)
9
- watchEventUpdates(events),
10
- // Get all bookmarks
11
- map((event) => event && getBookmarks(event)));
12
- }
13
- /** A model that returns all the public bookmarks of a user */
14
- export function UserPublicBookmarkModel(pubkey) {
15
- return (events) => events.replaceable(kinds.Mutelist, pubkey).pipe(map((event) => event && getPublicBookmarks(event)));
7
+ return (events) => events.replaceable(kinds.BookmarkList, pubkey).pipe(map((event) => event && getBookmarks(event)));
16
8
  }
17
9
  /** A model that returns all the hidden bookmarks of a user */
18
10
  export function UserHiddenBookmarkModel(pubkey) {
@@ -31,15 +31,9 @@ export function ChannelMetadataModel(channel) {
31
31
  ];
32
32
  let latest = channel;
33
33
  return events.filters(filters).pipe(map((event) => {
34
- try {
35
- if (event.pubkey === latest.pubkey && event.created_at > latest.created_at) {
36
- latest = event;
37
- }
38
- return getChannelMetadataContent(latest);
39
- }
40
- catch (error) {
41
- return undefined;
42
- }
34
+ if (event.pubkey === latest.pubkey && event.created_at > latest.created_at)
35
+ latest = event;
36
+ return getChannelMetadataContent(latest) ?? undefined;
43
37
  }));
44
38
  };
45
39
  }
@@ -1,11 +1,12 @@
1
1
  import { Model } from "applesauce-core/event-store";
2
2
  import { NostrEvent } from "applesauce-core/helpers/event";
3
3
  import { type Observable } from "rxjs";
4
+ import { CommentPointer } from "../helpers/comment.js";
4
5
  /** A model that returns all NIP-22 comment replies for the event */
5
- export declare function CommentsModel(parent: NostrEvent): Model<NostrEvent[]>;
6
+ export declare function CommentsModel(parent: NostrEvent | CommentPointer): Model<NostrEvent[]>;
6
7
  declare module "applesauce-core/event-store" {
7
8
  interface EventModels {
8
9
  /** Subscribe to an event's comments */
9
- comments(event: NostrEvent): Observable<NostrEvent[]>;
10
+ comments(event: NostrEvent | CommentPointer): Observable<NostrEvent[]>;
10
11
  }
11
12
  }
@@ -1,9 +1,27 @@
1
1
  import { EventModels } from "applesauce-core/event-store";
2
+ import { createReplaceableAddress, isEvent } from "applesauce-core/helpers/event";
2
3
  import { buildCommonEventRelationFilters } from "applesauce-core/helpers/model";
4
+ import { of } from "rxjs";
3
5
  import { COMMENT_KIND } from "../helpers/comment.js";
4
6
  /** A model that returns all NIP-22 comment replies for the event */
5
7
  export function CommentsModel(parent) {
6
- return (events) => events.timeline(buildCommonEventRelationFilters({ kinds: [COMMENT_KIND] }, parent));
8
+ return (events) => {
9
+ if (isEvent(parent))
10
+ return events.timeline(buildCommonEventRelationFilters({ kinds: [COMMENT_KIND] }, parent));
11
+ else {
12
+ switch (parent.type) {
13
+ case "event":
14
+ return events.timeline({ kinds: [COMMENT_KIND], "#e": [parent.id] });
15
+ case "address":
16
+ return events.timeline({
17
+ kinds: [COMMENT_KIND],
18
+ "#a": [createReplaceableAddress(parent.kind, parent.pubkey, parent.identifier)],
19
+ });
20
+ default:
21
+ return of([]);
22
+ }
23
+ }
24
+ };
7
25
  }
8
26
  // Register this model with EventModels
9
27
  EventModels.prototype.comments = function (event) {
@@ -12,6 +12,8 @@ export * from "./user-status.js";
12
12
  export * from "./wrapped-messages.js";
13
13
  export * from "./zaps.js";
14
14
  export * from "./gift-wrap.js";
15
- export * from "./relays.js";
15
+ export * from "./relay-lists.js";
16
16
  export * from "./stream.js";
17
+ export * from "./shares.js";
18
+ export * from "applesauce-core/models";
17
19
  import "../register.js";
@@ -12,7 +12,10 @@ export * from "./user-status.js";
12
12
  export * from "./wrapped-messages.js";
13
13
  export * from "./zaps.js";
14
14
  export * from "./gift-wrap.js";
15
- export * from "./relays.js";
15
+ export * from "./relay-lists.js";
16
16
  export * from "./stream.js";
17
+ export * from "./shares.js";
18
+ // Export all models from core
19
+ export * from "applesauce-core/models";
17
20
  // Register the common models with the event store
18
21
  import "../register.js";
@@ -1,16 +1,16 @@
1
1
  import { Model } from "applesauce-core/event-store";
2
2
  import { ProfilePointer } from "applesauce-core/helpers/pointers";
3
3
  import { type Observable } from "rxjs";
4
- import { Mutes } from "../helpers/mute.js";
4
+ import { MutedThings } from "../helpers/mute.js";
5
5
  /** A model that returns all a users muted things */
6
- export declare function MuteModel(user: string | ProfilePointer): Model<Mutes | undefined>;
6
+ export declare function MuteModel(user: string | ProfilePointer): Model<MutedThings | undefined>;
7
7
  /** A model that returns all a users public muted things */
8
- export declare function PublicMuteModel(pubkey: string): Model<Mutes | undefined>;
8
+ export declare function PublicMuteModel(pubkey: string): Model<MutedThings | undefined>;
9
9
  /** A model that returns all a users hidden muted things */
10
- export declare function HiddenMuteModel(pubkey: string): Model<Mutes | null | undefined>;
10
+ export declare function HiddenMuteModel(pubkey: string): Model<MutedThings | null | undefined>;
11
11
  declare module "applesauce-core/event-store" {
12
12
  interface EventModels {
13
13
  /** Subscribe to a users mutes */
14
- mutes(user: string | ProfilePointer): Observable<Mutes | undefined>;
14
+ mutes(user: string | ProfilePointer): Observable<MutedThings | undefined>;
15
15
  }
16
16
  }
@@ -1,7 +1,8 @@
1
1
  import { kinds } from "applesauce-core/helpers/event";
2
2
  import { watchEventUpdates } from "applesauce-core/observable";
3
3
  import { identity, map } from "rxjs";
4
- import { FAVORITE_RELAYS_KIND, getAddressPointersFromList, getRelaysFromList } from "../helpers/lists.js";
4
+ import { getAddressPointersFromList, getRelaysFromList } from "../helpers/lists.js";
5
+ import { FAVORITE_RELAYS_KIND } from "../helpers/relay-list.js";
5
6
  /**
6
7
  * A model that returns all favorite relays for a pubkey
7
8
  * @param pubkey - The pubkey to get the favorite relays for
@@ -0,0 +1,3 @@
1
+ import { Model } from "applesauce-core/event-store";
2
+ import { NostrEvent } from "applesauce-core/helpers/event";
3
+ export declare function SharesModel(event: NostrEvent): Model<NostrEvent[]>;
@@ -0,0 +1,5 @@
1
+ import { kinds } from "applesauce-core/helpers/event";
2
+ import { buildCommonEventRelationFilters } from "applesauce-core/helpers/model";
3
+ export function SharesModel(event) {
4
+ return (events) => events.timeline(buildCommonEventRelationFilters({ kinds: [kinds.Repost, kinds.GenericRepost] }, event));
5
+ }
@@ -1,12 +1,10 @@
1
- import { createReplaceableAddress, getEventUID, isEvent, kinds } from "applesauce-core/helpers/event";
2
- import { getTagValue } from "applesauce-core/helpers/event";
3
- import { getReplaceableAddressFromPointer, isAddressPointer, isEventPointer, } from "applesauce-core/helpers/pointers";
4
- import { isAddressableKind } from "applesauce-core/helpers/event";
5
- import { map } from "rxjs/operators";
6
- import { COMMENT_KIND } from "../helpers/comment.js";
7
- import { getNip10References, interpretThreadTags } from "../helpers/threading.js";
8
- // Import EventModels as a value (class) to modify its prototype
9
1
  import { EventModels } from "applesauce-core/event-store";
2
+ import { buildCommonEventRelationFilters } from "applesauce-core/helpers";
3
+ import { getEventUID, kinds } from "applesauce-core/helpers/event";
4
+ import { eventMatchesPointer, getReplaceableAddressFromPointer, isAddressPointer, } from "applesauce-core/helpers/pointers";
5
+ import { map } from "rxjs/operators";
6
+ import { COMMENT_KIND, getCommentReplyPointer } from "../helpers/comment.js";
7
+ import { getNip10References } from "../helpers/threading.js";
10
8
  const defaultOptions = {
11
9
  kinds: [kinds.ShortTextNote],
12
10
  };
@@ -69,22 +67,30 @@ export function ThreadModel(root, opts) {
69
67
  /** A model that gets all legacy and NIP-10, and NIP-22 replies for an event */
70
68
  export function RepliesModel(event, overrideKinds) {
71
69
  return (events) => {
72
- const kinds = overrideKinds || event.kind === 1 ? [1, COMMENT_KIND] : [COMMENT_KIND];
73
- const filter = { kinds };
74
- if (isEvent(parent) || isEventPointer(event))
75
- filter["#e"] = [event.id];
76
- const address = isAddressableKind(event.kind)
77
- ? createReplaceableAddress(event.kind, event.pubkey, getTagValue(event, "d"))
78
- : undefined;
79
- if (address) {
80
- filter["#a"] = [address];
81
- }
82
- return events.timeline(filter).pipe(map((events) => {
83
- return events.filter((e) => {
84
- const refs = interpretThreadTags(e.tags);
85
- return refs.reply?.e?.[1] === event.id || refs.reply?.a?.[1] === address;
86
- });
87
- }));
70
+ const filter = { kinds: overrideKinds || event.kind === 1 ? [1, COMMENT_KIND] : [COMMENT_KIND] };
71
+ return events.timeline(buildCommonEventRelationFilters(filter, event)).pipe(map((events) =>
72
+ // Filter for direct replies
73
+ events.filter((reply) => {
74
+ if (reply.kind === kinds.ShortTextNote) {
75
+ // Check if a NIP-10 reply is a direct reply to this event
76
+ const refs = getNip10References(reply);
77
+ const pointer = refs.reply?.a || refs.reply?.e;
78
+ if (!pointer)
79
+ return false;
80
+ return eventMatchesPointer(event, pointer);
81
+ }
82
+ else if (reply.kind === COMMENT_KIND) {
83
+ // Check if a NIP-22 reply is a direct reply to this event
84
+ const pointer = getCommentReplyPointer(reply);
85
+ if (!pointer)
86
+ return false;
87
+ if (pointer.type === "address")
88
+ return eventMatchesPointer(event, pointer);
89
+ else if (pointer.type === "event")
90
+ return pointer.id === event.id;
91
+ }
92
+ return false;
93
+ })));
88
94
  };
89
95
  }
90
96
  // Register this model with EventModels
@@ -0,0 +1,8 @@
1
+ import type { NostrEvent } from "applesauce-core/helpers/event";
2
+ import { OperatorFunction } from "rxjs";
3
+ import type { CastConstructor, CastRefEventStore } from "../casts/cast.js";
4
+ import { EventCast } from "../casts/cast.js";
5
+ /** Casts an event to a specific type */
6
+ export declare function castEventStream<C extends EventCast>(cls: CastConstructor<C>, store?: CastRefEventStore): OperatorFunction<NostrEvent | undefined, C | undefined>;
7
+ /** Casts and array of events to an array of casted events and filters out undefined values */
8
+ export declare function castTimelineStream<C extends EventCast>(cls: CastConstructor<C>, store?: CastRefEventStore): OperatorFunction<NostrEvent[], C[]>;
@@ -0,0 +1,29 @@
1
+ import { defined } from "applesauce-core/observable/defined";
2
+ import { map } from "rxjs";
3
+ import { castEvent } from "../casts/cast.js";
4
+ /** Casts an event to a specific type */
5
+ export function castEventStream(cls, store) {
6
+ return (source) => source.pipe(map((event) => {
7
+ if (!event)
8
+ return undefined;
9
+ try {
10
+ return castEvent(event, cls, store);
11
+ }
12
+ catch { }
13
+ return undefined;
14
+ }));
15
+ }
16
+ /** Casts and array of events to an array of casted events and filters out undefined values */
17
+ export function castTimelineStream(cls, store) {
18
+ return (source) => source.pipe(map((events) => {
19
+ const castedEvents = [];
20
+ for (const event of events) {
21
+ try {
22
+ const casted = castEvent(event, cls, store);
23
+ castedEvents.push(casted);
24
+ }
25
+ catch { }
26
+ }
27
+ return castedEvents;
28
+ }), defined());
29
+ }
@@ -0,0 +1,50 @@
1
+ import { Observable } from "rxjs";
2
+ /**
3
+ * Wraps an Observable in a Proxy that enables property chaining.
4
+ * When accessing a property ending with `$`, it uses switchMap to chain
5
+ * to that property's observable value.
6
+ * When accessing a non-observable property, it returns an Observable of that property's value.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const author$ = chainable(note.author$);
11
+ * const outboxes$ = author$.outboxes$; // Observable<string[] | undefined>
12
+ * const displayName$ = author$.displayName; // Observable<string | undefined>
13
+ * ```
14
+ */
15
+ export declare function chainable<T>(observable: Observable<T>): ChainableObservable<T>;
16
+ /**
17
+ * Helper type to extract nullable parts (undefined/null) from a type
18
+ */
19
+ type NullableParts<T> = Extract<T, undefined | null>;
20
+ /**
21
+ * Helper type to get the property type, preserving nullable parts from the parent type
22
+ */
23
+ type PropChain<T, K extends keyof NonNullable<T>> = NonNullable<T>[K] | NullableParts<T>;
24
+ /**
25
+ * A chainable Observable type that allows property chaining.
26
+ * This type maps all properties to chainable observables:
27
+ * - Properties ending with $: extracts inner type from Observable<U> → ChainableObservable<U>
28
+ * - Other properties: uses property type directly → ChainableObservable<PropertyType>
29
+ *
30
+ * Note: TypeScript has limitations inferring through Proxy types. For better
31
+ * type inference, you may need to explicitly type the result:
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const inboxes$: Observable<string[] | undefined> = note?.author$.inboxes$;
36
+ * const displayName$: Observable<string | undefined> = note?.author$.displayName;
37
+ * const inboxes = useObservableMemo(() => inboxes$, [note]);
38
+ * ```
39
+ */
40
+ export type ChainableObservable<T> = Observable<T> & Omit<{
41
+ [K in keyof NonNullable<T> as K extends string ? K : never]: K extends `${infer _}$` ? NonNullable<T>[K] extends Observable<infer U> ? ChainableObservable<U | NullableParts<T>> : never : ChainableObservable<PropChain<T, K>>;
42
+ }, "$first" | "$last"> & {
43
+ /** Returns a promise that resolves with the first value or rejects with a timeout error */
44
+ $first(first?: number): Promise<NonNullable<T>>;
45
+ $first<V>(first?: number, fallback?: V): Promise<NonNullable<T> | V>;
46
+ /** Returns a promise that resolves with the last value or rejects with a timeout error */
47
+ $last(max?: number): Promise<NonNullable<T>>;
48
+ $last<V>(max?: number, fallback?: V): Promise<NonNullable<T> | V>;
49
+ };
50
+ export {};
@@ -0,0 +1,79 @@
1
+ import { filter, firstValueFrom, isObservable, lastValueFrom, map, of, switchMap, timeout } from "rxjs";
2
+ /**
3
+ * A symbol used to mark an Observable as chainable
4
+ */
5
+ const CHAINABLE_CACHE_SYMBOL = Symbol.for("chainable-cache");
6
+ /**
7
+ * Wraps an Observable in a Proxy that enables property chaining.
8
+ * When accessing a property ending with `$`, it uses switchMap to chain
9
+ * to that property's observable value.
10
+ * When accessing a non-observable property, it returns an Observable of that property's value.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const author$ = chainable(note.author$);
15
+ * const outboxes$ = author$.outboxes$; // Observable<string[] | undefined>
16
+ * const displayName$ = author$.displayName; // Observable<string | undefined>
17
+ * ```
18
+ */
19
+ export function chainable(observable) {
20
+ // Create a Proxy that intercepts property access
21
+ const proxy = new Proxy(observable, {
22
+ get(target$, prop) {
23
+ const cache = observable[CHAINABLE_CACHE_SYMBOL] ||
24
+ (observable[CHAINABLE_CACHE_SYMBOL] = {});
25
+ // Forward all Observable methods and properties
26
+ if (prop in target$ || typeof prop === "symbol") {
27
+ const value = target$[prop];
28
+ // If it's a function, bind it to the target
29
+ if (typeof value === "function")
30
+ return value.bind(target$);
31
+ return value;
32
+ }
33
+ if (typeof prop === "string") {
34
+ // Return cached observable if it exists
35
+ const cached = cache[prop];
36
+ if (cached)
37
+ return cached;
38
+ // Otherwise, create a new observable
39
+ let prop$;
40
+ // Extra observalbe helpers to make it easier to work with observables
41
+ if (prop === "$first") {
42
+ return (...args) => firstValueFrom(target$.pipe(filter((v) => v !== undefined && v !== null), args.length === 2
43
+ ? timeout({ first: args[0], with: () => of(args[1]) })
44
+ : timeout({ first: args[0] ?? 10_000 })));
45
+ }
46
+ else if (prop === "$last") {
47
+ return (...args) => lastValueFrom(target$.pipe(filter((v) => v !== undefined && v !== null), args.length === 2
48
+ ? timeout({ first: args[0], with: () => of(args[1]) })
49
+ : timeout({ first: args[0] ?? 10_000 })));
50
+ }
51
+ // Handle property access for properties ending with $
52
+ else if (prop.endsWith("$")) {
53
+ // Use switchMap to chain to the nested observable
54
+ prop$ = target$.pipe(switchMap((target) => {
55
+ const value = target === undefined || target === null ? target : target[prop];
56
+ // If value is an observable, return it
57
+ if (isObservable(value))
58
+ return value;
59
+ // Otherwise wrap it in an observable
60
+ else
61
+ return of(value);
62
+ }));
63
+ }
64
+ // For non-$ properties, return an Observable of the property value
65
+ else {
66
+ prop$ = target$.pipe(
67
+ // Access the property on the value if target is not undefined or null
68
+ map((target) => (target === undefined || target === null ? target : target[prop])));
69
+ }
70
+ // Make the chained observable chainable too
71
+ const observable = chainable(prop$);
72
+ cache[prop] = observable;
73
+ return observable;
74
+ }
75
+ throw new Error(`Unable to access property "${prop}" on chainable observable`);
76
+ },
77
+ });
78
+ return proxy;
79
+ }
@@ -1 +1,3 @@
1
1
  export * from "./filter-timeline-by-mutes.js";
2
+ export * from "./cast-stream.js";
3
+ export * from "./chainable.js";
@@ -1 +1,3 @@
1
1
  export * from "./filter-timeline-by-mutes.js";
2
+ export * from "./cast-stream.js";
3
+ export * from "./chainable.js";
@@ -1,4 +1,5 @@
1
1
  import { EventOperation } from "applesauce-core/event-factory";
2
2
  import { NostrEvent } from "applesauce-core/helpers/event";
3
- /** Sets the necessary tags for a NIP-22 comment event to point to a parent event */
4
- export declare function setParent(parent: NostrEvent): EventOperation;
3
+ import { CommentPointer } from "../helpers/comment.js";
4
+ /** Sets the necessary tags for a NIP-22 comment event to point to a parent event or pointer */
5
+ export declare function setParent(parent: NostrEvent | CommentPointer): EventOperation;