web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

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:
Mutils-nostr/package.json | 3++-
Mutils-nostr/src/events/lib.ts | 49++++++++++++++++++++++++++++++++++++++++++++++---
Mutils-nostr/src/index.ts | 4+++-
Autils-nostr/src/relay/lib.ts | 33+++++++++++++++++++++++++++++++++
Mutils-nostr/src/types/lib.ts | 28++++++++++++++++++++++++++++
Dutils-nostr/src/utils/lib.ts | 1-
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);