applesauce-core 0.4.0 → 0.5.0

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.
@@ -5,3 +5,4 @@ export * from "./filter.js";
5
5
  export * from "./mailboxes.js";
6
6
  export * from "./threading.js";
7
7
  export * from "./pointers.js";
8
+ export * from "./string.js";
@@ -5,3 +5,4 @@ export * from "./filter.js";
5
5
  export * from "./mailboxes.js";
6
6
  export * from "./threading.js";
7
7
  export * from "./pointers.js";
8
+ export * from "./string.js";
@@ -0,0 +1,2 @@
1
+ export declare function isHex(str?: string): boolean;
2
+ export declare function isHexKey(key?: string): boolean;
@@ -0,0 +1,10 @@
1
+ export function isHex(str) {
2
+ if (str?.match(/^[0-9a-f]+$/i))
3
+ return true;
4
+ return false;
5
+ }
6
+ export function isHexKey(key) {
7
+ if (key?.toLowerCase()?.match(/^[0-9a-f]{64}$/))
8
+ return true;
9
+ return false;
10
+ }
@@ -2,4 +2,4 @@ export type Deferred<T> = Promise<T> & {
2
2
  resolve: (value?: T | PromiseLike<T>) => void;
3
3
  reject: (reason?: any) => void;
4
4
  };
5
- export default function createDefer<T>(): Deferred<T>;
5
+ export declare function createDefer<T>(): Deferred<T>;
@@ -1,4 +1,4 @@
1
- export default function createDefer() {
1
+ export function createDefer() {
2
2
  let _resolve;
3
3
  let _reject;
4
4
  const promise = new Promise((resolve, reject) => {
@@ -2,6 +2,5 @@ export * from "./simple.js";
2
2
  export * from "./profile.js";
3
3
  export * from "./mailboxes.js";
4
4
  export * from "./reactions.js";
5
- export * from "./channel.js";
6
5
  export * from "./mute.js";
7
6
  export * from "./thread.js";
@@ -2,6 +2,5 @@ export * from "./simple.js";
2
2
  export * from "./profile.js";
3
3
  export * from "./mailboxes.js";
4
4
  export * from "./reactions.js";
5
- export * from "./channel.js";
6
5
  export * from "./mute.js";
7
6
  export * from "./thread.js";
@@ -1,5 +1,10 @@
1
1
  import { Filter, NostrEvent } from "nostr-tools";
2
2
  import { Query } from "../query-store/index.js";
3
+ /** Creates a Query that returns a single event or undefined */
3
4
  export declare function SingleEventQuery(uid: string): Query<NostrEvent | undefined>;
5
+ /** Creates a Query returning the latest version of a replaceable event */
4
6
  export declare function ReplaceableQuery(kind: number, pubkey: string, d?: string): Query<NostrEvent | undefined>;
7
+ /** Creates a Query that returns an array of sorted events matching the filters */
5
8
  export declare function TimelineQuery(filters: Filter | Filter[]): Query<NostrEvent[]>;
9
+ /** Creates a Query that returns a directory of events by their UID */
10
+ export declare function ReplaceableSetQuery(filters: Filter | Filter[]): Query<Record<string, NostrEvent>>;
@@ -1,20 +1,32 @@
1
1
  import stringify from "json-stringify-deterministic";
2
- import { getReplaceableUID } from "../helpers/event.js";
2
+ import { getEventUID, getReplaceableUID } from "../helpers/event.js";
3
+ /** Creates a Query that returns a single event or undefined */
3
4
  export function SingleEventQuery(uid) {
4
5
  return {
5
6
  key: uid,
6
7
  run: (events) => events.event(uid),
7
8
  };
8
9
  }
10
+ /** Creates a Query returning the latest version of a replaceable event */
9
11
  export function ReplaceableQuery(kind, pubkey, d) {
10
12
  return {
11
13
  key: getReplaceableUID(kind, pubkey, d),
12
14
  run: (events) => events.replaceable(kind, pubkey, d),
13
15
  };
14
16
  }
17
+ /** Creates a Query that returns an array of sorted events matching the filters */
15
18
  export function TimelineQuery(filters) {
16
19
  return {
17
20
  key: stringify(filters),
18
21
  run: (events) => events.timeline(Array.isArray(filters) ? filters : [filters]),
19
22
  };
20
23
  }
24
+ /** Creates a Query that returns a directory of events by their UID */
25
+ export function ReplaceableSetQuery(filters) {
26
+ return {
27
+ key: stringify(filters),
28
+ run: (events) => events
29
+ .timeline(Array.isArray(filters) ? filters : [filters])
30
+ .map((events) => events.reduce((dir, event) => ({ ...dir, [getEventUID(event)]: event }), {})),
31
+ };
32
+ }
@@ -23,6 +23,8 @@ export declare class QueryStore {
23
23
  event(id: string): Observable<import("nostr-tools").Event | undefined>;
24
24
  /** Returns the latest version of a replaceable event */
25
25
  replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
26
+ /** Returns a directory of events by their UID */
27
+ replaceableSet(filters: Filter | Filter[]): Observable<Record<string, import("nostr-tools").Event>>;
26
28
  /** Returns an array of events that match the filter */
27
29
  timeline(filters: Filter | Filter[]): Observable<import("nostr-tools").Event[]>;
28
30
  /** Returns the parsed profile (0) for a pubkey */
@@ -29,6 +29,10 @@ export class QueryStore {
29
29
  replaceable(kind, pubkey, d) {
30
30
  return this.runQuery(Queries.ReplaceableQuery)(kind, pubkey, d);
31
31
  }
32
+ /** Returns a directory of events by their UID */
33
+ replaceableSet(filters) {
34
+ return this.runQuery(Queries.ReplaceableSetQuery)(filters);
35
+ }
32
36
  /** Returns an array of events that match the filter */
33
37
  timeline(filters) {
34
38
  return this.runQuery(Queries.TimelineQuery)(filters);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "applesauce-core",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,10 +22,18 @@
22
22
  "import": "./dist/helpers/index.js",
23
23
  "types": "./dist/helpers/index.d.ts"
24
24
  },
25
+ "./queries": {
26
+ "import": "./dist/queries/index.js",
27
+ "types": "./dist/queries/index.d.ts"
28
+ },
25
29
  "./observable": {
26
30
  "import": "./dist/observable/index.js",
27
31
  "types": "./dist/observable/index.d.ts"
28
32
  },
33
+ "./promise": {
34
+ "import": "./dist/promise/index.js",
35
+ "types": "./dist/promise/index.d.ts"
36
+ },
29
37
  "./query-store": {
30
38
  "import": "./dist/query-store/index.js",
31
39
  "types": "./dist/query-store/index.d.ts"
@@ -33,28 +41,24 @@
33
41
  "./event-store": {
34
42
  "import": "./dist/event-store/index.js",
35
43
  "types": "./dist/event-store/index.d.ts"
36
- },
37
- "./queries": {
38
- "import": "./dist/queries/index.js",
39
- "types": "./dist/queries/index.d.ts"
40
44
  }
41
45
  },
42
46
  "dependencies": {
43
- "@types/zen-push": "^0.1.4",
44
47
  "debug": "^4.3.7",
45
48
  "json-stringify-deterministic": "^1.0.12",
46
49
  "nanoid": "^5.0.7",
47
50
  "nostr-tools": "^2.7.2",
48
- "zen-push": "^0.3.1"
51
+ "zen-push": "^0.3.1",
52
+ "zen-observable": "^0.10.0"
49
53
  },
50
54
  "devDependencies": {
55
+ "@types/zen-push": "^0.1.4",
51
56
  "@jest/globals": "^29.7.0",
52
57
  "@types/debug": "^4.1.12",
53
58
  "@types/jest": "^29.5.13",
54
59
  "@types/zen-observable": "^0.8.7",
55
60
  "jest": "^29.7.0",
56
- "jest-extended": "^4.0.2",
57
- "zen-observable": "^0.10.0"
61
+ "jest-extended": "^4.0.2"
58
62
  },
59
63
  "jest": {
60
64
  "roots": [
@@ -1,15 +0,0 @@
1
- import { nip19, NostrEvent } from "nostr-tools";
2
- import { ChannelMetadata } from "nostr-tools/nip28";
3
- export declare const ChannelMetadataSymbol: unique symbol;
4
- declare module "nostr-tools" {
5
- interface Event {
6
- [ChannelMetadataSymbol]?: ChannelMetadataContent;
7
- }
8
- }
9
- export type ChannelMetadataContent = ChannelMetadata & {
10
- relays?: string[];
11
- };
12
- /** Gets the parsed metadata on a channel creation or channel metadata event */
13
- export declare function getChannelMetadataContent(channel: NostrEvent): ChannelMetadataContent;
14
- /** gets the EventPointer for a channel message or metadata event */
15
- export declare function getChannelPointer(event: NostrEvent): nip19.EventPointer | undefined;
@@ -1,27 +0,0 @@
1
- export const ChannelMetadataSymbol = Symbol.for("channel-metadata");
2
- function parseChannelMetadataContent(channel) {
3
- const metadata = JSON.parse(channel.content);
4
- if (metadata.name === undefined)
5
- throw new Error("Missing name");
6
- if (metadata.about === undefined)
7
- throw new Error("Missing about");
8
- if (metadata.picture === undefined)
9
- throw new Error("Missing picture");
10
- if (metadata.relays && !Array.isArray(metadata.relays))
11
- throw new Error("Invalid relays");
12
- return metadata;
13
- }
14
- /** Gets the parsed metadata on a channel creation or channel metadata event */
15
- export function getChannelMetadataContent(channel) {
16
- let metadata = channel[ChannelMetadataSymbol];
17
- if (!metadata)
18
- metadata = channel[ChannelMetadataSymbol] = parseChannelMetadataContent(channel);
19
- return metadata;
20
- }
21
- /** gets the EventPointer for a channel message or metadata event */
22
- export function getChannelPointer(event) {
23
- const tag = event.tags.find((t) => t[0] === "e" && t[1]);
24
- if (!tag)
25
- return undefined;
26
- return tag[2] ? { id: tag[1], relays: [tag[2]] } : { id: tag[1] };
27
- }
@@ -1,11 +0,0 @@
1
- import { NostrEvent } from "nostr-tools";
2
- import { Query } from "../query-store/index.js";
3
- import { ChannelMetadataContent } from "../helpers/channel.js";
4
- /** Creates a query that returns the latest parsed metadata */
5
- export declare function ChannelMetadataQuery(channel: NostrEvent): Query<ChannelMetadataContent | undefined>;
6
- /** Creates a query that returns a map of hidden messages Map<id, reason> */
7
- export declare function ChannelHiddenQuery(channel: NostrEvent, authors?: string[]): Query<Map<string, string>>;
8
- /** Creates a query that returns a map of muted users Map<pubkey, reason> */
9
- export declare function ChannelMutedQuery(channel: NostrEvent, authors?: string[]): Query<Map<string, string>>;
10
- /** Creates a query that returns all messages in a channel */
11
- export declare function ChannelMessagesQuery(channel: NostrEvent): Query<NostrEvent[]>;
@@ -1,72 +0,0 @@
1
- import { kinds } from "nostr-tools";
2
- import { getChannelMetadataContent } from "../helpers/channel.js";
3
- import { safeParse } from "../helpers/json.js";
4
- /** Creates a query that returns the latest parsed metadata */
5
- export function ChannelMetadataQuery(channel) {
6
- return {
7
- key: channel.id,
8
- run: (events) => {
9
- const filters = [
10
- { ids: [channel.id] },
11
- { kinds: [kinds.ChannelMetadata], "#e": [channel.id], authors: [channel.pubkey] },
12
- ];
13
- let latest = channel;
14
- return events.stream(filters).map((event) => {
15
- try {
16
- if (event.pubkey === latest.pubkey && event.created_at > latest.created_at) {
17
- latest = event;
18
- }
19
- return getChannelMetadataContent(latest);
20
- }
21
- catch (error) {
22
- return undefined;
23
- }
24
- });
25
- },
26
- };
27
- }
28
- /** Creates a query that returns a map of hidden messages Map<id, reason> */
29
- export function ChannelHiddenQuery(channel, authors = []) {
30
- return {
31
- key: channel.id,
32
- run: (events) => {
33
- const hidden = new Map();
34
- return events
35
- .stream([{ kinds: [kinds.ChannelHideMessage], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
36
- .map((event) => {
37
- const reason = safeParse(event.content)?.reason;
38
- for (const tag of event.tags) {
39
- if (tag[0] === "e" && tag[1])
40
- hidden.set(tag[1], reason ?? "");
41
- }
42
- return hidden;
43
- });
44
- },
45
- };
46
- }
47
- /** Creates a query that returns a map of muted users Map<pubkey, reason> */
48
- export function ChannelMutedQuery(channel, authors = []) {
49
- return {
50
- key: channel.id + authors.join(","),
51
- run: (events) => {
52
- const muted = new Map();
53
- return events
54
- .stream([{ kinds: [kinds.ChannelMuteUser], "#e": [channel.id], authors: [channel.pubkey, ...authors] }])
55
- .map((event) => {
56
- const reason = safeParse(event.content)?.reason;
57
- for (const tag of event.tags) {
58
- if (tag[0] === "p" && tag[1])
59
- muted.set(tag[1], reason ?? "");
60
- }
61
- return muted;
62
- });
63
- },
64
- };
65
- }
66
- /** Creates a query that returns all messages in a channel */
67
- export function ChannelMessagesQuery(channel) {
68
- return {
69
- key: channel.id,
70
- run: (events) => events.timeline([{ kinds: [kinds.ChannelMessage], "#e": [channel.id] }]),
71
- };
72
- }