web_lib

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

commit 23a87521e2c97476c01d203854eb5b7ce4a533df
parent 085403d8ed7a103fd54bbf39e1089929dbf41e4b
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Fri,  8 Aug 2025 23:20:31 +0000

utils-nostr: add `comment` nip-22 schemas, types, event parsing utils

Diffstat:
Autils-nostr/src/events/comment/lib.ts | 19+++++++++++++++++++
Autils-nostr/src/events/comment/parse.ts | 15+++++++++++++++
Mutils-nostr/src/events/listing/lib.ts | 4+---
Mutils-nostr/src/events/subscription.ts | 9++++++++-
Mutils-nostr/src/schemas/lib.ts | 14++++++++++++++
Mutils-nostr/src/types/lib.ts | 4+++-
Mutils-nostr/src/utils/tags.ts | 38+++++++++++++++++++++++++++++++++++++-
7 files changed, 97 insertions(+), 6 deletions(-)

diff --git a/utils-nostr/src/events/comment/lib.ts b/utils-nostr/src/events/comment/lib.ts @@ -0,0 +1,18 @@ +import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; +import { NostrEventComment } from "../../types/lib"; +import { NDKEventFigure } from "../../types/ndk"; +import { tags_comment } from "../../utils/tags.js"; +import { ndk_event } from "../lib.js"; + +export const ndk_event_comment = async (opts: NDKEventFigure<{ data: NostrEventComment; }>): Promise<NDKEvent | undefined> => { + const { ndk, ndk_user, data } = opts; + return await ndk_event({ + ndk, + ndk_user, + basis: { + kind: NDKKind.GenericReply, + content: data.content, + tags: tags_comment(data) + }, + }); +}; +\ No newline at end of file diff --git a/utils-nostr/src/events/comment/parse.ts b/utils-nostr/src/events/comment/parse.ts @@ -0,0 +1,15 @@ +import { NDKEvent } from "@nostr-dev-kit/ndk"; +import { nostr_event_comment_schema } from "../../schemas/lib.js"; +import { NostrEventComment } from "../../types/lib.js"; + +export const parse_nostr_comment_event = (event: NDKEvent): NostrEventComment | undefined => { + if (!event || typeof event.content !== 'string' || event.kind !== 1111) return undefined; + + try { + const parsed = JSON.parse(event.content); + const result = nostr_event_comment_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 @@ -4,9 +4,7 @@ import { NDKEventFigure } from "../../types/ndk"; import { tags_classified } from "../../utils/tags.js"; import { ndk_event } from "../lib.js"; -export const ndk_event_classified = async (opts: NDKEventFigure<{ - data: NostrEventListing; -}>): Promise<NDKEvent | undefined> => { +export const ndk_event_classified = async (opts: NDKEventFigure<{ data: NostrEventListing; }>): Promise<NDKEvent | undefined> => { const { ndk, ndk_user, data } = opts; return await ndk_event({ ndk, diff --git a/utils-nostr/src/events/subscription.ts b/utils-nostr/src/events/subscription.ts @@ -1,11 +1,13 @@ import { NDKEvent } from "@nostr-dev-kit/ndk"; -import { NostrEventListing, type NostrEventMetadata } from "../types/lib.js"; +import { NostrEventComment, NostrEventListing, type NostrEventMetadata } from "../types/lib.js"; +import { parse_nostr_comment_event } from "./comment/parse.js"; import { parse_nostr_listing_event } from "./listing/parse.js"; import { parse_nostr_metadata_event } from "./metadata/parse.js"; export type NdkEventPayload = | { kind: 0; metadata: NostrEventMetadata; } | { kind: 30402; listing: NostrEventListing; } + | { kind: 1111; listing: NostrEventComment; } export const on_ndk_event = (event: NDKEvent): NdkEventPayload | undefined => { if (!event || typeof event.kind !== 'number') return undefined; @@ -21,6 +23,11 @@ export const on_ndk_event = (event: NDKEvent): NdkEventPayload | undefined => { if (!data) return; return { kind: event.kind, listing: data }; }; + case 1111: { + const data = parse_nostr_comment_event(event); + if (!data) return; + return { kind: event.kind, listing: data }; + }; default: return undefined; } }; diff --git a/utils-nostr/src/schemas/lib.ts b/utils-nostr/src/schemas/lib.ts @@ -108,4 +108,18 @@ export const nostr_event_listing_schema = z.object({ location: nostr_tag_location_schema.optional(), images: z.array(nostr_tag_image_schema).optional(), client: nostr_tag_client_schema.optional() +}); + +export const nostr_event_referenced_schema = z.object({ + id: z.string().regex(/^[0-9a-f]{64}$/i, "expected 64-char hex id"), + kind: z.number().int().nonnegative(), + author: z.string().regex(/^[0-9a-f]{64}$/i, "expected 64-char hex pubkey"), + relays: z.array(z.url()).nonempty().optional(), + d_tag: z.string().min(1).optional(), +}); + +export const nostr_event_comment_schema = z.object({ + root_event: nostr_event_referenced_schema, + ref_event: nostr_event_referenced_schema.optional(), + content: z.string().min(1), }); \ No newline at end of file diff --git a/utils-nostr/src/types/lib.ts b/utils-nostr/src/types/lib.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { nostr_event_listing_schema, nostr_event_metadata_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_listing_schema, nostr_event_metadata_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"; export type NostrEventMetadata = z.infer<typeof nostr_event_metadata_schema>; export type NostrEventListing = z.infer<typeof nostr_event_listing_schema> @@ -10,6 +10,8 @@ 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 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 { NostrEventListing, NostrEventTag, NostrEventTagClient, NostrEventTagImage, NostrEventTagLocation, NostrEventTagPrice, NostrEventTagPriceDiscount, NostrEventTagQuantity, NostrEventTags } from "../types/lib.js"; +import { NostrEventComment, NostrEventListing, NostrEventTag, NostrEventTagClient, NostrEventTagImage, NostrEventTagLocation, NostrEventTagPrice, NostrEventTagPriceDiscount, NostrEventTagQuantity, NostrEventTags } from "../types/lib.js"; export const tag_client = (opts: NostrEventTagClient, d_tag?: string): NostrEventTag => { const tag = [`client`, opts.name]; @@ -73,3 +73,38 @@ export const tags_classified = (opts: NostrEventListing): NostrEventTags => { if (opts.images) for (const image_tags of opts.images) tags.push(tag_classified_image(image_tags)); return tags; }; + +export const tags_comment = (opts: NostrEventComment): NostrEventTags => { + const { root_event, ref_event } = opts; + + const root = { + kind: root_event.kind.toString(), + author: root_event.author, + id: root_event.id, + d_tag: root_event.d_tag, + relays: root_event.relays || [], + }; + + const parent = (ref_event && ref_event.id) + ? { + kind: ref_event.kind.toString(), + author: ref_event.author, + id: ref_event.id, + d_tag: ref_event.d_tag, + relays: ref_event.relays || [], + } + : root; + + const tags: NostrEventTags = [ + ["E", root.id, ...root.relays], + ["P", root.author], + ["K", root.kind], + ...(root.d_tag ? [["A", `${root.kind}:${root.author}:${root.d_tag}`, ...root.relays]] : []), + ["e", parent.id, ...parent.relays], + ["p", parent.author], + ["k", parent.kind], + ...(parent.d_tag ? [["a", `${parent.kind}:${parent.author}:${parent.d_tag}`, ...parent.relays]] : []), + ]; + + return tags; +}; +\ No newline at end of file