commit 7637e9e8e7083972181db54a67c7432d9fec7a57
parent 4a3a17f9a2827c76f8d862229c70c497e98cf414
Author: triesap <137732411+triesap@users.noreply.github.com>
Date: Sat, 9 Aug 2025 18:22:44 +0000
utils-nostr: add `event` and `relay` utils, types
Diffstat:
6 files changed, 112 insertions(+), 6 deletions(-)
diff --git a/utils-nostr/package.json b/utils-nostr/package.json
@@ -15,7 +15,7 @@
}
},
"scripts": {
- "build:esm": "tsc -p tsconfig.json",
+ "build:esm": "tsc -p tsconfig.esm.json",
"build:cjs": "tsc -p tsconfig.cjs.json",
"build": "npm run clean && npm run build:esm && npm run build:cjs",
"prebuild": "npm run clean",
@@ -32,6 +32,7 @@
"typescript": "5.8.3"
},
"dependencies": {
+ "@radroots/utils": "*",
"@noble/curves": "^1.6.0",
"@noble/hashes": "^1.4.0",
"@nostr-dev-kit/ndk": "2.14.33",
diff --git a/utils-nostr/src/events/lib.ts b/utils-nostr/src/events/lib.ts
@@ -1,12 +1,55 @@
+import { schnorr } from "@noble/curves/secp256k1";
+import { hexToBytes } from "@noble/hashes/utils";
import { NDKEvent, NDKTag } from "@nostr-dev-kit/ndk";
-import { NostrEventTags } from "../types/lib.js";
+import { time_now_ms, uuidv4 } from "@radroots/utils";
+import { finalizeEvent, getEventHash, nip19, type NostrEvent as NostrToolsEvent } from "nostr-tools";
+import { ILibNostrEventSign, ILibNostrNeventEncode, NostrEventTags } from "../types/lib.js";
import { NDKEventFigure } from "../types/ndk.js";
-import { time_now_ms } from "../utils/lib.js";
-
export const get_event_tag = (tags: NDKTag[], key: string): string => tags.find(t => t[0] === key)?.[1] ?? '';
export const get_event_tags = (tags: NDKTag[], key: string): NDKTag[] => tags.filter(t => t[0] === key);
+export const lib_nostr_event_verify = (event: NostrToolsEvent): boolean => {
+ const hash = getEventHash(event);
+ if (hash !== event.id) return false
+ const valid = schnorr.verify(event.sig, hash, event.pubkey);
+ return valid;
+};
+
+export const lib_nostr_event_sign = (opts: ILibNostrEventSign): NostrToolsEvent => {
+ return finalizeEvent(opts.event, hexToBytes(opts.secret_key))
+};
+
+export const lib_nostr_event_sign_attest = (secret_key: string): NostrToolsEvent => {
+ return lib_nostr_event_sign({
+ secret_key,
+ event: {
+ kind: 1,
+ created_at: Math.floor(Date.now() / 1000),
+ tags: [],
+ content: uuidv4(),
+ },
+ });
+};
+
+
+export const lib_nostr_event_verify_serialized = async (event_serialized: string): Promise<{ public_key: string } | undefined> => {
+ try {
+ const event = JSON.parse(event_serialized);
+ const hash = getEventHash(event);
+ if (hash !== event.id) return undefined;
+ const valid = schnorr.verify(event.sig, hash, event.pubkey);
+ if (valid) return { public_key: String(event.pubkey) };
+ return undefined;
+ } catch {
+ return undefined;
+ }
+};
+
+export const lib_nostr_nevent_encode = (opts: ILibNostrNeventEncode): string => {
+ return nip19.neventEncode(opts);
+};
+
export const ndk_event = async (opts: NDKEventFigure<{
basis: {
kind: number;
diff --git a/utils-nostr/src/index.ts b/utils-nostr/src/index.ts
@@ -1,5 +1,7 @@
export * from "./events/comment/lib.js"
export * from "./events/comment/parse.js"
+export * from "./events/follow/lib.js"
+export * from "./events/follow/parse.js"
export * from "./events/lib.js"
export * from "./events/listing/lib.js"
export * from "./events/listing/parse.js"
@@ -9,9 +11,9 @@ export * from "./events/reaction/lib.js"
export * from "./events/reaction/parse.js"
export * from "./events/subscription.js"
export * from "./keys/lib.js"
+export * from "./relay/lib.js"
export * from "./schemas/lib.js"
export * from "./types/lib.js"
export * from "./types/ndk.js"
-export * from "./utils/lib.js"
export * from "./utils/ndk.js"
export * from "./utils/tags.js"
diff --git a/utils-nostr/src/relay/lib.ts b/utils-nostr/src/relay/lib.ts
@@ -0,0 +1,32 @@
+import { NostrRelayInformationDocument, NostrRelayInformationDocumentFields } from "../types/lib.js";
+
+export const lib_nostr_relay_parse_information_document = (data: any): NostrRelayInformationDocument | undefined => {
+ const obj = typeof data === `string` ? JSON.parse(data) : data;
+ return {
+ id: typeof obj.id === 'string' ? obj.id : undefined,
+ name: typeof obj.name === 'string' ? obj.name : undefined,
+ description: typeof obj.description === 'string' ? obj.description : undefined,
+ pubkey: typeof obj.pubkey === 'string' ? obj.pubkey : undefined,
+ contact: typeof obj.contact === 'string' ? obj.contact : undefined,
+ supported_nips: Array.isArray(obj.supported_nips) && obj.supported_nips.every((nip: any) => typeof nip === 'number')
+ ? obj.supported_nips
+ : undefined,
+ software: typeof obj.software === 'string' ? obj.software : undefined,
+ version: typeof obj.version === 'string' ? obj.version : undefined,
+ limitation_payment_required: obj.limitation && typeof obj.limitation === 'object' && typeof obj.limitation.payment_required === 'string' ? obj.limitation.payment_required : undefined,
+ limitation_restricted_writes: obj.limitation && typeof obj.limitation === 'object' && typeof obj.limitation.restricted_writes === 'boolean' ? obj.limitation.restricted_writes : undefined,
+ };
+};
+
+export const lib_nostr_relay_build_information_document = (data: any): NostrRelayInformationDocumentFields | undefined => {
+ const doc = lib_nostr_relay_parse_information_document(data);
+ if (!doc) return;
+ const result: Partial<NostrRelayInformationDocumentFields> = {};
+ Object.entries(doc).forEach(([key, value]) => {
+ if (typeof value === 'boolean') result[key as keyof NostrRelayInformationDocument] = value ? '1' : '0';
+ else if (Array.isArray(value)) result[key as keyof NostrRelayInformationDocument] = value.join(', ');
+ else if (value === null || value === undefined) result[key as keyof NostrRelayInformationDocument] = '';
+ else result[key as keyof NostrRelayInformationDocument] = String(value);
+ });
+ return result;
+};
+\ No newline at end of file
diff --git a/utils-nostr/src/types/lib.ts b/utils-nostr/src/types/lib.ts
@@ -1,3 +1,4 @@
+import { type EventTemplate as NostrToolsEventTemplate } from "nostr-tools";
import { z } from 'zod';
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";
@@ -89,4 +90,31 @@ export type NostrEventTagImage = {
w: number;
h: number;
};
+};
+
+export type NostrRelayInformationDocument = {
+ id?: string;
+ name?: string;
+ description?: string;
+ pubkey?: string;
+ contact?: string;
+ supported_nips?: number[];
+ software?: string;
+ version?: string;
+ limitation_payment_required?: string;
+ limitation_restricted_writes?: boolean;
+}
+
+export type NostrRelayInformationDocumentFields = { [K in keyof NostrRelayInformationDocument]: string; };
+
+export type ILibNostrNeventEncode = {
+ id: string;
+ relays: string[];
+ author: string;
+ kind: number;
+};
+
+export type ILibNostrEventSign = {
+ secret_key: string;
+ event: NostrToolsEventTemplate;
};
\ No newline at end of file
diff --git a/utils-nostr/src/utils/lib.ts b/utils-nostr/src/utils/lib.ts
@@ -1 +0,0 @@
-export const time_now_ms = (): number => Math.floor(new Date().getTime() / 1000);