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:
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`: