commit 4a3a17f9a2827c76f8d862229c70c497e98cf414
parent 8262fb08ca3dbe172633e9e67f3cfbbc43941ed1
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sat, 9 Aug 2025 17:25:34 +0000
utils-nostr: add `follow` nip-02 schemas, types, event parsing utils
Diffstat:
12 files changed, 106 insertions(+), 26 deletions(-)
diff --git a/utils-nostr/package.json b/utils-nostr/package.json
@@ -4,7 +4,7 @@
"private": true,
"license": "GPLv3",
"type": "module",
- "main": "./dist/cjs/index.cjs",
+ "main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/types/index.d.ts",
"exports": {
diff --git a/utils-nostr/src/events/follow/lib.ts b/utils-nostr/src/events/follow/lib.ts
@@ -0,0 +1,18 @@
+import { NDKEvent } from "@nostr-dev-kit/ndk";
+import { NostrEventFollow } from "../../types/lib.js";
+import { NDKEventFigure } from "../../types/ndk.js";
+import { tags_follow_list } from "../../utils/tags.js";
+import { ndk_event } from "../lib.js";
+
+export const ndk_event_follows = async (opts: NDKEventFigure<{ data: NostrEventFollow; }>): Promise<NDKEvent | undefined> => {
+ const { ndk, ndk_user, data } = opts;
+ return await ndk_event({
+ ndk,
+ ndk_user,
+ basis: {
+ kind: 3,
+ content: ``,
+ tags: tags_follow_list(data.list),
+ },
+ });
+};
+\ No newline at end of file
diff --git a/utils-nostr/src/events/follow/parse.ts b/utils-nostr/src/events/follow/parse.ts
@@ -0,0 +1,15 @@
+import { NDKEvent } from "@nostr-dev-kit/ndk";
+import { nostr_event_follow_schema } from "../../schemas/lib.js";
+import { NostrEventFollow } from "../../types/lib.js";
+
+export const parse_nostr_follow_event = (event: NDKEvent): NostrEventFollow | undefined => {
+ if (!event || typeof event.content !== 'string' || event.kind !== 3) return undefined;
+
+ try {
+ const parsed = JSON.parse(event.content);
+ const result = nostr_event_follow_schema.parse(parsed);
+ return result;
+ } catch {
+ return undefined;
+ }
+};
diff --git a/utils-nostr/src/events/listing/lib.ts b/utils-nostr/src/events/listing/lib.ts
@@ -1,6 +1,6 @@
import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk";
import { NostrEventListing } from "../../types/lib.js";
-import { NDKEventFigure } from "../../types/ndk";
+import { NDKEventFigure } from "../../types/ndk.js";
import { tags_classified } from "../../utils/tags.js";
import { ndk_event } from "../lib.js";
diff --git a/utils-nostr/src/events/subscription.ts b/utils-nostr/src/events/subscription.ts
@@ -1,6 +1,7 @@
import { NDKEvent } from "@nostr-dev-kit/ndk";
-import { NostrEventComment, NostrEventListing, NostrEventReaction, type NostrEventMetadata } from "../types/lib.js";
+import { NostrEventComment, NostrEventFollow, NostrEventListing, NostrEventReaction, type NostrEventMetadata } from "../types/lib.js";
import { parse_nostr_comment_event } from "./comment/parse.js";
+import { parse_nostr_follow_event } from "./follow/parse.js";
import { parse_nostr_listing_event } from "./listing/parse.js";
import { parse_nostr_metadata_event } from "./metadata/parse.js";
import { parse_nostr_reaction_event } from "./reaction/parse.js";
@@ -10,6 +11,8 @@ export type NdkEventPayload =
| { kind: 30402; listing: NostrEventListing; }
| { kind: 1111; comment: NostrEventComment; }
| { kind: 7; reaction: NostrEventReaction; }
+ | { kind: 3; follow: NostrEventFollow; }
+
export const on_ndk_event = (event: NDKEvent): NdkEventPayload | undefined => {
if (!event || typeof event.kind !== 'number') return undefined;
@@ -35,6 +38,11 @@ export const on_ndk_event = (event: NDKEvent): NdkEventPayload | undefined => {
if (!data) return;
return { kind: event.kind, reaction: data };
};
+ case 3: {
+ const data = parse_nostr_follow_event(event);
+ if (!data) return;
+ return { kind: event.kind, follow: data };
+ };
default: return undefined;
}
diff --git a/utils-nostr/src/index.ts b/utils-nostr/src/index.ts
@@ -1,8 +1,12 @@
+export * from "./events/comment/lib.js"
+export * from "./events/comment/parse.js"
export * from "./events/lib.js"
export * from "./events/listing/lib.js"
export * from "./events/listing/parse.js"
export * from "./events/metadata/lib.js"
export * from "./events/metadata/parse.js"
+export * from "./events/reaction/lib.js"
+export * from "./events/reaction/parse.js"
export * from "./events/subscription.js"
export * from "./keys/lib.js"
export * from "./schemas/lib.js"
diff --git a/utils-nostr/src/schemas/lib.ts b/utils-nostr/src/schemas/lib.ts
@@ -128,3 +128,13 @@ export const nostr_event_reaction_schema = z.object({
ref_event: nostr_event_referenced_schema,
content: z.string().min(1),
});
+
+export const nostr_follow_list_schema = z.object({
+ public_key: z.string(),
+ relay_url: z.url().optional(),
+ contact_name: z.string().optional()
+});
+
+export const nostr_event_follow_schema = z.object({
+ list: z.array(nostr_follow_list_schema)
+});
diff --git a/utils-nostr/src/types/lib.ts b/utils-nostr/src/types/lib.ts
@@ -1,18 +1,20 @@
import { z } from 'zod';
-import { nostr_event_comment_schema, nostr_event_listing_schema, nostr_event_metadata_schema, nostr_event_reaction_schema, nostr_event_referenced_schema, nostr_tag_client_schema, nostr_tag_discount_schema, nostr_tag_image_schema, nostr_tag_listing_schema, nostr_tag_location_schema, nostr_tag_price_schema, nostr_tag_quantity_schema } from "../schemas/lib.js";
+import { nostr_event_comment_schema, nostr_event_follow_schema, nostr_event_listing_schema, nostr_event_metadata_schema, nostr_event_reaction_schema, nostr_event_referenced_schema, nostr_follow_list_schema, nostr_tag_client_schema, nostr_tag_discount_schema, nostr_tag_image_schema, nostr_tag_listing_schema, nostr_tag_location_schema, nostr_tag_price_schema, nostr_tag_quantity_schema } from "../schemas/lib.js";
-export type NostrEventMetadata = z.infer<typeof nostr_event_metadata_schema>;
-export type NostrEventListing = z.infer<typeof nostr_event_listing_schema>
-export type NostrTagClient = z.infer<typeof nostr_tag_client_schema>
-export type NostrTagMediaUpload = z.infer<typeof nostr_tag_image_schema>
-export type NostrTagLocation = z.infer<typeof nostr_tag_location_schema>
-export type NostrTagPriceDiscount = z.infer<typeof nostr_tag_discount_schema>
-export type NostrTagPrice = z.infer<typeof nostr_tag_price_schema>
-export type NostrTagQuantity = z.infer<typeof nostr_tag_quantity_schema>
-export type NostrTagListing = z.infer<typeof nostr_tag_listing_schema>
-export type NostrEventComment = z.infer<typeof nostr_event_comment_schema>
-export type NostrEventReferenced = z.infer<typeof nostr_event_referenced_schema>
-export type NostrEventReaction = z.infer<typeof nostr_event_reaction_schema>
+export type NostrEventMetadata = z.infer<typeof nostr_event_metadata_schema>;;
+export type NostrEventListing = z.infer<typeof nostr_event_listing_schema>;
+export type NostrTagClient = z.infer<typeof nostr_tag_client_schema>;
+export type NostrTagMediaUpload = z.infer<typeof nostr_tag_image_schema>;
+export type NostrTagLocation = z.infer<typeof nostr_tag_location_schema>;
+export type NostrTagPriceDiscount = z.infer<typeof nostr_tag_discount_schema>;
+export type NostrTagPrice = z.infer<typeof nostr_tag_price_schema>;
+export type NostrTagQuantity = z.infer<typeof nostr_tag_quantity_schema>;
+export type NostrTagListing = z.infer<typeof nostr_tag_listing_schema>;
+export type NostrEventComment = z.infer<typeof nostr_event_comment_schema>;
+export type NostrEventReferenced = z.infer<typeof nostr_event_referenced_schema>;
+export type NostrEventReaction = z.infer<typeof nostr_event_reaction_schema>;
+export type NostrEventFollow = z.infer<typeof nostr_event_follow_schema>;
+export type NostrFollowList = z.infer<typeof nostr_follow_list_schema>;
export type NostrEventTag = string[];
export type NostrEventTags = NostrEventTag[];
diff --git a/utils-nostr/src/utils/tags.ts b/utils-nostr/src/utils/tags.ts
@@ -1,5 +1,5 @@
import ngeotags, { type InputData as NostrGeotagsInputData } from "nostr-geotags";
-import { NostrEventComment, NostrEventListing, NostrEventReaction, NostrEventTag, NostrEventTagClient, NostrEventTagImage, NostrEventTagLocation, NostrEventTagPrice, NostrEventTagPriceDiscount, NostrEventTagQuantity, NostrEventTags } from "../types/lib.js";
+import { NostrEventComment, NostrEventListing, NostrEventReaction, NostrEventTag, NostrEventTagClient, NostrEventTagImage, NostrEventTagLocation, NostrEventTagPrice, NostrEventTagPriceDiscount, NostrEventTagQuantity, NostrEventTags, NostrFollowList } from "../types/lib.js";
export const tag_client = (opts: NostrEventTagClient, d_tag?: string): NostrEventTag => {
const tag = [`client`, opts.name];
@@ -120,4 +120,13 @@ export const tags_reaction = (opts: NostrEventReaction): NostrEventTags => {
];
if (ref_event.d_tag) tags.push([`a`, `${ref_kind}:${ref_author}:${ref_event.d_tag}`, ...ref_event.relays || ``])
return tags;
+};
+
+export const tags_follow_list = (list: NostrFollowList[]): NostrEventTags => {
+ return list.map(({ public_key, relay_url, contact_name }) => {
+ const entry = [`p`, public_key];
+ if (relay_url) entry.push(relay_url);
+ if (contact_name) entry.push(contact_name);
+ return entry;
+ });
};
\ No newline at end of file
diff --git a/utils-nostr/tsconfig.cjs.json b/utils-nostr/tsconfig.cjs.json
@@ -1,8 +1,14 @@
{
- "extends": "@radroots/tsconfig/tsconfig.cjs.json",
+ "extends": "@radroots/tsconfig/tsconfig.esm.json",
"compilerOptions": {
+ "module": "CommonJS",
+ "moduleResolution": "Node",
"rootDir": "./src",
- "outDir": "dist/cjs"
+ "outDir": "dist/cjs",
+ "declaration": false,
+ "declarationMap": false,
+ "emitDeclarationOnly": false,
+ "tsBuildInfoFile": "node_modules/.cache/tsc.utils-nostr.cjs.tsbuildinfo"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
diff --git a/utils-nostr/tsconfig.esm.json b/utils-nostr/tsconfig.esm.json
@@ -0,0 +1,13 @@
+{
+ "extends": "@radroots/tsconfig/tsconfig.esm.json",
+ "compilerOptions": {
+ "moduleResolution": "nodenext",
+ "rootDir": "./src",
+ "outDir": "dist/esm",
+ "declaration": true,
+ "declarationMap": true,
+ "declarationDir": "dist/types"
+ },
+ "include": ["src"],
+ "exclude": ["node_modules", "dist"]
+}
diff --git a/utils-nostr/tsconfig.json b/utils-nostr/tsconfig.json
@@ -1,9 +1,3 @@
{
- "extends": "@radroots/tsconfig/tsconfig.esm.json",
- "compilerOptions": {
- "rootDir": "./src",
- "outDir": "dist/esm"
- },
- "include": ["src"],
- "exclude": ["node_modules", "dist"]
+ "extends": "./tsconfig.esm.json"
}