web_lib

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

commit dbba3a227450d23e14f847359d4bf02322703894
parent 015ca7057c72e7e067631f1836ae5fe6f45fcfc2
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Fri,  6 Dec 2024 05:26:41 +0000

utils: add/edit utils, update packages

Diffstat:
Mutils/package.json | 4++--
Mutils/src/currency.ts | 13++++++++-----
Mutils/src/file.ts | 9++++++---
Mutils/src/geolocation.ts | 13+++++++------
Mutils/src/index.ts | 3++-
Mutils/src/list.ts | 13++++++-------
Mutils/src/nostr/geotags.ts | 2+-
Mutils/src/nostr/lib.ts | 43+++++++++++++++++++++----------------------
Autils/src/nostr/lib2.ts | 147+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mutils/src/nostr/ndk.ts | 22++++++++++++++++++++++
Mutils/src/nostr/types.ts | 15+++++++++++++++
Mutils/src/object.ts | 2+-
Mutils/src/regex.ts | 1+
Mutils/src/trade.ts | 2+-
Mutils/src/units.ts | 2+-
15 files changed, 241 insertions(+), 50 deletions(-)

diff --git a/utils/package.json b/utils/package.json @@ -20,9 +20,9 @@ "dependencies": { "@noble/curves": "^1.6.0", "@noble/hashes": "^1.4.0", - "@nostr-dev-kit/ndk": "^2.10.0", + "@nostr-dev-kit/ndk": "^2.10.6", "convert": "^5.5.1", - "ngeohash": "0.6.3", + "geohashing": "^2.0.1", "nostr-geotags": "^0.7.1", "nostr-tools": "^2.7.2", "uuid": "^10.0.0", diff --git a/utils/src/currency.ts b/utils/src/currency.ts @@ -108,10 +108,12 @@ export const parse_currency_price = (locale: string, currency: string, price: nu const cur_pos = fmt_amt.startsWith(cur_m); const dec_s = locale_fractional_decimal(locale); const [_val_i, _val_f] = fmt_num.split(dec_s); - const val_i = _val_i.replace(regex.nbsp_rp, ``).replace(regex.rtlm_rp, ``); - const val_f = _val_f.replace(regex.nbsp_rp, ``).replace(regex.rtlm_rp, ``) - const num_i = parseInt(val_i.replace(dec_s === `,` ? regex.periods : regex.commas, ``)); - const num_f = parseInt(val_f); + const val_i = _val_i?.replace(regex.nbsp_rp, ``).replace(regex.rtlm_rp, ``); + const val_f = _val_f?.replace(regex.nbsp_rp, ``).replace(regex.rtlm_rp, ``); + if (!val_i || !val_f) return undefined; + const num_i = Math.max(Math.min(parseInt(val_i?.replace(dec_s === `,` ? regex.periods : regex.commas, ``) || `0`), 0)); + const num_f = Math.max(Math.min(parseInt(val_f || `0`), 0)); + return { cur, cur_s, @@ -134,7 +136,7 @@ export const sum_currency_price = (currency_price: CurrencyPrice): number => { return currency_price.num_i + (currency_price.num_f / 100); }; -export const parse_currency_price_fmt = (locale: string, _currency: string, amount: number): CurrencyPriceFmt => { +export const parse_currency_price_fmt = (locale: string, _currency: string, amount: number): CurrencyPriceFmt | undefined => { const currency = parse_currency(_currency); const fmt = new Intl.NumberFormat(locale, { style: 'currency', @@ -143,6 +145,7 @@ export const parse_currency_price_fmt = (locale: string, _currency: string, amou }); const fmt_amt = fmt.format(amount); const [symbol_val_i, val_f] = fmt_amt.split('.'); + if (!symbol_val_i || !val_f) return undefined; return [symbol_val_i.charAt(0), currency, Number(symbol_val_i.replaceAll(`,`, ``).slice(1)), Number(val_f)]; }; diff --git a/utils/src/file.ts b/utils/src/file.ts @@ -2,8 +2,11 @@ import type { FileBytesFormat, FilePath } from "./types"; export const parse_file_name = (file_path: string): string => { const file_path_dirs = file_path.split(`/`); - if (file_path_dirs.length > 0) return file_path_dirs[file_path_dirs.length - 1]; - return `` + if (file_path_dirs.length) { + const res = file_path_dirs[file_path_dirs.length - 1]; + if (res) return res; + }; + return ``; }; export const format_file_bytes = (num_bytes: number, format: FileBytesFormat): string => { @@ -26,7 +29,7 @@ export const format_file_bytes = (num_bytes: number, format: FileBytesFormat): s export const parse_file_path = (file_path: string): FilePath | undefined => { const file_path_spl = file_path.split(`/`); - const file_path_file = file_path_spl[file_path_spl.length - 1]; + const file_path_file = file_path_spl[file_path_spl.length - 1] || ``; const [file_name, mime_type] = file_path_file.split(`.`); if (!file_name || !mime_type) return undefined; return { diff --git a/utils/src/geolocation.ts b/utils/src/geolocation.ts @@ -1,4 +1,5 @@ -import * as ngeohash from "ngeohash"; +//import * as ngeohash from "ngeohash"; +import { decodeBase32, encodeBase32 } from "geohashing"; import type { LocationPoint } from "./types"; export type GeolocationCoordinatesPoint = { @@ -10,14 +11,14 @@ export const geohash_encode = (opts: { lat: string | number; lng: string | number; }): string => { - const geohash = ngeohash.encode(opts.lat, opts.lng); + const lat = typeof opts.lat === `string` ? parseFloat(opts.lat) : opts.lat; + const lng = typeof opts.lng === `string` ? parseFloat(opts.lng) : opts.lng; + const geohash = encodeBase32(lat, lng); return geohash; }; -export const geohash_decode = (opts: { - geohash: string; -}): LocationPoint => { - const { latitude: lat, longitude: lng, error: { latitude: lat_err, longitude: lng_err } } = ngeohash.decode(opts.geohash); +export const geohash_decode = (geohash: string): LocationPoint => { + const { lat, lng, error: { lat: lat_err, lng: lng_err } } = decodeBase32(geohash); return { lat, lng, diff --git a/utils/src/index.ts b/utils/src/index.ts @@ -1,4 +1,3 @@ - export * from "./currency" export * from "./error" export * from "./file" @@ -7,7 +6,9 @@ export * from "./geolocation" export * from "./hash" export * from "./list" export * from "./math" +export * from "./nostr/geotags" export * from "./nostr/lib" +export * from "./nostr/lib2" export * from "./nostr/ndk" export * from "./nostr/types" export * from "./object" diff --git a/utils/src/list.ts b/utils/src/list.ts @@ -6,13 +6,12 @@ export const list_remove_index = <T>(array: T[], index: number): T[] => { export const list_move_index = <T>(array: T[], index_start: number, index_end: number): T[] => { if (index_start < 0 || index_start >= array.length || index_end < 0 || index_end >= array.length) throw new Error(`Index out of bounds`); const item = array[index_start]; - const newArray = array.filter((_, index) => index !== index_start); - const adjustedIndexEnd = index_end > index_start ? index_end - 1 : index_end; - return [ - ...newArray.slice(0, adjustedIndexEnd), - item, - ...newArray.slice(adjustedIndexEnd) - ]; + const arr_new = array.filter((_, index) => index !== index_start); + const adjusted_final_index = index_end > index_start ? index_end - 1 : index_end; + const arr_res: T[] = arr_new.slice(0, adjusted_final_index); + if (item) arr_res.push(item) + arr_res.push(...arr_new.slice(adjusted_final_index)) + return arr_res; }; export const list_assign = (list_curr: string[], list_new: string[]): string[] => { diff --git a/utils/src/nostr/geotags.ts b/utils/src/nostr/geotags.ts @@ -1,5 +1,5 @@ import ngeotags, { type GeoTags, type InputData, type Options } from 'nostr-geotags'; -import { NostrTagLocation } from './types'; +import type { NostrTagLocation } from './types'; const options: Options = { geohash: true, diff --git a/utils/src/nostr/lib.ts b/utils/src/nostr/lib.ts @@ -58,24 +58,26 @@ export const fmt_tag_quantity = (opts: NostrTagQuantity): string[] => { 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.region_code && !isNaN(parseInt(opts.region_code))) tag.push(opts.region_code); + else if (opts.region) tag.push(opts.region); //@todo if (opts.country_code) tag.push(opts.country_code); return tag; }; export const fmt_tag_image = (opts: NostrTagMediaUpload): string[] => { - const tag = ["image", opts.url]; + const tag = [`image`, opts.url]; if (opts.size) tag.push(`${opts.size.w}x${opts.size.h}`) return tag; }; -export const fmt_tag_client = (opts: NostrTagClient, d_tag: string): string[] => { - const tag = [`client`, opts.name, `31990:${opts.pubkey}:${d_tag}`, opts.relay] +export const fmt_tag_client = (opts: NostrTagClient, d_tag?: string): string[] => { + const tag = [`client`, opts.name]; + if (d_tag) tag.push(`31990:${opts.pubkey}:${d_tag}`); + tag.push(opts.relay); return tag; }; - -export const fmt_tags_basis_nip99 = async (opts: { +export const fmt_tags_basis_nip99 = (opts: { d_tag: string; listing: NostrTagListing; quantity: NostrTagQuantity; @@ -83,22 +85,19 @@ export const fmt_tags_basis_nip99 = async (opts: { location: NostrTagLocation; images?: NostrTagMediaUpload[]; client?: NostrTagClient; -}): Promise<string[][] | undefined> => { - try { - const tags: string[][] = [ - [`d`, opts.d_tag], - ]; - if (opts.client) tags.push(fmt_tag_client(opts.client, opts.d_tag)); - 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); - } +}): string[][] => { + const { d_tag, listing, quantity, price, location } = opts; + const tags: string[][] = [ + [`d`, d_tag] + ]; + if (opts.client) tags.push(fmt_tag_client(opts.client, opts.d_tag)); + for (const [k, v] of Object.entries(listing)) if (v) tags.push([k, v]); + tags.push(fmt_tag_quantity(quantity)); + tags.push(fmt_tag_price(price)); + tags.push(fmt_tag_location(location)); + if (opts.images) for (const image of opts.images) tags.push(fmt_tag_image(image)); + tags.push(...fmt_tag_geotags(location)); + return tags; }; export const nostr_event_sign = (opts: { diff --git a/utils/src/nostr/lib2.ts b/utils/src/nostr/lib2.ts @@ -0,0 +1,147 @@ +import { bytesToHex, hexToBytes } from '@noble/hashes/utils'; +import { generateSecretKey, getPublicKey, nip19 } from 'nostr-tools'; + +export class ClientNostrLib2 { + private generate_key_bytes(): Uint8Array { + const secret_key = generateSecretKey(); + return secret_key; + }; + + private get_key_hex(bytes: Uint8Array): string { + const hex = bytesToHex(bytes); + return hex; + }; + + private get_key_bytes(hex: string): Uint8Array { + const bytes = hexToBytes(hex); + return bytes; + }; + + /** + * + * @returns nostr secret key hex + */ + public generate_key(): string { + const bytes = this.generate_key_bytes(); + const hex = this.get_key_hex(bytes); + return hex; + }; + + /** + * + * @returns nostr public key hex + */ + public public_key(secret_key_hex: string | undefined): string { + try { + if (!secret_key_hex) return ``; + const bytes = this.get_key_bytes(secret_key_hex); + const hex = getPublicKey(bytes) + return hex; + } catch (e) { + return `` + } + } + + /** + * + * @returns nostr secret key to public key hex + */ + public publickey_decode(secret_key_hex?: string): string | undefined { + try { + if (secret_key_hex) { + return getPublicKey(this.get_key_bytes(secret_key_hex)) + } + return undefined; + } catch (e) { + return undefined; + } + } + + /** + * + * @returns nostr public key npub + */ + public npub(public_key_hex: string | undefined, fallback_to_hex?: boolean): string { + if (!public_key_hex) return ``; + const npub = nip19.npubEncode(public_key_hex); + return npub ? npub : fallback_to_hex ? public_key_hex : ``; + } + + /** + * + * @returns public key hex from npub + */ + public npub_decode(npub: string): string { + const decode = nip19.decode(npub); + console.log(`decode `, decode) + if (decode && decode.type === `npub` && decode.data) return decode.data + return ``; + } + + /** + * + * @returns nostr secret key nsec + */ + public nsec(secret_key_hex: string | undefined): string { + if (!secret_key_hex) return ``; + const bytes = this.get_key_bytes(secret_key_hex); + const nsec = nip19.nsecEncode(bytes); + return nsec; + } + + /** + * + * @returns nostr secret key hex from nsec + */ + public nsec_decode(nsec: string): string | undefined { + try { + if (!nsec) return undefined; + const decode = nip19.decode(nsec); + if (decode && decode.type === `nsec` && decode.data) return bytesToHex(decode.data); + return undefined; + } catch (e) { + return undefined; + } + } + + /** + * + * @returns + */ + public nevent(event_pointer: nip19.EventPointer, relays: string[]): string { + return nip19.neventEncode(event_pointer) + } + + /** + * + * @returns nostr public key nprofile + */ + public nprofile(public_key_hex: string, relays: string[]): string { + if (!public_key_hex || !relays.length) return ``; + return nip19.nprofileEncode({ pubkey: public_key_hex, relays }) + } + + /** + * + * @returns nostr public key nprofile + */ + public nprofile_decode(nprofile: string): [string, string[]] | undefined { + if (!nprofile) return undefined; + const decode = nip19.decode(nprofile); + if (decode && decode.type === `nprofile` && decode.data && decode.data.pubkey && decode.data.relays) return [decode.data.pubkey, decode.data.relays] + return undefined; + } + + /** + * + * @returns + */ + public secretkey_to_publickey(nsec_or_hex: string): string | undefined { + if (nsec_or_hex.startsWith(`nsec1`)) { + return this.nsec_decode(nsec_or_hex); + } else if (nsec_or_hex.length === 64) { + return this.publickey_decode(nsec_or_hex) + } + return undefined; + } +}; diff --git a/utils/src/nostr/ndk.ts b/utils/src/nostr/ndk.ts @@ -1,5 +1,6 @@ import NDK, { NDKEvent, NDKPrivateKeySigner, NDKUser } from '@nostr-dev-kit/ndk'; import { time_now_ms } from '../time'; +import type { NostrMetadataTmp } from './types'; export const ndk_init = async (opts: { $ndk: NDK; @@ -20,6 +21,27 @@ export const ndk_init = async (opts: { }; }; +export const ndk_event_metadata = async (opts: { + $ndk: NDK; + $ndk_user: NDKUser; + metadata: NostrMetadataTmp +}): Promise<NDKEvent | undefined> => { + try { + const { $ndk, $ndk_user } = opts; + const ev = await ndk_event({ + $ndk, + $ndk_user, + basis: { + kind: 0, + content: JSON.stringify(opts.metadata), + }, + }); + return ev; + } catch (e) { + console.log(`(error) ndk_event_metadata `, e); + } +}; + export const ndk_event = async (opts: { $ndk: NDK; $ndk_user: NDKUser; diff --git a/utils/src/nostr/types.ts b/utils/src/nostr/types.ts @@ -14,7 +14,9 @@ export type NostrRelayInformationDocument = { export type NostrRelayInformationDocumentFormFields = { [K in keyof NostrRelayInformationDocument]: string; }; export type NostrTagListing = { + key: string; title: string; + category: string; summary?: string; process?: string; lot?: string; @@ -59,4 +61,17 @@ export type NostrTagClient = { name: string; pubkey: string; relay: string; +}; + +export type NostrMetadataTmp = { + name?: string; + display_name?: string; + about?: string; + website?: string; + picture?: string; + banner?: string; + nip05?: string; + lud06?: string; + lud16?: string; + bot?: boolean; }; \ No newline at end of file diff --git a/utils/src/object.ts b/utils/src/object.ts @@ -1,3 +1,3 @@ -export const obj_en = <KeyType extends string, ValType>(object: Record<string, ValType>, parse_function: (key: string) => KeyType): [KeyType, ValType][] => { +export const obj_en = <KeyType extends string, ValType>(object: Record<string, ValType>, parse_function: (key: string) => KeyType = (i) => i as 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 @@ -19,6 +19,7 @@ export const regex = { profile_name: /^[a-zA-Z0-9._]{3,30}$/, profile_name_ch: /[a-zA-Z0-9._]/, trade_product_key: /^(?:[a-zA-Z0-9]+(?:\s+[a-zA-Z0-9]+){0,2})$/, + trade_product_category: /^(?:[a-zA-Z0-9]+(?:\s+[a-zA-Z0-9]+){0,2})$/, currency_symbol: /(?:[A-Za-z]{3,5}\$|\p{Sc})/u, currency_marker: /(?:[A-Za-z]{2,4}[^\d\s]+|[^\d\s]{1,3}[A-Za-z]{2,4})/ ///(?:[A-Za-z]{2,4}\$|\$(?=[A-Za-z]{2,4}$))/ diff --git a/utils/src/trade.ts b/utils/src/trade.ts @@ -207,7 +207,7 @@ export function parse_trade_mass_tuple(val?: string): [number, MassUnit, string] const mass = vals[0]; const mass_unit = vals[1]; const label = vals[2]; - const amt = parseInt(mass, 10); + const amt = parseInt(mass || `0`, 10); if (isNaN(amt) || amt <= 0) return; const units = parse_mass_unit(mass_unit); if (!units) return; diff --git a/utils/src/units.ts b/utils/src/units.ts @@ -11,7 +11,7 @@ export const mass_units: MassUnit[] = [`kg`, `lb`, `g`] as const; export type MassUnit = z.infer<typeof MassUnitSchema>; -export function parse_mass_unit(val: string): MassUnit { +export function parse_mass_unit(val?: string): MassUnit { switch (val) { case `kg`: case `lb`: