web_lib

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

commit 0d3e5cb6ec51d6f9dde82c8dd131d658a407f218
parent 32abad61ff9d20265bc932672c05e892490c087b
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Sun, 17 Nov 2024 07:55:06 +0000

utils: add/edit utils

Diffstat:
Mutils/package.json | 3+++
Mutils/src/error.ts | 15++++++++++++---
Mutils/src/file.ts | 1+
Autils/src/format.ts | 5+++++
Mutils/src/index.ts | 2++
Autils/src/nostr/geotags.ts | 22++++++++++++++++++++++
Mutils/src/nostr/lib.ts | 116++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mutils/src/nostr/types.ts | 35+++++++++++++++++++++++++++++++++++
Autils/src/object.ts | 4++++
Mutils/src/regex.ts | 5++++-
Mutils/src/types.ts | 5++---
11 files changed, 188 insertions(+), 25 deletions(-)

diff --git a/utils/package.json b/utils/package.json @@ -18,10 +18,13 @@ "access": "public" }, "dependencies": { + "@noble/curves": "^1.6.0", "@noble/hashes": "^1.4.0", "@nostr-dev-kit/ndk": "^2.10.0", "convert": "^5.5.1", "ngeohash": "0.6.3", + "nostr-geotags": "^0.7.1", + "nostr-tools": "^2.7.2", "uuid": "^10.0.0", "zod": "^3.23.8" } diff --git a/utils/src/error.ts b/utils/src/error.ts @@ -1,11 +1,19 @@ import type { ErrorMessage, ErrorResponse } from "./types"; -export const handle_error = (e: unknown, append?: string): ErrorResponse => { +export const handle_error = (e: unknown, append?: string): ErrorMessage<string> => { const msg = (e as Error).message ? (e as Error).message : String(e); - const error = `${msg}${append ? ` ${append}` : ``}`; - return { error }; + const err = `${msg}${append ? ` ${append}` : ``}`; + return { err }; }; export const err_msg = <T extends string>(err: T): ErrorMessage<T> => { return { err }; }; + +export const err_res = <T extends object>(error: T): ErrorResponse<T> => { + return { error }; +}; + +export const err_system = (message: string): boolean => { + return message.split(` `).length > 1 +}; +\ No newline at end of file diff --git a/utils/src/file.ts b/utils/src/file.ts @@ -30,6 +30,7 @@ export const parse_file_path = (file_path: string): FilePath | undefined => { const [file_name, mime_type] = file_path_file.split(`.`); if (!file_name || !mime_type) return undefined; return { + file_path, file_name, mime_type }; diff --git a/utils/src/format.ts b/utils/src/format.ts @@ -0,0 +1,4 @@ +export const fmt_plural_agreement = (num: number = 0, val_s: string, val_pl: string): string => { + if (num === 1) return val_s; + return val_pl; +}; +\ No newline at end of file diff --git a/utils/src/index.ts b/utils/src/index.ts @@ -2,6 +2,7 @@ export * from "./currency" export * from "./error" export * from "./file" +export * from "./format" export * from "./geolocation" export * from "./hash" export * from "./list" @@ -9,6 +10,7 @@ export * from "./math" export * from "./nostr/lib" export * from "./nostr/ndk" export * from "./nostr/types" +export * from "./object" export * from "./regex" export * from "./time" export * from "./trade" diff --git a/utils/src/nostr/geotags.ts b/utils/src/nostr/geotags.ts @@ -0,0 +1,21 @@ +import ngeotags, { type GeoTags, type InputData, type Options } from 'nostr-geotags'; +import { NostrTagLocation } from './types'; + +const options: Options = { + geohash: true, + gps: true, + city: true, + iso31662: true, +}; + +export const fmt_tag_geotags = (opts: NostrTagLocation): GeoTags[] => { + const data: InputData = { + lat: opts.lat, + lon: opts.lng, + city: opts.city, + regionName: opts.region, + countryName: opts.country, + countryCode: opts.country_code + }; + return ngeotags(data, options) +}; +\ No newline at end of file diff --git a/utils/src/nostr/lib.ts b/utils/src/nostr/lib.ts @@ -1,4 +1,9 @@ -import type { NostrRelayInformationDocument, NostrRelayInformationDocumentFormFields } from "./types"; +import { schnorr } from '@noble/curves/secp256k1'; +import { hexToBytes } from '@noble/hashes/utils'; +import { type EventTemplate, finalizeEvent, getEventHash, type NostrEvent } from "nostr-tools"; +import { uuidv4 } from '../uuid'; +import { fmt_tag_geotags } from './geotags'; +import type { NostrRelayInformationDocument, NostrRelayInformationDocumentFormFields, NostrTagListing, NostrTagLocation, NostrTagPrice, NostrTagQuantity } from "./types"; export const parse_nostr_relay_information_document = (data: any): NostrRelayInformationDocument | undefined => { const obj = JSON.parse(data); @@ -36,31 +41,106 @@ export const parse_nostr_relay_information_document_fields = (data: any): NostrR return result; }; + +export const fmt_tag_price = (opts: NostrTagPrice): string[] => { + const tag = [`price`, opts.amt, opts.currency, opts.qty_amt, opts.qty_unit]; + return tag; +}; + + +export const fmt_tag_quantity = (opts: NostrTagQuantity): string[] => { + const tag = [`quantity`, opts.amt, opts.unit]; + if (opts.label) tag.push(opts.label); + return tag; +}; + + +export const fmt_tag_location = (opts: NostrTagLocation): string[] => { + const tag = [`location`]; + if (opts.city) tag.push(opts.city); + if (opts.region_code) tag.push(opts.region_code); + if (opts.country_code) tag.push(opts.country_code); + return tag; +}; + +type NostrTagMediaUpload = { + url: string; + size?: { + w: number; + h: number; + }; +}; +export const fmt_tag_image = (opts: NostrTagMediaUpload): string[] => { + const tag = ["image", opts.url]; + if (opts.size) tag.push(`${opts.size.w}x${opts.size.h}`) + return tag; +}; + export const fmt_tags_basis_nip99 = async (opts: { d_tag: string; - title: string; - image?: { - url: string; - size: string; - }; - summary?: string; - location?: string; - price?: { - amt: string; - currency: string; - }; + listing: NostrTagListing; + quantity: NostrTagQuantity; + price: NostrTagPrice; + location: NostrTagLocation; + images?: NostrTagMediaUpload[]; }): Promise<string[][] | undefined> => { try { const tags: string[][] = [ - ["d", opts.d_tag], - ["title", opts.title], + [`d`, opts.d_tag], ]; - if (opts.image) tags.push(["image", opts.image.url, opts.image.size],) - if (opts.summary) tags.push(["summary", opts.summary]) - if (opts.location) tags.push(["location", opts.location]) - if (opts.price) tags.push(["price", opts.price.amt, opts.price.currency]) + for (const [k, v] of Object.entries(opts.listing)) if (v) tags.push([k, v]); + tags.push(fmt_tag_quantity(opts.quantity)); + tags.push(fmt_tag_price(opts.price)); + tags.push(fmt_tag_location(opts.location)); + if (opts.images) for (const image of opts.images) tags.push(fmt_tag_image(image)); + tags.push(...fmt_tag_geotags(opts.location)) return tags; } catch (e) { console.log(`(error) fmt_tags_nip99 `, e); } +}; + +export const nostr_event_sign = (opts: { + secret_key: string; + event: EventTemplate; +}): NostrEvent => { + return finalizeEvent(opts.event, hexToBytes(opts.secret_key)) +}; + +export const nostr_event_sign_attest = (secret_key: string): NostrEvent => { + return nostr_event_sign({ + secret_key, + event: { + kind: 1, + created_at: Math.floor(Date.now() / 1000), + tags: [], + content: uuidv4(), + }, + }); +}; + + +export const nostr_event_verify = async (event: NostrEvent): Promise<boolean> => { + try { + const hash = getEventHash(event); + console.log(`hash `, hash) + if (hash !== event.id) return false + const valid = schnorr.verify(event.sig, hash, event.pubkey); + return valid; + } catch { + return false; + } +}; + +export const nostr_event_verify_serialized = async (event_serialized: string): Promise<boolean> => { + try { + const event = JSON.parse(event_serialized); + const hash = getEventHash(event); + console.log(`hash `, hash) + if (hash !== event.id) return false + const valid = schnorr.verify(event.sig, hash, event.pubkey); + return valid; + } catch { + return false; + } }; \ No newline at end of file diff --git a/utils/src/nostr/types.ts b/utils/src/nostr/types.ts @@ -12,3 +12,37 @@ export type NostrRelayInformationDocument = { } export type NostrRelayInformationDocumentFormFields = { [K in keyof NostrRelayInformationDocument]: string; }; + +export type NostrTagListing = { + title: string; + summary?: string; + process?: string; + lot?: string; + location?: string; + profile?: string; + year?: string; +}; + +export type NostrTagPrice = { + amt: string; + currency: string; + qty_amt: string; + qty_unit: string; +}; + +export type NostrTagQuantity = { + amt: string; + unit: string; + label?: string; +}; + +export type NostrTagLocation = { + city?: string; + region?: string; + region_code?: string; + country?: string; + country_code?: string; + lat: number; + lng: number; + geohash: string; +}; +\ No newline at end of file diff --git a/utils/src/object.ts b/utils/src/object.ts @@ -0,0 +1,3 @@ +export const obj_en = <KeyType extends string, ValType>(object: Record<string, ValType>, parse_function: (key: string) => KeyType): [KeyType, ValType][] => { + return Object.entries(object).map<[KeyType, ValType]>(([k, v]) => [parse_function(k), v]) +}; +\ No newline at end of file diff --git a/utils/src/regex.ts b/utils/src/regex.ts @@ -1,4 +1,6 @@ export const regex = { + description: /^(?:\S+(?:\s+\S+)*)$/, + description_ch: /[^a-zA-Z0-9.,!?;:'"(){}[]\s\u0600-\u06FF\u0900-\u097F\u0400-\u04FF\u0500-\u052F\u1F00-\u1FFF\u4E00-\u9FFF\uAC00-\uD7AF\u3040-\u309F\u30A0-\u30FF ]+/, nbsp: /[\u00A0]/g, nbsp_rp: /[\u00A0]+/g, rtlm: /[\u200F]/g, @@ -10,7 +12,8 @@ export const regex = { alpha_ch: /[a-zA-Z ]$/, num: /^[0-9]+$/, alphanum: /[a-zA-Z0-9., ]$/, - alphanum_ch: /[a-zA-Z0-9., ]/, + alphanum_ch: /[a-zA-Z0-9.,\s\u0600-\u06FF\u0900-\u097F\u0400-\u04FF\u0500-\u052F\u1F00-\u1FFF\u4E00-\u9FFF\uAC00-\uD7AF\u3040-\u309F\u30A0-\u30FF ]+/, + ///[a-zA-Z0-9., ]/, price: /^\d+(\.\d+)?$/, price_ch: /[0-9.]$/, profile_name: /^[a-zA-Z0-9._]{3,30}$/, diff --git a/utils/src/types.ts b/utils/src/types.ts @@ -1,6 +1,6 @@ import type { GeolocationCoordinatesPoint } from "./geolocation"; -export type ErrorResponse = { error: string; }; +export type ErrorResponse<T extends object> = { error: T; }; export type ErrorMessage<T extends string> = { err: T }; export type ResultId = { id: string; }; @@ -20,4 +20,4 @@ export type NumberTuple = [number, number]; export type FileBytesFormat = `kb` | `mb` | `gb`; export type FileMimeType = string; -export type FilePath = { file_name: string; mime_type: FileMimeType; } -\ No newline at end of file +export type FilePath = { file_path: string; file_name: string; mime_type: FileMimeType; }