applesauce-common 0.0.0-next-20251220152312 → 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.
- package/README.md +45 -4
- package/dist/blueprints/__register__.d.ts +1 -0
- package/dist/blueprints/__register__.js +1 -0
- package/dist/blueprints/comment.d.ts +3 -2
- package/dist/blueprints/torrent.d.ts +7 -0
- package/dist/blueprints/torrent.js +5 -1
- package/dist/casts/article.d.ts +2 -0
- package/dist/casts/article.js +6 -0
- package/dist/casts/bookmarks.d.ts +3 -3
- package/dist/casts/bookmarks.js +18 -6
- package/dist/casts/comment.d.ts +2 -0
- package/dist/casts/comment.js +5 -0
- package/dist/casts/groups.d.ts +19 -0
- package/dist/casts/groups.js +43 -0
- package/dist/casts/index.d.ts +6 -0
- package/dist/casts/index.js +6 -0
- package/dist/casts/note.d.ts +8 -0
- package/dist/casts/note.js +30 -0
- package/dist/casts/profile.d.ts +8 -0
- package/dist/casts/profile.js +12 -0
- package/dist/casts/reaction.d.ts +17 -0
- package/dist/casts/reaction.js +46 -0
- package/dist/casts/relay-discovery.d.ts +29 -0
- package/dist/casts/relay-discovery.js +54 -0
- package/dist/casts/relay-monitor.d.ts +21 -0
- package/dist/casts/relay-monitor.js +41 -0
- package/dist/casts/report.d.ts +31 -0
- package/dist/casts/report.js +74 -0
- package/dist/casts/share.d.ts +2 -0
- package/dist/casts/share.js +6 -0
- package/dist/casts/torrent.d.ts +31 -0
- package/dist/casts/torrent.js +62 -0
- package/dist/casts/user.d.ts +2 -0
- package/dist/casts/user.js +13 -1
- package/dist/helpers/calendar-event.d.ts +7 -1
- package/dist/helpers/calendar-event.js +8 -10
- package/dist/helpers/channels.d.ts +1 -1
- package/dist/helpers/channels.js +5 -8
- package/dist/helpers/comment.d.ts +3 -1
- package/dist/helpers/comment.js +12 -2
- package/dist/helpers/external-id.d.ts +32 -0
- package/dist/helpers/external-id.js +85 -0
- package/dist/helpers/file-metadata.d.ts +1 -4
- package/dist/helpers/file-metadata.js +1 -4
- package/dist/helpers/groups.d.ts +11 -1
- package/dist/helpers/groups.js +30 -5
- package/dist/helpers/reports.d.ts +4 -1
- package/dist/helpers/reports.js +14 -10
- package/dist/models/blossom.d.ts +2 -2
- package/dist/models/blossom.js +1 -1
- package/dist/models/channels.js +3 -9
- package/dist/models/comments.d.ts +3 -2
- package/dist/models/comments.js +19 -1
- package/dist/operations/comment.d.ts +3 -2
- package/dist/operations/comment.js +19 -5
- package/dist/operations/torrent.d.ts +2 -0
- package/dist/operations/torrent.js +4 -0
- package/package.json +2 -2
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NostrEvent } from "applesauce-core/helpers";
|
|
2
|
+
import { RelayMonitorAnnouncementEvent } from "../helpers/relay-discovery.js";
|
|
3
|
+
import { CastRefEventStore, EventCast } from "./cast.js";
|
|
4
|
+
import { RelayDiscovery } from "./relay-discovery.js";
|
|
5
|
+
/** Cast a kind 10166 event to a RelayMonitor */
|
|
6
|
+
export declare class RelayMonitor extends EventCast<RelayMonitorAnnouncementEvent> {
|
|
7
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
8
|
+
/** Gets the frequency at which this monitor publishes events (in seconds) */
|
|
9
|
+
get frequency(): number | undefined;
|
|
10
|
+
/** Gets all timeout values from this monitor announcement */
|
|
11
|
+
get timeouts(): {
|
|
12
|
+
test?: string;
|
|
13
|
+
timeout: number;
|
|
14
|
+
}[];
|
|
15
|
+
/** Gets all checks conducted by this monitor */
|
|
16
|
+
get checks(): string[];
|
|
17
|
+
/** Gets the geohash from this monitor announcement (NIP-52 geohash) */
|
|
18
|
+
get geohash(): string | undefined;
|
|
19
|
+
/** Get the status of a relay from this monitor */
|
|
20
|
+
relayStatus(relay: string): import("../observable/chainable.js").ChainableObservable<RelayDiscovery | undefined>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { switchMap } from "rxjs";
|
|
2
|
+
import { getMonitorChecks, getMonitorFrequency, getMonitorGeohash, getMonitorTimeouts, isValidRelayMonitorAnnouncement, RELAY_DISCOVERY_KIND, } from "../helpers/relay-discovery.js";
|
|
3
|
+
import { castEventStream } from "../observable/cast-stream.js";
|
|
4
|
+
import { chainable } from "../observable/chainable.js";
|
|
5
|
+
import { EventCast } from "./cast.js";
|
|
6
|
+
import { RelayDiscovery } from "./relay-discovery.js";
|
|
7
|
+
/** Cast a kind 10166 event to a RelayMonitor */
|
|
8
|
+
export class RelayMonitor extends EventCast {
|
|
9
|
+
constructor(event, store) {
|
|
10
|
+
if (!isValidRelayMonitorAnnouncement(event))
|
|
11
|
+
throw new Error("Invalid relay monitor announcement");
|
|
12
|
+
super(event, store);
|
|
13
|
+
}
|
|
14
|
+
/** Gets the frequency at which this monitor publishes events (in seconds) */
|
|
15
|
+
get frequency() {
|
|
16
|
+
return getMonitorFrequency(this.event);
|
|
17
|
+
}
|
|
18
|
+
/** Gets all timeout values from this monitor announcement */
|
|
19
|
+
get timeouts() {
|
|
20
|
+
return getMonitorTimeouts(this.event);
|
|
21
|
+
}
|
|
22
|
+
/** Gets all checks conducted by this monitor */
|
|
23
|
+
get checks() {
|
|
24
|
+
return getMonitorChecks(this.event);
|
|
25
|
+
}
|
|
26
|
+
/** Gets the geohash from this monitor announcement (NIP-52 geohash) */
|
|
27
|
+
get geohash() {
|
|
28
|
+
return getMonitorGeohash(this.event);
|
|
29
|
+
}
|
|
30
|
+
/** Get the status of a relay from this monitor */
|
|
31
|
+
relayStatus(relay) {
|
|
32
|
+
return chainable(this.author.outboxes$.pipe(switchMap((outboxes) => this.store
|
|
33
|
+
.replaceable({
|
|
34
|
+
kind: RELAY_DISCOVERY_KIND,
|
|
35
|
+
pubkey: this.author.pubkey,
|
|
36
|
+
identifier: relay,
|
|
37
|
+
relays: outboxes,
|
|
38
|
+
})
|
|
39
|
+
.pipe(castEventStream(RelayDiscovery, this.store)))));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { KnownEvent, NostrEvent } from "applesauce-core/helpers/event";
|
|
2
|
+
import { ReportReason, ReportedEvent, ReportedUser } from "../helpers/reports.js";
|
|
3
|
+
import { CastRefEventStore, EventCast } from "./cast.js";
|
|
4
|
+
declare const REPORT_KIND = 1984;
|
|
5
|
+
/** Cast a kind 1984 event to a Report */
|
|
6
|
+
export declare class Report extends EventCast<KnownEvent<typeof REPORT_KIND>> {
|
|
7
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
8
|
+
/** Get the parsed report data (either a user or event report) */
|
|
9
|
+
get reported(): ReportedEvent | ReportedUser | null;
|
|
10
|
+
/** Check if this is a user report */
|
|
11
|
+
get isUserReport(): boolean;
|
|
12
|
+
/** Check if this is an event report */
|
|
13
|
+
get isEventReport(): boolean;
|
|
14
|
+
/** Get the reason for the report */
|
|
15
|
+
get reason(): ReportReason | undefined;
|
|
16
|
+
/** Get the comment/content of the report */
|
|
17
|
+
get comment(): string | undefined;
|
|
18
|
+
/** Get the pubkey of the user being reported */
|
|
19
|
+
get reportedPubkey(): string | undefined;
|
|
20
|
+
/** Get the User being reported */
|
|
21
|
+
get reportedUser(): import("./user.js").User | undefined;
|
|
22
|
+
/** Get the event ID being reported (for event reports) */
|
|
23
|
+
get reportedEventId(): string | undefined;
|
|
24
|
+
/** Get the event being reported (for event reports) */
|
|
25
|
+
get reportedEvent$(): import("../observable/chainable.js").ChainableObservable<import("nostr-tools").Event | undefined>;
|
|
26
|
+
/** Get the blob hashes being reported (for event reports with x tags) */
|
|
27
|
+
get blobs(): string[] | undefined;
|
|
28
|
+
/** Get the server URLs for blob reports */
|
|
29
|
+
get servers(): string[];
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { of } from "rxjs";
|
|
2
|
+
import { getReportServers, getReported } from "../helpers/reports.js";
|
|
3
|
+
import { EventCast } from "./cast.js";
|
|
4
|
+
import { castUser } from "./user.js";
|
|
5
|
+
import { addRelayHintsToPointer } from "applesauce-core/helpers";
|
|
6
|
+
const REPORT_KIND = 1984;
|
|
7
|
+
function isValidReport(event) {
|
|
8
|
+
return event.kind === REPORT_KIND;
|
|
9
|
+
}
|
|
10
|
+
/** Cast a kind 1984 event to a Report */
|
|
11
|
+
export class Report extends EventCast {
|
|
12
|
+
constructor(event, store) {
|
|
13
|
+
if (!isValidReport(event))
|
|
14
|
+
throw new Error("Invalid report");
|
|
15
|
+
super(event, store);
|
|
16
|
+
}
|
|
17
|
+
/** Get the parsed report data (either a user or event report) */
|
|
18
|
+
get reported() {
|
|
19
|
+
return getReported(this.event);
|
|
20
|
+
}
|
|
21
|
+
/** Check if this is a user report */
|
|
22
|
+
get isUserReport() {
|
|
23
|
+
return this.reported?.type === "user";
|
|
24
|
+
}
|
|
25
|
+
/** Check if this is an event report */
|
|
26
|
+
get isEventReport() {
|
|
27
|
+
return this.reported?.type === "event";
|
|
28
|
+
}
|
|
29
|
+
/** Get the reason for the report */
|
|
30
|
+
get reason() {
|
|
31
|
+
return this.reported?.reason;
|
|
32
|
+
}
|
|
33
|
+
/** Get the comment/content of the report */
|
|
34
|
+
get comment() {
|
|
35
|
+
if (!this.reported)
|
|
36
|
+
return undefined;
|
|
37
|
+
return this.reported.type === "event" ? this.reported.comment : this.reported.comment;
|
|
38
|
+
}
|
|
39
|
+
/** Get the pubkey of the user being reported */
|
|
40
|
+
get reportedPubkey() {
|
|
41
|
+
return this.reported?.pubkey;
|
|
42
|
+
}
|
|
43
|
+
/** Get the User being reported */
|
|
44
|
+
get reportedUser() {
|
|
45
|
+
const pubkey = this.reportedPubkey;
|
|
46
|
+
if (!pubkey)
|
|
47
|
+
return undefined;
|
|
48
|
+
return castUser(pubkey, this.store);
|
|
49
|
+
}
|
|
50
|
+
/** Get the event ID being reported (for event reports) */
|
|
51
|
+
get reportedEventId() {
|
|
52
|
+
return this.reported?.type === "event" ? this.reported.id : undefined;
|
|
53
|
+
}
|
|
54
|
+
/** Get the event being reported (for event reports) */
|
|
55
|
+
get reportedEvent$() {
|
|
56
|
+
return this.$$ref("reportedEvent$", (store) => {
|
|
57
|
+
const eventId = this.reportedEventId;
|
|
58
|
+
if (!eventId)
|
|
59
|
+
return of(undefined);
|
|
60
|
+
const pointer = {
|
|
61
|
+
id: eventId,
|
|
62
|
+
};
|
|
63
|
+
return store.event(addRelayHintsToPointer(pointer, this.seen));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** Get the blob hashes being reported (for event reports with x tags) */
|
|
67
|
+
get blobs() {
|
|
68
|
+
return this.reported?.type === "event" ? this.reported.blobs : undefined;
|
|
69
|
+
}
|
|
70
|
+
/** Get the server URLs for blob reports */
|
|
71
|
+
get servers() {
|
|
72
|
+
return getReportServers(this.event);
|
|
73
|
+
}
|
|
74
|
+
}
|
package/dist/casts/share.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
2
2
|
import { ShareEvent } from "../helpers/share.js";
|
|
3
3
|
import { CastRefEventStore, EventCast } from "./cast.js";
|
|
4
|
+
import { Reaction } from "./reaction.js";
|
|
4
5
|
/** Cast class for kind 6 and 16 share events */
|
|
5
6
|
export declare class Share extends EventCast<ShareEvent> {
|
|
6
7
|
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
@@ -10,4 +11,5 @@ export declare class Share extends EventCast<ShareEvent> {
|
|
|
10
11
|
get sharedEventPointer(): import("nostr-tools/nip19").EventPointer;
|
|
11
12
|
get sharedPointer(): import("nostr-tools/nip19").EventPointer | import("nostr-tools/nip19").AddressPointer;
|
|
12
13
|
get shared$(): import("../observable/chainable.js").ChainableObservable<import("nostr-tools").Event | undefined>;
|
|
14
|
+
get reactions$(): import("../observable/chainable.js").ChainableObservable<Reaction[]>;
|
|
13
15
|
}
|
package/dist/casts/share.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { getEmbededSharedEvent, getSharedAddressPointer, getSharedEventPointer, isValidShare, } from "../helpers/share.js";
|
|
2
|
+
import { ReactionsModel } from "../models/reactions.js";
|
|
3
|
+
import { castTimelineStream } from "../observable/cast-stream.js";
|
|
2
4
|
import { EventCast } from "./cast.js";
|
|
5
|
+
import { Reaction } from "./reaction.js";
|
|
3
6
|
/** Cast class for kind 6 and 16 share events */
|
|
4
7
|
export class Share extends EventCast {
|
|
5
8
|
constructor(event, store) {
|
|
@@ -25,4 +28,7 @@ export class Share extends EventCast {
|
|
|
25
28
|
get shared$() {
|
|
26
29
|
return this.$$ref("shared$", (store) => store.event(this.sharedPointer));
|
|
27
30
|
}
|
|
31
|
+
get reactions$() {
|
|
32
|
+
return this.$$ref("reactions$", (store) => store.model(ReactionsModel, this.event).pipe(castTimelineStream(Reaction, store)));
|
|
33
|
+
}
|
|
28
34
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
2
|
+
import { TorrentEvent } from "../helpers/torrent.js";
|
|
3
|
+
import { CastRefEventStore, EventCast } from "./cast.js";
|
|
4
|
+
import { Comment } from "./comment.js";
|
|
5
|
+
import { Reaction } from "./reaction.js";
|
|
6
|
+
import { Zap } from "./zap.js";
|
|
7
|
+
export declare class Torrent extends EventCast<TorrentEvent> {
|
|
8
|
+
constructor(event: NostrEvent, store: CastRefEventStore);
|
|
9
|
+
/** The BitTorrent info hash from the `x` tag */
|
|
10
|
+
get infoHash(): string;
|
|
11
|
+
/** The torrent title from the `title` tag */
|
|
12
|
+
get title(): string | undefined;
|
|
13
|
+
/** All file entries from `file` tags */
|
|
14
|
+
get files(): import("../helpers/torrent.js").TorrentFile[];
|
|
15
|
+
/** All tracker URLs from `tracker` tags */
|
|
16
|
+
get trackers(): string[];
|
|
17
|
+
/** The newznab category ID from the `i` tag with `newznab:` prefix */
|
|
18
|
+
get category(): number | undefined;
|
|
19
|
+
/** All search tags (for searchability) from `t` tags */
|
|
20
|
+
get searchTags(): string[];
|
|
21
|
+
/** The category path from the `tcat` identifier in `i` tags (e.g., "video,movie,4k") */
|
|
22
|
+
get categoryPath(): string | undefined;
|
|
23
|
+
/** All external identifiers from `i` tags (excluding tcat and newznab, which are handled separately) */
|
|
24
|
+
get externalIdentifiers(): import("../helpers/torrent.js").TorrentExternalIdentifier[];
|
|
25
|
+
/** The magnet link for the torrent, built from the info hash, trackers, and title */
|
|
26
|
+
get magnetLink(): string | undefined;
|
|
27
|
+
/** Gets the NIP-22 comments to this event */
|
|
28
|
+
get comments$(): import("../observable/chainable.js").ChainableObservable<Comment[]>;
|
|
29
|
+
get zaps$(): import("../observable/chainable.js").ChainableObservable<Zap[]>;
|
|
30
|
+
get reactions$(): import("../observable/chainable.js").ChainableObservable<Reaction[]>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { getTorrentCategory, getTorrentCategoryPath, getTorrentExternalIdentifiers, getTorrentFiles, getTorrentInfoHash, getTorrentMagnetLink, getTorrentSearchTags, getTorrentTitle, getTorrentTrackers, isValidTorrent, } from "../helpers/torrent.js";
|
|
2
|
+
import { CommentsModel } from "../models/comments.js";
|
|
3
|
+
import { ReactionsModel } from "../models/reactions.js";
|
|
4
|
+
import { EventZapsModel } from "../models/zaps.js";
|
|
5
|
+
import { castTimelineStream } from "../observable/cast-stream.js";
|
|
6
|
+
import { EventCast } from "./cast.js";
|
|
7
|
+
import { Comment } from "./comment.js";
|
|
8
|
+
import { Reaction } from "./reaction.js";
|
|
9
|
+
import { Zap } from "./zap.js";
|
|
10
|
+
export class Torrent extends EventCast {
|
|
11
|
+
constructor(event, store) {
|
|
12
|
+
if (!isValidTorrent(event))
|
|
13
|
+
throw new Error("Invalid torrent");
|
|
14
|
+
super(event, store);
|
|
15
|
+
}
|
|
16
|
+
/** The BitTorrent info hash from the `x` tag */
|
|
17
|
+
get infoHash() {
|
|
18
|
+
return getTorrentInfoHash(this.event);
|
|
19
|
+
}
|
|
20
|
+
/** The torrent title from the `title` tag */
|
|
21
|
+
get title() {
|
|
22
|
+
return getTorrentTitle(this.event);
|
|
23
|
+
}
|
|
24
|
+
/** All file entries from `file` tags */
|
|
25
|
+
get files() {
|
|
26
|
+
return getTorrentFiles(this.event);
|
|
27
|
+
}
|
|
28
|
+
/** All tracker URLs from `tracker` tags */
|
|
29
|
+
get trackers() {
|
|
30
|
+
return getTorrentTrackers(this.event);
|
|
31
|
+
}
|
|
32
|
+
/** The newznab category ID from the `i` tag with `newznab:` prefix */
|
|
33
|
+
get category() {
|
|
34
|
+
return getTorrentCategory(this.event);
|
|
35
|
+
}
|
|
36
|
+
/** All search tags (for searchability) from `t` tags */
|
|
37
|
+
get searchTags() {
|
|
38
|
+
return getTorrentSearchTags(this.event);
|
|
39
|
+
}
|
|
40
|
+
/** The category path from the `tcat` identifier in `i` tags (e.g., "video,movie,4k") */
|
|
41
|
+
get categoryPath() {
|
|
42
|
+
return getTorrentCategoryPath(this.event);
|
|
43
|
+
}
|
|
44
|
+
/** All external identifiers from `i` tags (excluding tcat and newznab, which are handled separately) */
|
|
45
|
+
get externalIdentifiers() {
|
|
46
|
+
return getTorrentExternalIdentifiers(this.event);
|
|
47
|
+
}
|
|
48
|
+
/** The magnet link for the torrent, built from the info hash, trackers, and title */
|
|
49
|
+
get magnetLink() {
|
|
50
|
+
return getTorrentMagnetLink(this.event);
|
|
51
|
+
}
|
|
52
|
+
/** Gets the NIP-22 comments to this event */
|
|
53
|
+
get comments$() {
|
|
54
|
+
return this.$$ref("comments$", (store) => store.model(CommentsModel, this.event).pipe(castTimelineStream(Comment, store)));
|
|
55
|
+
}
|
|
56
|
+
get zaps$() {
|
|
57
|
+
return this.$$ref("zaps$", (store) => store.model(EventZapsModel, this.event).pipe(castTimelineStream(Zap, store)));
|
|
58
|
+
}
|
|
59
|
+
get reactions$() {
|
|
60
|
+
return this.$$ref("reactions$", (store) => store.model(ReactionsModel, this.event).pipe(castTimelineStream(Reaction, store)));
|
|
61
|
+
}
|
|
62
|
+
}
|
package/dist/casts/user.d.ts
CHANGED
|
@@ -33,6 +33,8 @@ export declare class User {
|
|
|
33
33
|
get searchRelays$(): ChainableObservable<import("./relay-lists.js").SearchRelays | undefined>;
|
|
34
34
|
get blockedRelays$(): ChainableObservable<import("./relay-lists.js").BlockedRelays | undefined>;
|
|
35
35
|
get directMessageRelays$(): ChainableObservable<string[] | undefined>;
|
|
36
|
+
/** Gets the users list of NIP-29 groups */
|
|
37
|
+
get groups$(): ChainableObservable<import("./groups.js").GroupsList | undefined>;
|
|
36
38
|
/** Get the latest live stream for the user */
|
|
37
39
|
get live$(): ChainableObservable<import("./stream.js").Stream | undefined>;
|
|
38
40
|
}
|
package/dist/casts/user.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { isEvent, isHexKey, kinds, nprofileEncode, npubEncode, } from "applesauce-core/helpers";
|
|
2
2
|
import { combineLatest, defer, from, map, switchMap, tap } from "rxjs";
|
|
3
|
+
import { GROUPS_LIST_KIND } from "../helpers/groups.js";
|
|
4
|
+
import { getRelaysFromList } from "../helpers/lists.js";
|
|
3
5
|
import { FAVORITE_RELAYS_KIND } from "../helpers/relay-list.js";
|
|
4
6
|
import { castEventStream } from "../observable/cast-stream.js";
|
|
5
7
|
import { chainable } from "../observable/chainable.js";
|
|
6
|
-
import { getRelaysFromList } from "../helpers/lists.js";
|
|
7
8
|
export function castUser(user, store) {
|
|
8
9
|
if (isEvent(user)) {
|
|
9
10
|
return castUser(user.pubkey, store);
|
|
@@ -157,6 +158,17 @@ export class User {
|
|
|
157
158
|
.replaceable({ kind: kinds.DirectMessageRelaysList, pubkey: this.pubkey, relays: outboxes })
|
|
158
159
|
.pipe(map((event) => event && getRelaysFromList(event))))));
|
|
159
160
|
}
|
|
161
|
+
/** Gets the users list of NIP-29 groups */
|
|
162
|
+
get groups$() {
|
|
163
|
+
return this.$$ref("groups$", (store) => combineLatest([
|
|
164
|
+
// Import the BlockedRelays class
|
|
165
|
+
defer(() => from(import("./groups.js").then((m) => m.GroupsList))),
|
|
166
|
+
// Get outboxes and start without them
|
|
167
|
+
this.outboxes$,
|
|
168
|
+
]).pipe(switchMap(([GroupsList, outboxes]) => store
|
|
169
|
+
.replaceable({ kind: GROUPS_LIST_KIND, pubkey: this.pubkey, relays: outboxes })
|
|
170
|
+
.pipe(castEventStream(GroupsList, store)))));
|
|
171
|
+
}
|
|
160
172
|
/** Get the latest live stream for the user */
|
|
161
173
|
get live$() {
|
|
162
174
|
return this.$$ref("live$", (store) => defer(() => import("./stream.js").then((m) => m.Stream)).pipe(switchMap((Stream) => store
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { NostrEvent } from "applesauce-core/helpers/event";
|
|
1
|
+
import { KnownEvent, NostrEvent } from "applesauce-core/helpers/event";
|
|
2
2
|
import { ProfilePointer } from "applesauce-core/helpers/pointers";
|
|
3
3
|
import { NameValueTag } from "applesauce-core/helpers/tags";
|
|
4
4
|
export declare const DATE_BASED_CALENDAR_EVENT_KIND = 31922;
|
|
5
5
|
export declare const TIME_BASED_CALENDAR_EVENT_KIND = 31923;
|
|
6
|
+
export type DateBasedCalendarEvent = KnownEvent<typeof DATE_BASED_CALENDAR_EVENT_KIND>;
|
|
7
|
+
export type TimeBasedCalendarEvent = KnownEvent<typeof TIME_BASED_CALENDAR_EVENT_KIND>;
|
|
8
|
+
/** Checks if an event is a date-based calendar event */
|
|
9
|
+
export declare function isValidDateBasedCalendarEvent(event: NostrEvent): event is DateBasedCalendarEvent;
|
|
10
|
+
/** Checks if an event is a time-based calendar event */
|
|
11
|
+
export declare function isValidTimeBasedCalendarEvent(event: NostrEvent): event is TimeBasedCalendarEvent;
|
|
6
12
|
export type CalendarEventParticipant = ProfilePointer & {
|
|
7
13
|
role?: string;
|
|
8
14
|
};
|
|
@@ -6,6 +6,14 @@ import { fillAndTrimTag, isPTag, isRTag, isTTag } from "applesauce-core/helpers/
|
|
|
6
6
|
// NIP-52 Calendar Event Kinds
|
|
7
7
|
export const DATE_BASED_CALENDAR_EVENT_KIND = 31922;
|
|
8
8
|
export const TIME_BASED_CALENDAR_EVENT_KIND = 31923;
|
|
9
|
+
/** Checks if an event is a date-based calendar event */
|
|
10
|
+
export function isValidDateBasedCalendarEvent(event) {
|
|
11
|
+
return event.kind === DATE_BASED_CALENDAR_EVENT_KIND;
|
|
12
|
+
}
|
|
13
|
+
/** Checks if an event is a time-based calendar event */
|
|
14
|
+
export function isValidTimeBasedCalendarEvent(event) {
|
|
15
|
+
return event.kind === TIME_BASED_CALENDAR_EVENT_KIND;
|
|
16
|
+
}
|
|
9
17
|
// Cache symbols for complex operations only
|
|
10
18
|
export const CalendarEventLocationsSymbol = Symbol.for("calendar-event-locations");
|
|
11
19
|
export const CalendarEventParticipantsSymbol = Symbol.for("calendar-event-participants");
|
|
@@ -62,16 +70,12 @@ export function getCalendarEventEnd(event) {
|
|
|
62
70
|
}
|
|
63
71
|
/** Gets all locations from a calendar event */
|
|
64
72
|
export function getCalendarEventLocations(event) {
|
|
65
|
-
if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
|
|
66
|
-
throw new Error("Event is not a date-based or time-based calendar event");
|
|
67
73
|
return getOrComputeCachedValue(event, CalendarEventLocationsSymbol, () => {
|
|
68
74
|
return event.tags.filter((t) => t[0] === "location" && t[1]).map((t) => t[1]);
|
|
69
75
|
});
|
|
70
76
|
}
|
|
71
77
|
/** Gets the geohash of a calendar event */
|
|
72
78
|
export function getCalendarEventGeohash(event) {
|
|
73
|
-
if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
|
|
74
|
-
throw new Error("Event is not a date-based or time-based calendar event");
|
|
75
79
|
return getOrComputeCachedValue(event, CalendarEventGeohashSymbol, () => {
|
|
76
80
|
let hash = undefined;
|
|
77
81
|
for (const tag of event.tags) {
|
|
@@ -83,8 +87,6 @@ export function getCalendarEventGeohash(event) {
|
|
|
83
87
|
}
|
|
84
88
|
/** Gets all participants from a calendar event */
|
|
85
89
|
export function getCalendarEventParticipants(event) {
|
|
86
|
-
if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
|
|
87
|
-
throw new Error("Event is not a date-based or time-based calendar event");
|
|
88
90
|
return getOrComputeCachedValue(event, CalendarEventParticipantsSymbol, () => {
|
|
89
91
|
return event.tags
|
|
90
92
|
.filter(isPTag)
|
|
@@ -102,16 +104,12 @@ export function getCalendarEventParticipants(event) {
|
|
|
102
104
|
}
|
|
103
105
|
/** Gets all hashtags from a calendar event */
|
|
104
106
|
export function getCalendarEventHashtags(event) {
|
|
105
|
-
if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
|
|
106
|
-
throw new Error("Event is not a date-based or time-based calendar event");
|
|
107
107
|
return getOrComputeCachedValue(event, CalendarEventHashtagsSymbol, () => {
|
|
108
108
|
return event.tags.filter(isTTag).map((t) => t[1]);
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
/** Gets all references from a calendar event */
|
|
112
112
|
export function getCalendarEventReferences(event) {
|
|
113
|
-
if (event.kind !== DATE_BASED_CALENDAR_EVENT_KIND && event.kind !== TIME_BASED_CALENDAR_EVENT_KIND)
|
|
114
|
-
throw new Error("Event is not a date-based or time-based calendar event");
|
|
115
113
|
return getOrComputeCachedValue(event, CalendarEventReferencesSymbol, () => {
|
|
116
114
|
return event.tags.filter(isRTag).map((t) => t[1]);
|
|
117
115
|
});
|
|
@@ -8,6 +8,6 @@ export type ChannelMetadataContent = {
|
|
|
8
8
|
relays?: string[];
|
|
9
9
|
};
|
|
10
10
|
/** Gets the parsed metadata on a channel creation or channel metadata event */
|
|
11
|
-
export declare function getChannelMetadataContent(channel: NostrEvent): ChannelMetadataContent;
|
|
11
|
+
export declare function getChannelMetadataContent(channel: NostrEvent): ChannelMetadataContent | null;
|
|
12
12
|
/** gets the EventPointer for a channel message or metadata event */
|
|
13
13
|
export declare function getChannelPointer(event: NostrEvent): EventPointer | undefined;
|
package/dist/helpers/channels.js
CHANGED
|
@@ -2,14 +2,11 @@ import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
|
|
|
2
2
|
export const ChannelMetadataSymbol = Symbol.for("channel-metadata");
|
|
3
3
|
function parseChannelMetadataContent(channel) {
|
|
4
4
|
const metadata = JSON.parse(channel.content);
|
|
5
|
-
if (metadata.name === undefined
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
throw new Error("Missing picture");
|
|
11
|
-
if (metadata.relays && !Array.isArray(metadata.relays))
|
|
12
|
-
throw new Error("Invalid relays");
|
|
5
|
+
if (metadata.name === undefined ||
|
|
6
|
+
metadata.about === undefined ||
|
|
7
|
+
metadata.picture === undefined ||
|
|
8
|
+
(metadata.relays && !Array.isArray(metadata.relays)))
|
|
9
|
+
return null;
|
|
13
10
|
return metadata;
|
|
14
11
|
}
|
|
15
12
|
/** Gets the parsed metadata on a channel creation or channel metadata event */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { KnownEvent, NostrEvent } from "applesauce-core/helpers/event";
|
|
2
|
-
import { ExternalIdentifiers, ExternalPointer } from "
|
|
2
|
+
import { ExternalIdentifiers, ExternalPointer } from "./external-id.js";
|
|
3
3
|
export declare const COMMENT_KIND = 1111;
|
|
4
4
|
/** Type for validated comment events */
|
|
5
5
|
export type CommentEvent = KnownEvent<typeof COMMENT_KIND>;
|
|
@@ -39,6 +39,8 @@ export declare function getCommentReplyPointer(comment: NostrEvent): CommentPoin
|
|
|
39
39
|
export declare function isCommentEventPointer(pointer: any): pointer is CommentEventPointer;
|
|
40
40
|
/** Checks if a pointer is a {@link CommentAddressPointer} */
|
|
41
41
|
export declare function isCommentAddressPointer(pointer: any): pointer is CommentAddressPointer;
|
|
42
|
+
/** Checks if a pointer is a {@link CommentExternalPointer} */
|
|
43
|
+
export declare function isCommentExternalPointer(pointer: any): pointer is CommentExternalPointer<keyof ExternalIdentifiers>;
|
|
42
44
|
/** Checks if a comment event is valid */
|
|
43
45
|
export declare function isValidComment(comment: NostrEvent): comment is CommentEvent;
|
|
44
46
|
/** Create a set fo tags for a single CommentPointer */
|
package/dist/helpers/comment.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
|
|
2
2
|
import { createReplaceableAddress, getTagValue, isAddressableKind, } from "applesauce-core/helpers/event";
|
|
3
|
-
import { getExternalPointerFromTag } from "applesauce-core/helpers/external-id";
|
|
4
3
|
import { getAddressPointerFromATag } from "applesauce-core/helpers/pointers";
|
|
5
4
|
import { isSafeRelayURL } from "applesauce-core/helpers/relays";
|
|
6
5
|
import { fillAndTrimTag } from "applesauce-core/helpers/tags";
|
|
6
|
+
import { getExternalPointerFromTag } from "./external-id.js";
|
|
7
7
|
export const COMMENT_KIND = 1111;
|
|
8
8
|
export const CommentRootPointerSymbol = Symbol.for("comment-root-pointer");
|
|
9
9
|
export const CommentReplyPointerSymbol = Symbol.for("comment-reply-pointer");
|
|
@@ -56,9 +56,12 @@ export function getCommentAddressPointer(tags, root = false) {
|
|
|
56
56
|
export function getCommentExternalPointer(tags, root = false) {
|
|
57
57
|
const iTag = tags.find((t) => t[0] === (root ? "I" : "i"));
|
|
58
58
|
if (iTag) {
|
|
59
|
+
const pointer = getExternalPointerFromTag(iTag);
|
|
60
|
+
if (!pointer)
|
|
61
|
+
return null;
|
|
59
62
|
return {
|
|
60
63
|
type: "external",
|
|
61
|
-
...
|
|
64
|
+
...pointer,
|
|
62
65
|
};
|
|
63
66
|
}
|
|
64
67
|
return null;
|
|
@@ -112,6 +115,13 @@ export function isCommentAddressPointer(pointer) {
|
|
|
112
115
|
Reflect.has(pointer, "kind") &&
|
|
113
116
|
typeof pointer.kind === "number");
|
|
114
117
|
}
|
|
118
|
+
/** Checks if a pointer is a {@link CommentExternalPointer} */
|
|
119
|
+
export function isCommentExternalPointer(pointer) {
|
|
120
|
+
return (pointer?.type === "external" &&
|
|
121
|
+
Reflect.has(pointer, "kind") &&
|
|
122
|
+
Reflect.has(pointer, "identifier") &&
|
|
123
|
+
typeof pointer.kind === "string");
|
|
124
|
+
}
|
|
115
125
|
/** Checks if a comment event is valid */
|
|
116
126
|
export function isValidComment(comment) {
|
|
117
127
|
return (comment.kind === COMMENT_KIND && getCommentRootPointer(comment) !== null && getCommentReplyPointer(comment) !== null);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export type ExternalIdentifiers = {
|
|
2
|
+
web: string;
|
|
3
|
+
"#": `#${string}`;
|
|
4
|
+
geo: `geo:${string}`;
|
|
5
|
+
isbn: `isbn:${string}`;
|
|
6
|
+
"podcast:guid": `podcast:guid:${string}`;
|
|
7
|
+
"podcast:item:guid": `podcast:item:guid:${string}`;
|
|
8
|
+
"podcast:publisher:guid": `podcast:publisher:guid:${string}`;
|
|
9
|
+
isan: `isan:${string}`;
|
|
10
|
+
doi: `doi:${string}`;
|
|
11
|
+
"bitcoin:tx": `bitcoin:tx:${string}`;
|
|
12
|
+
"bitcoin:address": `bitcoin:address:${string}`;
|
|
13
|
+
"ethereum:tx": `ethereum:${string}:tx:${string}`;
|
|
14
|
+
"ethereum:address": `ethereum:${string}:address:${string}`;
|
|
15
|
+
[key: `${string}:tx`]: `${string}:tx:${string}`;
|
|
16
|
+
[key: `${string}:address`]: `${string}:address:${string}`;
|
|
17
|
+
};
|
|
18
|
+
export type ExternalPointer<Prefix extends keyof ExternalIdentifiers> = {
|
|
19
|
+
kind: Prefix;
|
|
20
|
+
identifier: ExternalIdentifiers[Prefix];
|
|
21
|
+
};
|
|
22
|
+
export type ParseResult = {
|
|
23
|
+
[P in keyof ExternalIdentifiers]: ExternalPointer<P>;
|
|
24
|
+
}[keyof ExternalIdentifiers];
|
|
25
|
+
/** Casts a string to a valid external pointer */
|
|
26
|
+
export declare function isValidExternalPointer(identifier: string): identifier is `${keyof ExternalIdentifiers}1${string}`;
|
|
27
|
+
/** Parses a NIP-73 external identifier */
|
|
28
|
+
export declare function parseExternalPointer<Prefix extends keyof ExternalIdentifiers>(identifier: `${Prefix}1${string}`): ExternalPointer<Prefix>;
|
|
29
|
+
export declare function parseExternalPointer(identifier: string): ParseResult | null;
|
|
30
|
+
/** Gets an ExternalPointer for a "i" tag */
|
|
31
|
+
export declare function getExternalPointerFromTag<Prefix extends keyof ExternalIdentifiers>(tag: string[]): ExternalPointer<Prefix> | null;
|
|
32
|
+
export declare function getExternalPointerFromTag(tag: string[]): ParseResult | null;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/** Casts a string to a valid external pointer */
|
|
2
|
+
export function isValidExternalPointer(identifier) {
|
|
3
|
+
return parseExternalPointer(identifier) !== null;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes a URL according to NIP-73:
|
|
7
|
+
* - Removes fragment
|
|
8
|
+
* - Returns the normalized URL string
|
|
9
|
+
*/
|
|
10
|
+
function normalizeUrl(url) {
|
|
11
|
+
try {
|
|
12
|
+
const urlObj = new URL(url);
|
|
13
|
+
urlObj.hash = ""; // Remove fragment
|
|
14
|
+
return urlObj.toString();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// If URL parsing fails, return original (will be caught by validation)
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function parseExternalPointer(identifier) {
|
|
22
|
+
// Check explicit prefixes first (these take precedence over URL parsing)
|
|
23
|
+
if (identifier.startsWith("#"))
|
|
24
|
+
return { kind: "#", identifier: identifier };
|
|
25
|
+
if (identifier.startsWith("geo:"))
|
|
26
|
+
return { kind: "geo", identifier: identifier };
|
|
27
|
+
if (identifier.startsWith("isbn:"))
|
|
28
|
+
return { kind: "isbn", identifier: identifier };
|
|
29
|
+
if (identifier.startsWith("podcast:guid:"))
|
|
30
|
+
return { kind: "podcast:guid", identifier: identifier };
|
|
31
|
+
if (identifier.startsWith("podcast:item:guid:"))
|
|
32
|
+
return { kind: "podcast:item:guid", identifier: identifier };
|
|
33
|
+
if (identifier.startsWith("podcast:publisher:guid:"))
|
|
34
|
+
return { kind: "podcast:publisher:guid", identifier: identifier };
|
|
35
|
+
if (identifier.startsWith("isan:"))
|
|
36
|
+
return { kind: "isan", identifier: identifier };
|
|
37
|
+
if (identifier.startsWith("doi:"))
|
|
38
|
+
return { kind: "doi", identifier: identifier };
|
|
39
|
+
// Check for blockchain identifiers
|
|
40
|
+
// Bitcoin: bitcoin:tx:<txid> or bitcoin:address:<address>
|
|
41
|
+
if (identifier.startsWith("bitcoin:tx:")) {
|
|
42
|
+
return { kind: "bitcoin:tx", identifier: identifier };
|
|
43
|
+
}
|
|
44
|
+
if (identifier.startsWith("bitcoin:address:")) {
|
|
45
|
+
return { kind: "bitcoin:address", identifier: identifier };
|
|
46
|
+
}
|
|
47
|
+
// Ethereum: ethereum:<chainId>:tx:<txHash> or ethereum:<chainId>:address:<address>
|
|
48
|
+
const ethereumTxMatch = identifier.match(/^ethereum:(\d+):tx:(.+)$/);
|
|
49
|
+
if (ethereumTxMatch) {
|
|
50
|
+
return { kind: "ethereum:tx", identifier: identifier };
|
|
51
|
+
}
|
|
52
|
+
const ethereumAddressMatch = identifier.match(/^ethereum:(\d+):address:(.+)$/);
|
|
53
|
+
if (ethereumAddressMatch) {
|
|
54
|
+
return { kind: "ethereum:address", identifier: identifier };
|
|
55
|
+
}
|
|
56
|
+
// Other blockchains: <blockchain>:tx:<txid> or <blockchain>:address:<address>
|
|
57
|
+
// Exclude known prefixes to avoid false matches
|
|
58
|
+
const blockchainTxMatch = identifier.match(/^([a-z0-9]+):tx:(.+)$/);
|
|
59
|
+
if (blockchainTxMatch && !identifier.startsWith("bitcoin:") && !identifier.startsWith("ethereum:")) {
|
|
60
|
+
const blockchain = blockchainTxMatch[1];
|
|
61
|
+
return { kind: `${blockchain}:tx`, identifier: identifier };
|
|
62
|
+
}
|
|
63
|
+
const blockchainAddressMatch = identifier.match(/^([a-z0-9]+):address:(.+)$/);
|
|
64
|
+
if (blockchainAddressMatch && !identifier.startsWith("bitcoin:") && !identifier.startsWith("ethereum:")) {
|
|
65
|
+
const blockchain = blockchainAddressMatch[1];
|
|
66
|
+
return { kind: `${blockchain}:address`, identifier: identifier };
|
|
67
|
+
}
|
|
68
|
+
// Check for URL (must be a valid URL, normalized, no fragment)
|
|
69
|
+
// URLs don't have a prefix, so we check if it's a valid URL after checking all prefixes
|
|
70
|
+
try {
|
|
71
|
+
new URL(identifier); // Validate URL
|
|
72
|
+
// Valid URL - normalize it (remove fragment) and return
|
|
73
|
+
const normalized = normalizeUrl(identifier);
|
|
74
|
+
return { kind: "web", identifier: normalized };
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Not a valid URL
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
export function getExternalPointerFromTag(tag) {
|
|
82
|
+
if (!tag[1])
|
|
83
|
+
return null;
|
|
84
|
+
return parseExternalPointer(tag[1]);
|
|
85
|
+
}
|
|
@@ -35,10 +35,7 @@ export type FileMetadata = {
|
|
|
35
35
|
};
|
|
36
36
|
/** Alias for {@link FileMetadata} */
|
|
37
37
|
export type MediaAttachment = FileMetadata;
|
|
38
|
-
/**
|
|
39
|
-
* Parses file metadata tags into {@link FileMetadata}
|
|
40
|
-
* @throws
|
|
41
|
-
*/
|
|
38
|
+
/** Parses file metadata tags into {@link FileMetadata} */
|
|
42
39
|
export declare function parseFileMetadataTags(tags: string[][]): FileMetadata;
|
|
43
40
|
/** Parses a imeta tag into a {@link FileMetadata} */
|
|
44
41
|
export declare function getFileMetadataFromImetaTag(tag: string[]): FileMetadata;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { getOrComputeCachedValue } from "applesauce-core/helpers/cache";
|
|
2
|
-
/**
|
|
3
|
-
* Parses file metadata tags into {@link FileMetadata}
|
|
4
|
-
* @throws
|
|
5
|
-
*/
|
|
2
|
+
/** Parses file metadata tags into {@link FileMetadata} */
|
|
6
3
|
export function parseFileMetadataTags(tags) {
|
|
7
4
|
const fields = {};
|
|
8
5
|
let fallback = undefined;
|