web_lib

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

commit 3088bafce40464c5445e15ce0ed89f2245bf90cc
parent 549e0db7c7d60a2746ad72ce1bc9bff3ab0be8b4
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Thu,  7 Aug 2025 17:35:12 +0000

utils: restructure library modules. update build process to use tsc with targets for esm and cjs outputs

Diffstat:
Mutils/.gitignore | 8++++++--
Mutils/package.json | 36+++++++++++++++++-------------------
Dutils/src/*regex.ts | 141-------------------------------------------------------------------------------
Dutils/src/*validation.ts | 56--------------------------------------------------------
Dutils/src/_env.ts | 3---
Dutils/src/app/config.ts | 31-------------------------------
Dutils/src/app/glyph.ts | 131-------------------------------------------------------------------------------
Dutils/src/app/lib.ts | 51---------------------------------------------------
Dutils/src/app/search.ts | 72------------------------------------------------------------------------
Dutils/src/app/styles.ts | 28----------------------------
Dutils/src/app/util.ts | 163-------------------------------------------------------------------------------
Dutils/src/browser.ts | 92-------------------------------------------------------------------------------
Dutils/src/client/datastore.ts | 24------------------------
Dutils/src/client/geo.ts | 12------------
Dutils/src/client/gui.ts | 23-----------------------
Dutils/src/config.ts | 20--------------------
Dutils/src/currency.ts | 47-----------------------------------------------
Dutils/src/error.ts | 25-------------------------
Autils/src/errors/lib.ts | 17+++++++++++++++++
Dutils/src/geo.ts | 278-------------------------------------------------------------------------------
Dutils/src/http.ts | 110-------------------------------------------------------------------------------
Dutils/src/i18n.ts | 52----------------------------------------------------
Dutils/src/image.ts | 22----------------------
Mutils/src/index.ts | 35++---------------------------------
Dutils/src/lib.ts | 68--------------------------------------------------------------------
Dutils/src/list.ts | 2--
Dutils/src/listings/order.ts | 145-------------------------------------------------------------------------------
Dutils/src/model.ts | 129-------------------------------------------------------------------------------
Dutils/src/models/farm.ts | 20--------------------
Dutils/src/models/location.ts | 8--------
Dutils/src/number.ts | 13-------------
Dutils/src/object.ts | 18------------------
Dutils/src/response.ts | 33---------------------------------
Dutils/src/time.ts | 6------
Dutils/src/trade.ts | 234-------------------------------------------------------------------------------
Dutils/src/types.ts | 42------------------------------------------
Autils/src/types/lib.ts | 18++++++++++++++++++
Dutils/src/unit.ts | 56--------------------------------------------------------
Dutils/src/uuid.ts | 6------
Autils/tsconfig.base.json | 11+++++++++++
Autils/tsconfig.cjs.json | 10++++++++++
Autils/tsconfig.esm.json | 12++++++++++++
Dutils/tsconfig.json | 29-----------------------------
Dutils/tsup.config.ts | 11-----------
44 files changed, 93 insertions(+), 2255 deletions(-)

diff --git a/utils/.gitignore b/utils/.gitignore @@ -43,5 +43,8 @@ vite.config.ts.timestamp-* .vscode notes*.txt notes*.md -git-diff.txt -justfile +notes*.json +git-diff*.txt +prompt*.txt +tree*.txt +justfile +\ No newline at end of file diff --git a/utils/package.json b/utils/package.json @@ -1,28 +1,32 @@ { - "name": "@radroots/util", - "version": "0.0.0", + "name": "@radroots/utils", + "version": "0.0.1", "private": true, - "license": "GPLv3", "type": "module", - "main": "dist/index.js", - "module": "dist/index.js", - "types": "dist/index.d.ts", + "main": "./dist/cjs/index.cjs", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", "exports": { ".": { - "import": "./dist/index.js", - "require": "./dist/index.cjs" + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js" } }, "scripts": { - "build": "tsup", - "dev": "tsup --watch", + "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", + "clean": "rimraf dist && rimraf tsconfig.tsbuildinfo", + "dev": "npm run watch", "watch": "tsc -w" }, "devDependencies": { "@types/ngeohash": "0.6.8", "@types/node": "^22.13.1", "@types/uuid": "^10.0.0", - "tsup": "^6.2.3", + "rimraf": "^6.0.1", "typescript": "5.8.3" }, "publishConfig": { @@ -31,16 +35,10 @@ "dependencies": { "@noble/curves": "^1.6.0", "@noble/hashes": "^1.4.0", - "@nostr-dev-kit/ndk": "^2.11.0", - "@sveltekit-i18n/base": "^1.3.7", - "@sveltekit-i18n/parser-icu": "^1.0.8", "@radroots/radroots-common-bindings": "*", "convert": "^5.5.1", "geohashing": "^2.0.1", - "nostr-geotags": "*", - "nostr-tools": "^2.10.4", - "sveltekit-i18n": "^2.4.2", - "uuid": "^10.0.0", - "zod": "^3.23.8" + "uuid": "^11.1.0", + "zod": "^4.0.5" } } \ No newline at end of file diff --git a/utils/src/*regex.ts b/utils/src/*regex.ts @@ -1,141 +0,0 @@ -export const util_rxp = { - product_key: /^[A-Za-z_]+$/, - product_key_ch: /^[A-Za-z_]$/, - product_title: /[A-Za-z0-9 ]+$/, - product_title_ch: /[A-Za-z0-9 ]$/, - float: /^[+-]?(\d+(\.\d*)?|\.\d+)$/, - float_ch: /^[0-9\.\+\-]$/, - float_pos: /^\d+(\.\d+)?$/, - float_pos_ch: /^[0-9\.]$/, - 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, - rtlm_rp: /[\u200F]+/g, - commas: /[,]+/g, - periods: /[.]+/g, - word_only: /^[a-zA-Z]+$/, - alpha: /[a-zA-Z ]$/, - alpha_ch: /[a-zA-Z ]$/, - num: /^[0-9]+$/, - lat: /^[-+]?([1-8]?[0-9](\.\d{1,6})?|90(\.0{1,6})?)$/, - lat_ch: /^[\d\.\+\-]$/, - lng: /^[-+]?((1[0-7]?[0-9]|180)(\.\d{1,6})?|(\d{1,2})(\.\d{1,6})?)$/, - lng_ch: /^[\d\.\+\-]$/, - alphanum: /[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 ]+/, - price: /^\d+(\.\d+)?$/, - price_ch: /[0-9.]$/, - price_cur: /^[A-Za-z]{3}$/, - price_cur_ch: /[A-Za-z]$/, - 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})/, - ws_proto: /^(wss:\/\/|ws:\/\/)/, - quantity_unit: /^(kg|lb|g)$/, - quantity_unit_ch: /[A-Za-z]$/, - url_image_upload: /^file:\/\/.*\.(png|jpg|jpeg|gif|webp|bmp|svg)$/, - ///^blob:https:\/\/domain\.tld\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/, - url_image_upload_dev: /^file:\/\/.*\.(png|jpg|jpeg|gif|webp|bmp|svg)$/, - // /^blob:http:\/\/localhost:\d+\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/, - country_code_a2: /^[A-Za-z]{2}$/, - addr_primary: /[a-zA-Z0-9., ]$/, - addr_admin: /[a-zA-Z0-9., ]$/, - num_int: /^[0-9]$/, - area_unit: /^(ac|ha|ft2|m2)$/, - area_unit_ch: /[A-Za-z2]$/, -}; - -export type FormFieldsKey = - | `nostr_secret_key` - | `product_title` - | `product_key` - | `product_process` - | `product_description` - | `price` - | `price_currency` - | `quantity_unit` - | `quantity` - | `quantity_label` - | `farm_name` - | `farm_size` - | `area` - | `area_unit` - | `contact_name` - | `profile_name` - -export type FormField = { - validate: RegExp; - charset: RegExp; -}; - -export const form_fields: Record<FormFieldsKey, FormField> = { - profile_name: { - charset: util_rxp.profile_name_ch, - validate: util_rxp.profile_name, - }, - product_description: { - charset: util_rxp.alpha_ch, - validate: util_rxp.alpha, - }, - product_key: { - charset: util_rxp.product_key_ch, - validate: util_rxp.product_key, - }, - product_title: { - charset: util_rxp.product_title_ch, - validate: util_rxp.product_title, - }, - product_process: { - charset: util_rxp.alphanum_ch, - validate: util_rxp.alphanum, - }, - price: { - charset: util_rxp.price_ch, - validate: util_rxp.price, - }, - price_currency: { - charset: util_rxp.price_cur_ch, - validate: util_rxp.price_cur, - }, - quantity: { - charset: util_rxp.num, - validate: util_rxp.num, - }, - quantity_unit: { - charset: util_rxp.quantity_unit_ch, - validate: util_rxp.quantity_unit, - }, - quantity_label: { - charset: util_rxp.alphanum_ch, - validate: util_rxp.alphanum, - }, - area: { - charset: util_rxp.float_ch, - validate: util_rxp.float, - }, - area_unit: { - charset: util_rxp.area_unit_ch, - validate: util_rxp.area_unit, - }, - farm_name: { - charset: util_rxp.alpha_ch, - validate: util_rxp.alpha, - }, - farm_size: { - charset: util_rxp.num_int, - validate: util_rxp.num_int, - }, - contact_name: { - charset: util_rxp.alpha_ch, - validate: util_rxp.alpha, - }, - nostr_secret_key: { - charset: util_rxp.alpha_ch, - validate: util_rxp.alpha, - } -}; diff --git a/utils/src/*validation.ts b/utils/src/*validation.ts @@ -1,55 +0,0 @@ -import { GeocoderReverseResult, type GeolocationAddress, type GeolocationPoint, parse_int, util_rxp } from "$root"; -import { z } from "zod"; - -export const zf_area_unit = z.union([ - z.literal(`ac`), - z.literal(`ft2`), - z.literal(`ha`), - z.literal(`m2`), -]); - -export const zf_mass_unit = z.union([ - z.literal(`kg`), - z.literal(`lb`), - z.literal(`g`), -]); - -export const schema_geolocation_address: z.ZodSchema<GeolocationAddress> = z.object({ - primary: z.string().regex(util_rxp.addr_primary), - admin: z.string().regex(util_rxp.addr_admin), - country: z.string().regex(util_rxp.country_code_a2) -}); - -export const schema_geocode_result: z.ZodSchema<GeocoderReverseResult> = z.object({ - id: z.number(), - name: z.string(), - admin1_id: z.union([z.string(), z.number()]), - admin1_name: z.string(), - country_id: z.string(), - country_name: z.string(), - latitude: z.number(), - longitude: z.number(), -}); - -export const schema_geolocation_point: z.ZodSchema<GeolocationPoint> = z.object({ - lat: z.number().min(-90).max(90), - lng: z.number().min(-180).max(180), -}); - -export const zf_price_amount = z.preprocess((input) => { - return parse_int(String(input), 1.00); -}, z.number().positive().multipleOf(0.01)); - -export const zf_quantity_amount = z.preprocess((input) => { - return parse_int(String(input), 1); -}, z.number().int().positive()); - -export const zf_price = z.number().positive().multipleOf(0.01); - -export const zf_numi_pos = z.number().int().positive(); - -export const zf_numf_pos = z.number().positive(); - -export const zf_email = z.string().email(); - -export const zf_username = z.string().regex(util_rxp.profile_name); -\ No newline at end of file diff --git a/utils/src/_env.ts b/utils/src/_env.ts @@ -1,3 +0,0 @@ -export const _env = { - PROD: process.env.NODE_ENV === `production` -}; diff --git a/utils/src/app/config.ts b/utils/src/app/config.ts @@ -1,30 +0,0 @@ -import type { AppLayoutKey } from "$root"; - -type ConfigWindow = { - layout: Record<AppLayoutKey, { - h: number; - }>; - debounce: { - search: number; - } -}; - -export const cfg_app: ConfigWindow = { - layout: { - ios0: { - h: 600 - }, - ios1: { - h: 750 - }, - webm0: { - h: 600 - }, - webm1: { - h: 750 - } - }, - debounce: { - search: 200 - }, -}; -\ No newline at end of file diff --git a/utils/src/app/glyph.ts b/utils/src/app/glyph.ts @@ -1,131 +0,0 @@ -import type { GeometryCardinalDirection } from "$root"; - -export type GlyphKey = | - string | - `chat-text` | - `eye` | - `export` | - `video-camera` | - `device-mobile-camera` | - `crop` | - `map-trifold` | - `trash-simple` | - `backspace` | - `user-circle-check` | - `images-square` | - `bell` | - `columns` | - `bold` | - `article` | - `grid-four` | - `link-simple` | - `seal-check` | - `selection-foreground` | - `image-square` | - `image-broken` | - `funnel` | - `users-three` | - `note-blank` | - `user-circle-plus` | - `user-circle` | - `receipt` | - `invoice` | - `note` | - `arrow-left` | - `arrows-down-up` | - `basket` | - `arrow-right` | - `upload-simple` | - `printer` | - `download-simple` | - `list` | - `asterisk` | - `asterisk-simple` | - `subtitles-slash` | - `cardholder` | - `globe-x` | - `exclamation-mark` | - `network-x` | - `x-circle` | - `address-book-tabs` | - `paper-plane-tilt` | - `note-pencil` | - `share-fat` | - `folder` | - `trash` | - `plus-circle` | - `currency-${GlyphKeyCurrency}` | - `arrow-down` | - `caret-circle-down` | - `caret-circle-up` | - `shopping-bag-open` | - `coffee-bean` | - `compass` | - `map-pin-simple` | - `handbag-simple` | - `devices` | - `lock-key` | - `gear` | - `gear-fine` | - `bell-simple` | - `envelope` | - `house-line` | - `arrows-left-right` | - `list-plus` | - `squares-four` | - `list-plus` | - `app-window` | - `circle-notch` | - `subtract-square` | - `device-tablet-speaker` | - `weather-cloud` | - `warning` | - `circle-notch` | - `minus` | - `key` | - `arrow-u-up-left` | - `arrow-counter-clockwise` | - `circle` | - `check-circle` | - `circle-dashed` | - `dots-three` | - `cards-three` | - `lightning` | - `cards` | - `note-pencil` | - `tray` | - `calendar-dots` | - `notepad` | - `network` | - `calendar-blank` | - `chats-circle` | - `plant` | - `farm` | - `magnifying-glass` | - `chat-circle-dots` | - `dots-three-outline` | - `copy` | - `circles-four` | - `waveform` | - `film-strip` | - `arrow-up` | - `arrow-circle-up` | - `plus` | - `funnel-simple` | - `user` | - `camera` | - `check` | - `file` | - `share-network` | - `question` | - `minus-circle` | - `globe-simple` | - `globe` | - `warning-circle` | - `x` | - `info` | - `caret-${GeometryCardinalDirection}` | - `caret-up-down`; - -export type GlyphKeyCurrency = `dollar` | `eur`; -export type GlyphWeight = `bold` | `fill` | `light`; diff --git a/utils/src/app/lib.ts b/utils/src/app/lib.ts @@ -1,51 +0,0 @@ -import type { CallbackPromise, CallbackPromiseGeneric } from "$root"; - -export type IViewBasis<T extends object> = { - kv_init_prevent?: boolean; - on_mount?: CallbackPromise; - on_destroy?: CallbackPromise; -} & T; - - -export type LabelFieldKind = `link` | `on` | `shade`; -export type EntryStyle = `guide` | `line`; -export type ImageAspectRatio = `auto` | `1/1` | `4/3` | `16/9` | `3/4`; - -export type SvelteTransitionEasingFunction = (t: number) => number; - -export type LoadingBlades = 8 | 12; -export type LoadingDimension = GeometryDimension | `glyph-send-button`; //@todo remove - -export type LayerGlyphBasisKind = `_a` | `_d` | `_pl`; - -export interface SvelteTransitionConfig { - delay?: number; - duration?: number; - easing?: SvelteTransitionEasingFunction; - css?: (t: number, u: number) => string; - tick?: (t: number, u: number) => void; -} - -export type ElementCallbackValue = CallbackPromiseGeneric<{ value: string; pass: boolean; }>; -export type ElementCallbackValueKeydown<T extends HTMLElement> = CallbackPromiseGeneric<{ key: string; key_s: boolean; el: T }>; -export type ElementCallbackValueBlur<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; -export type ElementCallbackValueFocus<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; -export type ElementCallbackMount<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>; - -export type GeometryScreenPositionHorizontal = `left` | `center` | `right`; -export type GeometryScreenPositionVertical = `top` | `center` | `bottom`; -export type GeometryScreenPosition = `${GeometryScreenPositionVertical}-${GeometryScreenPositionHorizontal}`; -export type GeometryCardinalDirection = `up` | `down` | `left` | `right`; -export type GeometryDimension = - `xs` | - `sm` | - `md` | - `lg` | - `xl`; -export type GeometryGlyphDimension = - | `${GeometryDimension}` - | `${GeometryDimension}-` - | `${GeometryDimension}--` - | `${GeometryDimension}+`; - -export type NavigationParamTuple<T extends string> = [T, string]; diff --git a/utils/src/app/search.ts b/utils/src/app/search.ts @@ -1,72 +0,0 @@ -export type SearchServiceResult = Record<string, any> & { id: string, result_k: string; result_v: string; }; -export type SearchServiceFlattenedData = Record<string, any> & { list: string; }; - -export type ISearchService = { - search(input: string): SearchServiceResult[] -}; - -export class SearchService implements ISearchService { - private _flattened_data: SearchServiceFlattenedData[] = []; - private _index_map: Map<string, Record<string, any>[]> = new Map(); - - constructor(data: Record<string, any[]>) { - this.flatten_data(data); - this.index_data(); - } - - private get flattened_data() { - return this._flattened_data; - } - - private flatten_data(data: Record<string, any[]>) { - Object.keys(data).forEach((list_group) => { - const list = data[list_group]; - list.forEach((item) => { - const flattened_item: SearchServiceFlattenedData = { ...item, group: list_group }; - this._flattened_data.push(flattened_item); - }); - }); - } - - private index_data(): void { - this.flattened_data.forEach((item) => { - Object.keys(item).forEach((key) => { - const key_lower = key.toLowerCase(); - const value = item[key]; - if (value != null) { - if (!this._index_map.has(key_lower)) this._index_map.set(key_lower, []); - this._index_map.get(key_lower)?.push(item); - } - }); - }); - } - - public search(query: string): SearchServiceResult[] { - if (!query) return []; - const search_query = query.toLowerCase().trim(); - let results: SearchServiceResult[] = []; - const results_seen = new Set<string>(); - this._index_map.forEach((items) => { - items.forEach((item) => { - for (const [key, value] of Object.entries(item)) { - if (key === `id` || key === `created_at` || key === `updated_at` || key === `public_key` || key === `group`) continue; - if (value && value.toString().replace(/[()_-]/gi, ` `).toLowerCase().includes(search_query)) { - const { group, ...rest } = item; - if (!(`id` in item)) continue; - const result_key = item.id; - if (results_seen.has(result_key)) continue; - results_seen.add(result_key); - const reshaped_result: SearchServiceResult = { - id: item.id, - result_k: key, - result_v: value, - [group]: { ...rest }, - }; - results.push(reshaped_result); - } - }; - }); - }); - return results; - } -} diff --git a/utils/src/app/styles.ts b/utils/src/app/styles.ts @@ -1,28 +0,0 @@ -export type ThemeLayer = 0 | 1 | 2; - -export type AppConfigType = `farmer` | `personal` - -export type AppLayoutKeyIOS = `ios0` | `ios1`; -export type AppLayoutKeyWeb = `webm0` | `webm1`; -export type AppLayoutKey = AppLayoutKeyIOS | AppLayoutKeyWeb; - -export type AppLayoutIOS<T extends string> = `${T}_${AppLayoutKeyIOS}`; -export type AppLayoutWeb<T extends string> = `${T}_${AppLayoutKeyWeb}`; - -export type AppLayoutKeyHeight = - | `lo_view_main` - | `lo_bottom_button` - | `nav_tabs` - | `nav_page_header` - | `nav_page_toolbar`; - -export type AppLayoutKeyWidth = - | `lo` - | `lo_line_entry` - | `lo_textdesc`; - -export type AppHeightsResponsiveIOS = AppLayoutIOS<AppLayoutKeyHeight>; -export type AppHeightsResponsiveWeb = AppLayoutWeb<AppLayoutKeyHeight>; - -export type AppWidthsResponsiveIOS = AppLayoutIOS<AppLayoutKeyWidth>; -export type AppWidthsResponsiveWeb = AppLayoutWeb<AppLayoutKeyWidth>; diff --git a/utils/src/app/util.ts b/utils/src/app/util.ts @@ -1,163 +0,0 @@ -import { sleep, type AppLayoutKey, type AppLayoutKeyIOS, type AppLayoutKeyWeb, type LabelFieldKind, type NavigationParamTuple, type ThemeLayer } from "$root"; - -export const fmt_cl = (classes?: string): string => { - return classes ? classes : ``; -}; - -export const get_layout = (val?: string): AppLayoutKey | undefined => { - const lo_ios = get_ios_layout(val); - if (lo_ios) return lo_ios; - const lo_web = get_web_layout(val); - if (lo_web) return lo_web; - return undefined; -}; - -export const get_layout_default = (val?: string): AppLayoutKey => { - const res = get_layout(val); - return res || `webm0`; -}; - -export const get_ios_layout = (val?: string): AppLayoutKeyIOS | undefined => { - switch (val) { - case `ios0`: - case `ios1`: - return val; - default: - return undefined; - }; -}; - -export const get_ios_layout_default = (val?: string): AppLayoutKeyIOS => { - const res = get_ios_layout(val); - return res || `ios0`; -}; - -export const get_web_layout = (val?: string): AppLayoutKeyWeb | undefined => { - switch (val) { - case `webm0`: - case `webm1`: - return val; - default: - return undefined; - }; -}; - -export const get_web_layout_default = (val?: string): AppLayoutKeyWeb => { - const res = get_web_layout(val); - return res || `webm0`; -}; - -export const parse_layer = (layer?: number, layer_default?: ThemeLayer): ThemeLayer => { - switch (layer) { - case 0: - case 1: - case 2: - return layer; - default: - return layer_default ? layer_default : 0; - }; -}; - -export const value_constrain = (regex_charset: RegExp, value: string): string => { - return value - .split(``) - .filter((char) => regex_charset.test(char)) - .join(``); -}; - -export const value_constrain_textarea = (regex_charset: RegExp, value: string): string => { - return value - .replace(/\u00A0/g, ` `) - .split(/[\n]/) - .map(line => line - .split(``) - .filter((char) => regex_charset.test(char)) - .join(``) - ) - .join("\n"); -}; - -export const encode_query_params = <T extends string>(params_list: NavigationParamTuple<T>[] = []): string => { - let query = ""; - for (const [k, v] of params_list) { - if (k && v) { - if (query) query += `&`; - query += `${k.trim()}=${encodeURIComponent(v.trim())}`; - } - } - return query ? `?${query}` : ``; -}; - -export const encode_route = <TRoute extends string, TParam extends string>(route: TRoute, params_list?: NavigationParamTuple<TParam>[]): string => { - const query = encode_query_params(params_list); - if (!query) return route; - return `${route === `/` ? `/` : route.replace(/\/+$/, ``)}${query}`; -}; - -export const fmt_trellis = (hide_border_t: boolean, hide_border_b: boolean): string => { - return `${hide_border_t ? `group-first:border-t-0` : `group-first:border-t-line`} ${hide_border_b ? `group-last:border-b-0` : `group-last:border-b-line`}`; -}; - -export const get_label_classes_kind = (layer: ThemeLayer, label_kind: LabelFieldKind | undefined, hide_active: boolean): string => { - return `text-layer-${layer}-glyph${label_kind ? `-${label_kind}` : ``} ${hide_active ? `` : `group-active:text-layer-${layer}-glyph${label_kind ? `-${label_kind}_a` : `_a`}`}` -}; - -export const fmt_textarea_value = (value: string): string => { - return value.replace(/ /g, `\u00A0`); -}; - -export const list_assign = (list_curr: string[], list_new: string[]): string[] => { - return Array.from( - new Set([...list_curr, ...list_new]), - ).filter((i) => !!i); -}; - -export const el_id = (id: string): HTMLElement | undefined => { - const el = document.getElementById(id); - return el ? el : undefined; -}; - -export const el_toggle = (id: string, toggle_class: string): void => { - const el = document.getElementById(id); - if (el) el.classList.toggle(toggle_class); -}; - -export const els_id_pref = (id_pref: string): Element[] | undefined => { - const els = document.querySelectorAll(`[id^="${id_pref}"]`); - if (els && els.length) return Array.from(els); - return undefined; -}; - -export const els_id_pref_index = (id_pref: string, num_index: number, orientation: `greater` | `lesser` | `not` = `greater`, inclusive: boolean = true): Element[] | undefined => { - const els = document.querySelectorAll(`[id^="${`${id_pref}-`.replaceAll(`--`, `-`)}"]`); - if (els && els.length) return Array.from(els).filter(el => { - const match = el.id.match(/(?<=^|\-)[0-9]\d*(?=\-)/) - if (match) { - const num = parseInt(match[0], 10); - switch (orientation) { - case `greater`: { - if (inclusive) return num >= num_index; - else return num > num_index; - } - case `lesser`: { - if (inclusive) return num <= num_index; - else return num < num_index; - } - case `not`: { - return num !== num_index; - } - } - } - return false; - }); - return undefined; -}; - -export const el_focus = async (id: string, callback: () => Promise<void>, layer: ThemeLayer = 1): Promise<void> => { - const el = el_id(id); - el?.classList.add(`entry-layer-${layer}-highlight`); - el?.focus(); - await sleep(1200); - await callback(); - el?.classList.remove(`entry-layer-${layer}-highlight`); -}; diff --git a/utils/src/browser.ts b/utils/src/browser.ts @@ -1,92 +0,0 @@ -export type BrowserPlatformInfo = { - os: string; - browser: string; - version: string; -}; - -const remove_excess_mozilla_and_version = /^mozilla\/\d\.\d\W/; -const browser_pattern = /(\w+)\/(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)/g; -const engine_and_version_pattern = /^(ver|cri|gec)/; -const brand_list = ['chrome', 'opera', 'safari', 'edge', 'firefox']; - -const mobiles: Record<string, RegExp> = { - iphone: /iphone/, - ipad: /ipad|macintosh/, - android: /android/ -}; - -const desktops: Record<string, RegExp> = { - windows: /win/, - mac: /macintosh/, - linux: /linux/ -}; - -const parse_user_agent_string = (ua_string: string): BrowserPlatformInfo => { - const ua = ua_string.toLowerCase().replace(remove_excess_mozilla_and_version, ''); - - const mobile_os = Object.keys(mobiles).find( - (key) => mobiles[key].test(ua) && navigator.maxTouchPoints >= 1 - ); - const desktop_os = Object.keys(desktops).find((key) => desktops[key].test(ua)); - const os = mobile_os || desktop_os || ''; - - const browser_matches = ua.match(browser_pattern); - const version_regex = /version\/(\d+(\.\d+)*)/; - const safari_version_match = ua.match(version_regex); - const safari_version = Array.isArray(safari_version_match) ? safari_version_match[1] : null; - - const browser_offset = - browser_matches && browser_matches.length > 2 && !engine_and_version_pattern.test(browser_matches[1]) - ? 1 - : 0; - const browser_result = - browser_matches && browser_matches[browser_matches.length - 1 - browser_offset].split('/'); - const browser = browser_result ? browser_result[0] : ''; - const version = safari_version || (browser_result ? browser_result[1] : ''); - - return { os, browser, version }; -}; - -export const browser_platform = (): BrowserPlatformInfo | undefined => { - if (typeof navigator !== 'undefined') { - if ('userAgentData' in navigator && navigator.userAgentData) { - const ua_data = navigator.userAgentData as { - platform: string; - brands: { brand: string; version: string }[]; - }; - const os = ua_data.platform.toLowerCase(); - let browser = ''; - let version = ''; - - if (Array.isArray(ua_data.brands)) { - for (const { brand, version: brand_version } of ua_data.brands) { - const lower_brand = brand.toLowerCase(); - if (brand_list.some((b) => lower_brand.includes(b))) { - browser = lower_brand; - version = brand_version; - break; - } - } - } - - if (!browser && navigator.userAgent) { - return parse_user_agent_string(navigator.userAgent); - } - return { os, browser, version }; - } - - if (navigator.userAgent) { - return parse_user_agent_string(navigator.userAgent); - } - - const nav_platform = navigator.platform; - if (!nav_platform) return undefined; - return { - os: nav_platform, - browser: '', - version: '' - }; - } - - return undefined; -}; diff --git a/utils/src/client/datastore.ts b/utils/src/client/datastore.ts @@ -1,23 +0,0 @@ -import type { ResolveError, ResultObj, ResultPass, ResultsList } from "$root"; - -export type IClientDatastoreSetResolve = ResolveError<ResultPass>; -export type IClientDatastoreGetResolve = ResolveError<ResultObj<string>>; -export type IClientDatastoreSetPResolve = ResolveError<ResultPass>; -export type IClientDatastoreGetPResolve = ResolveError<ResultObj<string>>; -export type IClientDatastoreKeysResolve = ResolveError<ResultsList<string>>; -export type IClientDatastoreEntriesResolve = ResolveError<ResultsList<[string, unknown]>>; -export type IClientDatastoreRemoveResolve = ResolveError<ResultPass>; - -export type IClientDatastore< - TKeyMap extends Record<string, string>, - TParamKeyMap extends Record<string, (...args: any[]) => string> -> = { - init(): Promise<void>; - set(key: keyof TKeyMap, value: string): Promise<IClientDatastoreSetResolve>; - get(key: keyof TKeyMap): Promise<IClientDatastoreGetResolve>; - setp<K extends keyof TParamKeyMap>(key: K, key_param: Parameters<TParamKeyMap[K]>[0], value: string): Promise<IClientDatastoreSetPResolve>; - getp<K extends keyof TParamKeyMap>(key: K, key_param: Parameters<TParamKeyMap[K]>[0]): Promise<IClientDatastoreGetPResolve>; - keys(): Promise<IClientDatastoreKeysResolve>; - entries(): Promise<IClientDatastoreEntriesResolve> - remove(key: string): Promise<IClientDatastoreRemoveResolve>; -}; -\ No newline at end of file diff --git a/utils/src/client/geo.ts b/utils/src/client/geo.ts @@ -1,11 +0,0 @@ -import type { ErrorMessage, IClientGeolocationPosition } from "$root"; - -export type IGeolocationErrorMessage = - | `error.client.geolocation.permission_denied` - | `error.client.geolocation.location_unavailable` - | `error.client.geolocation.timeout` - | `*`; - -export type IClientGeolocation = { - current(): Promise<IClientGeolocationPosition | ErrorMessage<IGeolocationErrorMessage>>; -}; -\ No newline at end of file diff --git a/utils/src/client/gui.ts b/utils/src/client/gui.ts @@ -1,22 +0,0 @@ -export type IClientGuiDialogKind = "info" | "warning" | "error"; - -export type IClientGuiDialogAlertOpts = string; - -export type IClientGuiDialogConfirmOpts = string | { title?: string, kind?: IClientGuiDialogKind; message: string; cancel?: string; ok?: string; }; - -export type IClientGuiDialogResolve = { results: string[]; }; - -export type IClientGuiNotifyPermission = "default" | "denied" | "granted"; -export type IClientGuiNotifySendOptions = { - id?: number; - channel_id?: string; - title: string; - body?: string; -}; - -export type IClientGui = { - alert(opts: IClientGuiDialogAlertOpts): Promise<boolean>; - confirm(opts: IClientGuiDialogConfirmOpts): Promise<boolean>; - notify_init(): Promise<IClientGuiNotifyPermission | undefined>; - notify_send(opts: string | IClientGuiNotifySendOptions): Promise<void>; -}; -\ No newline at end of file diff --git a/utils/src/config.ts b/utils/src/config.ts @@ -1,19 +0,0 @@ -export const cfg_map = { - styles: { - base: { - light: `https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json`, - dark: `https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json` - } - }, - popup: { - dot: { - offset: [0, -10] as [number, number] - } - }, - coords: { - default: { - lat: 0, - lng: 0, - } - } -}; -\ No newline at end of file diff --git a/utils/src/currency.ts b/utils/src/currency.ts @@ -1,46 +0,0 @@ -import { util_rxp } from "$root"; - -export type FiatCurrency = `usd` | `eur`; -export const fiat_currencies: FiatCurrency[] = [`usd`, `eur`] as const; - -// @todo -export const price_to_formatted = (n: number, _currency: string) => Math.round(n * 100) / 100; - -export const parse_currency = (val?: string): FiatCurrency => { - const cur = val?.trim().toLowerCase() - switch (cur) { - case `usd`: - case `eur`: - return cur; - default: - return `usd`; - }; -}; - -export const fmt_price = (locale: string, value: string, currency: string): string => { - const fmt = new Intl.NumberFormat(locale, { - style: 'currency', - currency: currency.toUpperCase(), - minimumFractionDigits: 2, - maximumFractionDigits: 2 - }); - return fmt.format(parseFloat(value)); -}; - -export const parse_currency_marker = (locale: string, currency: string): string => { - const cur = parse_currency(currency); - const fmt = new Intl.NumberFormat(locale, { - style: 'currency', - currency: cur.toUpperCase(), - minimumFractionDigits: 2, - }); - const fmt_basis = fmt.format(1); - let fmt_res: string | undefined = undefined; - fmt_res = fmt_basis.match(util_rxp.currency_marker)?.[0]; - if (fmt_res) return fmt_res; - fmt_res = fmt_basis.match(util_rxp.currency_symbol)?.[0]; - if (fmt_res) return fmt_res; - fmt_res = fmt_basis.match(new RegExp(cur, `i`))?.[0]; - if (fmt_res) return fmt_res; - return cur.toUpperCase(); -}; -\ No newline at end of file diff --git a/utils/src/error.ts b/utils/src/error.ts @@ -1,24 +0,0 @@ -export type IErrorCatchCallback = { - name: string; - message: string; - stack: string; - url: string; - func: string; -}; - -export type ErrorMessage<T extends string> = { err: T }; - -export const err_msg = <T extends string>(err: T): ErrorMessage<T> => { - return { err }; -}; - -export const throw_err = (param: string | ErrorMessage<string>): never => { - if (typeof param === `string`) throw new Error(param); - else throw new Error(param.err); -}; - -export const handle_error = (e: unknown, append?: string): ErrorMessage<string> => { - const msg = (e as Error).message ? (e as Error).message : String(e); - const err = `${msg}${append ? ` ${append}` : ``}`; - return { err }; -}; -\ No newline at end of file diff --git a/utils/src/errors/lib.ts b/utils/src/errors/lib.ts @@ -0,0 +1,16 @@ +import { ErrorMessage } from "../types/lib.js"; + +export const err_msg = <T extends string>(err: T): ErrorMessage<T> => { + return { err }; +}; + +export const throw_err = (param: string | ErrorMessage<string>): never => { + if (typeof param === `string`) throw new Error(param); + else throw new Error(param.err); +}; + +export const handle_error = (e: unknown, append?: string): ErrorMessage<string> => { + const msg = (e as Error).message ? (e as Error).message : String(e); + const err = `${msg}${append ? ` ${append}` : ``}`; + return { err }; +}; +\ No newline at end of file diff --git a/utils/src/geo.ts b/utils/src/geo.ts @@ -1,278 +0,0 @@ -import { LocationBasis, type GeolocationPointTuple, type GeometryPoint } from "$root"; -import { decodeBase32, encodeBase32 } from "geohashing"; - -export type GeolocationAddress = { - primary: string; - admin: string; - country: string; -}; - -export type GeolocationPoint = { - lat: number; - lng: number; -}; - -export type LocationPoint = GeolocationCoordinatesPoint & { - error: GeolocationCoordinatesPoint; -} - -export type GeocoderReverseResult = { - id: number; - name: string; - admin1_id: string | number; - admin1_name: string; - country_id: string; - country_name: string; - latitude: number; - longitude: number; -}; - -export type IClientGeolocationPosition = { - lat: number; - lng: number; - accuracy?: number; - altitude?: number; - altitude_accuracy?: number; -}; - -export type GeolocationCoordinatesPoint = { - lat: number; - lng: number; -} - -export type GeolocationLatitudeFmtOption = 'dms' | 'd' | 'dm'; - -export const geohash_encode = (opts: { - lat: string | number; - lng: string | number; -}): string => { - 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 = (geohash: string): LocationPoint => { - const { lat, lng, error: { lat: lat_err, lng: lng_err } } = decodeBase32(geohash); - return { - lat, - lng, - error: { - lat: lat_err, - lng: lng_err - } - }; -}; - -export const location_geohash = (point: GeolocationCoordinatesPoint): string => { - const { lat, lng } = point; - const res = geohash_encode({ lat, lng }); - return res; -}; - -export const parse_geop_point = (point: GeolocationCoordinatesPoint): GeolocationPoint => { - const { lat, lng } = point; - return { lat, lng }; -}; - -export const parse_geol_coords = (number: number): number => { - return Math.round(number * 1e7) / 1e7; -}; - -export const parse_geolocation_address = (addr?: GeolocationAddress): GeolocationAddress | undefined => { - if (!addr) return undefined; - const { primary, admin, country } = addr; - return { primary, admin, country }; -}; - -export const parse_geolocation_point = (point?: GeometryPoint): GeolocationPoint | undefined => { - if (!point) return undefined; - return { - lat: point.coordinates[1], - lng: point.coordinates[0], - }; -}; - -export const geo_point_to_geometry = (point?: GeolocationPoint): GeometryPoint | undefined => { - if (!point) return undefined; - return { - type: 'Point', - coordinates: [point.lng, point.lat] - }; -}; - -export const location_basis_to_geo_point = (basis?: LocationBasis): GeolocationPoint | undefined => { - if (!basis) return undefined; - return { - lat: basis.point.lat, - lng: basis.point.lng - }; -}; - -export const parse_geocode_address = (geoc?: GeocoderReverseResult): GeolocationAddress | undefined => { - if (!geoc) return undefined; - const { name: primary, admin1_name: admin, country_id: country } = geoc; - return { primary, admin, country }; -}; - -export const fmt_geocode_address = (geoc: GeocoderReverseResult): string => { - const addr = parse_geocode_address(geoc); - return addr ? `${addr.primary}, ${addr.admin}, ${addr.country}` : ``; -}; - -export const fmt_geolocation_address = (addr: GeolocationAddress): string => { - return `${addr.primary}, ${addr.admin}, ${addr.country}`; -}; - -export const fmt_geometry_point_coords = (point: GeometryPoint, locale: string): string => { - const lat = geol_lat_fmt(point.coordinates[0], `dms`, locale, 3); - const lng = geol_lng_fmt(point.coordinates[1], `dms`, locale, 3); - return `${lat}, ${lng}`; -}; - -export const parse_geom_point_tup = (point: GeometryPoint): GeolocationPointTuple => { - return [ - point.coordinates[0], - point.coordinates[1], - ]; -}; - -export const parse_geol_point_tup = (point: GeolocationPoint): GeolocationPointTuple => { - return [ - point.lng, - point.lat - ]; -}; - -export const parse_tup_geop_point = (map_center: GeolocationPointTuple): GeolocationPoint => { - return { - lat: map_center[1], - lng: map_center[0] - } -}; - -export const geol_lat_fmt = (lat: number, fmt_opt: GeolocationLatitudeFmtOption, locale: string, precision: number = 5): string => { - const options: Intl.NumberFormatOptions = { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }; - const fmt_deg = new Intl.NumberFormat(locale, { maximumFractionDigits: 0 }); - const fmt_min = new Intl.NumberFormat(locale, options); - const fmt_sec = new Intl.NumberFormat(locale, options); - if (fmt_opt === 'dms') { - const deg = Math.floor(Math.abs(lat)); - const min = Math.floor((Math.abs(lat) - deg) * 60); - const sec = ((Math.abs(lat) - deg - min / 60) * 3600); - return `${fmt_deg.format(deg)}° ${fmt_min.format(min)}' ${fmt_sec.format(sec)}" ${lat >= 0 ? 'N' : 'S'}`; - } else if (fmt_opt === 'dm') { - const deg = Math.floor(Math.abs(lat)); - const min = (Math.abs(lat) - deg) * 60; - return `${fmt_deg.format(deg)}° ${fmt_min.format(min)}' ${lat >= 0 ? 'N' : 'S'}`; - } else { - return `${lat.toLocaleString(locale, { maximumFractionDigits: precision })}° ${lat >= 0 ? 'N' : 'S'}`; - } -}; - -export const geol_lng_fmt = (lng: number, fmt_opt: GeolocationLatitudeFmtOption, locale: string, precision: number = 5): string => { - const options: Intl.NumberFormatOptions = { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }; - const fmt_deg = new Intl.NumberFormat(locale, { maximumFractionDigits: 0 }); - const fmt_min = new Intl.NumberFormat(locale, options); - const fmt_sec = new Intl.NumberFormat(locale, options); - if (fmt_opt === 'dms') { - const degrees = Math.floor(Math.abs(lng)); - const minutes = Math.floor((Math.abs(lng) - degrees) * 60); - const seconds = ((Math.abs(lng) - degrees - minutes / 60) * 3600); - return `${fmt_deg.format(degrees)}° ${fmt_min.format(minutes)}' ${fmt_sec.format(seconds)}" ${lng >= 0 ? 'E' : 'W'}`; - } else if (fmt_opt === 'dm') { - const degrees = Math.floor(Math.abs(lng)); - const minutes = (Math.abs(lng) - degrees) * 60; - return `${fmt_deg.format(degrees)}° ${fmt_min.format(minutes)}' ${lng >= 0 ? 'E' : 'W'}`; - } else { - return `${lng.toLocaleString(locale, { maximumFractionDigits: precision })}° ${lng >= 0 ? 'E' : 'W'}`; - } -}; - -export const compute_bounding_box = (lat: number, lng: number, distance_km: number): { nw: GeolocationPoint; ne: GeolocationPoint; se: GeolocationPoint; sw: GeolocationPoint; } => { - const deg_to_rad = (deg: number) => deg * (Math.PI / 180); - const rad_to_deg = (rad: number) => rad * (180 / Math.PI); - - const R = 6371; - - function destination_point(lat: number, lng: number, bearing: number, distance_km: number): GeolocationPoint { - const lat1 = deg_to_rad(lat); - const lon1 = deg_to_rad(lng); - const angular_distance = distance_km / R; - - const lat2 = Math.asin(Math.sin(lat1) * Math.cos(angular_distance) + Math.cos(lat1) * Math.sin(angular_distance) * Math.cos(deg_to_rad(bearing))); - const lon2 = lon1 + Math.atan2(Math.sin(deg_to_rad(bearing)) * Math.sin(angular_distance) * Math.cos(lat1), Math.cos(angular_distance) - Math.sin(lat1) * Math.sin(lat2)); - - return { lat: rad_to_deg(lat2), lng: rad_to_deg(lon2) }; - } - - const bearings = [0, 90, 180, 270]; - - const coords = bearings.map(bearing => destination_point(lat, lng, bearing, distance_km / Math.sqrt(2))); - - return { - nw: coords[0], - ne: coords[1], - se: coords[2], - sw: coords[3] - }; -}; - -export const geo_bounds_calc = (lat: number, lng: number, distance_km: number): { north: GeolocationPoint; south: GeolocationPoint; east: GeolocationPoint; west: GeolocationPoint; } => { - const deg_to_rad = (deg: number) => deg * (Math.PI / 180); - const rad_to_deg = (rad: number) => rad * (180 / Math.PI); - - const R = 6371; - - function destination_point(lat: number, lng: number, bearing: number, distance_km: number): GeolocationPoint { - const lat1 = deg_to_rad(lat); - const lon1 = deg_to_rad(lng); - const angular_distance = distance_km / R; - - const lat2 = Math.asin(Math.sin(lat1) * Math.cos(angular_distance) + Math.cos(lat1) * Math.sin(angular_distance) * Math.cos(deg_to_rad(bearing))); - const lon2 = lon1 + Math.atan2(Math.sin(deg_to_rad(bearing)) * Math.sin(angular_distance) * Math.cos(lat1), Math.cos(angular_distance) - Math.sin(lat1) * Math.sin(lat2)); - - return { lat: rad_to_deg(lat2), lng: rad_to_deg(lon2) }; - } - - return { - north: destination_point(lat, lng, 0, distance_km), - south: destination_point(lat, lng, 180, distance_km), - east: destination_point(lat, lng, 90, distance_km), - west: destination_point(lat, lng, 270, distance_km) - }; -}; - -export const location_gcs_to_location_basis = ({ - id, - lat, - lng, - gc_name: primary, - gc_admin1_name: admin, - gc_country_id: country, -}: { - id: string; - lat: number; - lng: number; - gc_name?: string; - gc_admin1_name?: string; - gc_country_id?: string; -}): LocationBasis => ({ - id, - point: { - lat, - lng, - }, - address: primary && admin && country ? { - primary, - admin, - country, - } : undefined -}); - diff --git a/utils/src/http.ts b/utils/src/http.ts @@ -1,110 +0,0 @@ -import { is_error_response, is_message_response, type ErrorMessage, type FieldRecord, type NotifyMessage } from "$root"; - -export type IClientHttp = { - fetch(opts: IHttpOpts): Promise<IHttpResponse | ErrorMessage<string>>; -}; - -export type IHttpImageResponse = { - status: number; - blob?: Blob; - headers: FieldRecord; - url: string; -}; - -export type IHttpResponse = { - status: number; - data?: any; - error?: string; - message?: NotifyMessage; - headers: FieldRecord; - url: string; -}; - -export type IHttpOptsData = any; -export type IHttpOptsParams = Record<string, string | string[]>; - -export type IHttpOpts = { - url: string; - method?: `get` | `post` | `put`; - params?: IHttpOptsParams; - data?: IHttpOptsData; - data_bin?: Uint8Array; - authorization?: string; - headers?: FieldRecord; - connect_timeout?: number; -}; - -export const lib_http_to_bodyinit = (data: any): BodyInit => { - if (typeof data === 'string') return data; - else if (data instanceof FormData) return data; - else if (data instanceof Blob) return data; - else if (data instanceof ArrayBuffer) return data; - else if (data instanceof URLSearchParams) return data; - return JSON.stringify(data); -} - -export const lib_http_parse_headers = (headers: Headers): FieldRecord => { - const acc: FieldRecord = {}; - headers.forEach((value, key) => acc[key] = value); - return acc; -}; - -export const http_fetch_opts = (opts: IHttpOpts): { url: string; options: RequestInit; } => { - const { url } = opts; - const method = opts.method ? opts.method.toUpperCase() : `GET`; - const headers = new Headers(); - if (method === `POST`) headers.append(`Content-Type`, `application/json`); - if (opts.authorization) headers.append(`Authorization`, `Bearer ${encodeURIComponent(opts.authorization)}`); - if (opts.headers) Object.entries(opts.headers).forEach(([k, v]) => headers.append(k, v)) - const options: RequestInit = { - method, - headers, - } - if (opts.data) options.body = lib_http_to_bodyinit(opts.data); - if (opts.data_bin) options.body = opts.data_bin; - return { - url, - options - } -}; - -export const lib_http_parse_response = async (res: Response): Promise<Promise<IHttpResponse>> => { - let data: any = null; - try { - const res_json = await res.json(); - if (typeof res_json === `string`) data = JSON.parse(res_json); - else data = res_json; - } catch { } - if (!data) data = await res.text(); - return { - status: res.status, - url: res.url, - data: res.ok && data ? data : null, - error: !res.ok && is_error_response(data) ? data.error : undefined, - message: res.ok && is_message_response(data) ? data : undefined, - headers: lib_http_parse_headers(res.headers) - }; -}; - -export const http_fetch = async (opts: IHttpOpts): Promise<IHttpResponse> => { - const { url, options } = http_fetch_opts(opts); - const response = await fetch(url, options); - let data: any = null; - try { - const res_json = await response.json(); - data = typeof res_json === `string` ? JSON.parse(res_json) : res_json; - } catch { } - if (!data) { - try { - const res_text = await response.text(); - data = res_text; - } catch { } - } - return { - status: response.status, - url: response.url, - data, - headers: lib_http_parse_headers(response.headers) - }; -}; - diff --git a/utils/src/i18n.ts b/utils/src/i18n.ts @@ -1,51 +0,0 @@ -import { type Loader } from '@sveltekit-i18n/base'; -import type { Config as ConfigIcu, Parser as ParserIcu } from "@sveltekit-i18n/parser-icu"; -import parser_icu from "@sveltekit-i18n/parser-icu"; -import i18n, { type Config } from 'sveltekit-i18n'; - -type LanguageConfig = { - default?: string; - value?: string; -}; - -const lib_config: Config<LanguageConfig> = { - initLocale: `en`, - fallbackLocale: `en`, - translations: {}, - loaders: [], -}; - -const lib_i18n = new i18n(lib_config); -export type I18nTranslateFunction = typeof lib_i18n.t; -export type I18nTranslateLocale = typeof lib_i18n.locale; - -export const i18n_conf = <T extends string>(opts: { - default_locale: T; - translations: Record<T, any>; - loaders: Loader.LoaderModule[] -}) => { - const { default_locale: initLocale, translations, loaders } = opts; - const config: Config<LanguageConfig> = { - initLocale, - fallbackLocale: initLocale, - translations, - loaders, - }; - return new i18n(config); -}; - -export const i18n_conf_icu = <T extends string>(opts: { - default_locale: T; - translations: Record<T, any>; - loaders: Loader.LoaderModule[] -}): i18n<ParserIcu.Params<LanguageConfig>> => { - const { default_locale: initLocale, translations, loaders } = opts; - const config: ConfigIcu<LanguageConfig> = { - initLocale, - fallbackLocale: initLocale, - translations, - parser: parser_icu(), - loaders, - }; - return new i18n(config); -}; -\ No newline at end of file diff --git a/utils/src/image.ts b/utils/src/image.ts @@ -1,21 +0,0 @@ -import { type FilePath } from "$root"; - -export type MediaImageUploadResult = { - base_url: string; - file_hash: string; - file_ext: string; -}; - -export const fmt_media_image_upload_result_url = (res: MediaImageUploadResult): string => `${res.base_url}/${res.file_hash}.${res.file_ext}`; - -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_name, mime_type] = file_path_file.split(`.`); - if (!file_name || !mime_type) return undefined; - return { - file_path, - file_name, - mime_type - }; -}; -\ No newline at end of file diff --git a/utils/src/index.ts b/utils/src/index.ts @@ -1,33 +1,2 @@ -export * from "./*regex" -export * from "./*validation" -export * from "./app/config" -export * from "./app/glyph" -export * from "./app/lib" -export * from "./app/search" -export * from "./app/styles" -export * from "./app/util" -export * from "./browser" -export * from "./client/datastore" -export * from "./client/geo" -export * from "./client/gui" -export * from "./config" -export * from "./currency" -export * from "./error" -export * from "./geo" -export * from "./http" -export * from "./i18n" -export * from "./image" -export * from "./lib" -export * from "./list" -export * from "./listings/order" -export * from "./model" -export * from "./models/farm" -export * from "./models/location" -export * from "./number" -export * from "./object" -export * from "./response" -export * from "./time" -export * from "./trade" -export * from "./types" -export * from "./unit" -export * from "./uuid" +export * from "./errors/lib.js" +export * from "./types/lib.js" diff --git a/utils/src/lib.ts b/utils/src/lib.ts @@ -1,67 +0,0 @@ -import { type CallbackPromise } from "./types"; - -export const symbols = { - bullet: '•', - dash: `—`, - up: `↑`, - down: `↓`, - percent: `%` -} - -export const root_symbol = "»--`--,---"; - -export const sleep = async (ms: number): Promise<void> => { - await new Promise((resolve) => setTimeout(resolve, ms)); -}; - -export const obj_keys_maxnum = (obj: any): number => Math.max( - ...Object.keys(obj).map(Number), -); - -export const debounce_callback = (func: Function, delay: number) => { - let timer: ReturnType<typeof setTimeout>; - return function (this: any, ...args: any) { - clearTimeout(timer); - timer = setTimeout(() => func.apply(this, args), delay); - }; -}; - -export const str_trunc = (val: string, max_length: number = 28): string => { - if (val.length <= max_length) return val; - return `${val.slice(0, max_length - 3)}...`; -}; - -export const str_capitalize_words = (val?: string): string => { - if (!val) return ``; - return val.split(` `).map(i => i ? `${i[0].toUpperCase()}${i.slice(1)}` : ``).filter(i => !!i).join(` `); -}; - -export const exe_iter = async (callback: CallbackPromise, num: number = 1, delay: number = 400): Promise<void> => { - try { - const iter_fn = (count: number) => { - if (count > 0) { - callback(); - if (count > 1) { - setTimeout(() => { - iter_fn(count - 1); - }, delay); - } - } - }; - iter_fn(num); - } catch (e) { - console.log(`(error) exe_iter `, e); - } -}; - - -export const fmt_tags_key = (...args: (string | number | undefined)[]) => - args.filter(Boolean).join("-").toLowerCase(); - -export const compare_str_eq = (a: string, b: string): boolean => { - return a.toLowerCase() === b.toLowerCase(); -}; - -export const compare_str_ne = (a: string, b: string): boolean => { - return a.toLowerCase() !== b.toLowerCase(); -}; -\ No newline at end of file diff --git a/utils/src/list.ts b/utils/src/list.ts @@ -1 +0,0 @@ -export const list_defined = (i: any): boolean => typeof i !== `undefined`; -\ No newline at end of file diff --git a/utils/src/listings/order.ts b/utils/src/listings/order.ts @@ -1,145 +0,0 @@ -import { compare_str_ne, fmt_tags_key, mass_to_g, parse_mass_unit, price_to_formatted, symbols } from "$root"; -import type { ListingOrder, ListingOrderDiscount } from "@radroots/radroots-common-bindings"; - -export type IListingOrderCreate = { - tags_prices: string[][]; - tags_quantities: string[][]; - tags_discounts: string[][]; - quantity: { - amount: number; - unit: string; - count: number; - }; -}; - -export const listing_order_create = (opts: IListingOrderCreate): ListingOrder => { - const { - tags_prices, tags_quantities, tags_discounts, - quantity: { amount: qty_amt, unit: qty_unit, count: qty_count } - } = opts; - - const qty_key = fmt_tags_key(qty_amt, qty_unit); - const qty_tag = tags_quantities.find(t => fmt_tags_key(t[1], t[2]) === qty_key); - if (!qty_tag) throw new Error(`invalid quantity tag`); - - const [, qty_tag_amt, qty_tag_unit, qty_tag_label] = qty_tag; - const qty_unit_parsed = parse_mass_unit(qty_tag_unit); - if (!qty_unit_parsed) throw new Error(`invalid quantity unit`); - - const price_qty_key = fmt_tags_key(qty_tag_amt, qty_tag_unit, qty_tag_label); - const price_tag = tags_prices.find(t => t[5]?.toLowerCase() === price_qty_key); - if (!price_tag) throw new Error(`invalid price tag`); - - const [, price_tag_amt, price_tag_currency, price_tag_qty_amt, price_tag_qty_unit] = price_tag; - const price_unit_parsed = parse_mass_unit(price_tag_qty_unit); - if (!price_unit_parsed) throw new Error(`invalid price unit`); - - const qty_amt_num = parseFloat(qty_tag_amt); - const price_amt = parseFloat(price_tag_amt); - const price_qty_amt = parseFloat(price_tag_qty_amt); - - const mass_g = mass_to_g(qty_amt_num * qty_count, qty_tag_unit); - const price_group_g = mass_to_g(price_qty_amt, price_tag_qty_unit); - const group_count = mass_g / price_group_g; - const subtotal = price_to_formatted(group_count * price_amt, price_tag_currency); - - const discounts: ListingOrderDiscount[] = []; - - for (const discount of tags_discounts) { - const discount_type = discount[0]; - - if (discount_type === `price-discount-subtotal`) { - const [, threshold_str, currency, value_str, mode] = discount; - if (compare_str_ne(currency, price_tag_currency)) continue; - const threshold = parseFloat(threshold_str); - if (subtotal < threshold) continue; - - if (mode === symbols.percent) { - const percent = parseFloat(value_str); - discounts.push({ - discount_type: `subtotal`, - threshold, - discount_percent: percent, - discount_amount: price_to_formatted(subtotal * percent / 100, currency), - currency - }); - } else { - const amount = parseFloat(value_str); - discounts.push({ - discount_type: `subtotal`, - threshold, - discount_amount: price_to_formatted(amount, currency), - currency - }); - } - } - - if (discount_type === `price-discount-mass`) { - const [, discount_unit, threshold_str, threshold_unit, per_unit_str, currency] = discount; - if (compare_str_ne(currency, price_tag_currency)) continue; - const threshold = parseFloat(threshold_str); - const mass_in_threshold = mass_g / mass_to_g(1, threshold_unit); - if (mass_in_threshold < threshold) continue; - - const per_unit = parseFloat(per_unit_str); - const unit_count = mass_g / mass_to_g(1, discount_unit); - discounts.push({ - discount_type: `mass`, - threshold, - threshold_unit, - discount_per_unit: per_unit, - discount_unit, - discount_amount: price_to_formatted(unit_count * per_unit, currency), - currency - }); - } - - if (discount_type === `price-discount-quantity`) { - const [, key, min_str, per_unit_str, currency] = discount; - if (compare_str_ne(currency, price_tag_currency)) continue; - if (key !== price_qty_key) continue; - const min = parseInt(min_str); - if (qty_count < min) continue; - const per_unit = parseFloat(per_unit_str); - discounts.push({ - discount_type: `quantity`, - threshold: min, - discount_per_unit: per_unit, - discount_amount: price_to_formatted(per_unit * qty_count, currency), - currency - }); - } - } - - const total_discount = price_to_formatted(discounts.reduce((acc, d) => acc + d.discount_amount, 0), price_tag_currency); - const subtotal_amt = price_to_formatted(subtotal, price_tag_currency); - const total_amt = price_to_formatted(subtotal - total_discount, price_tag_currency); - - return { - quantity: { - amount: qty_amt_num, - unit: qty_unit_parsed, - label: qty_tag_label, - count: qty_count, - }, - price: { - amount: price_amt, - currency: price_tag_currency, - quantity_amount: price_qty_amt, - quantity_unit: price_unit_parsed - }, - discounts, - subtotal: { - price_amount: subtotal_amt, - price_currency: price_tag_currency, - quantity_amount: qty_amt_num * qty_count, - quantity_unit: qty_tag_unit - }, - total: { - price_amount: total_amt, - price_currency: price_tag_currency, - quantity_amount: qty_amt_num * qty_count, - quantity_unit: qty_tag_unit - } - }; -}; diff --git a/utils/src/model.ts b/utils/src/model.ts @@ -1,129 +0,0 @@ -export type IModelsQueryValue = string | number | boolean | null; -export type IModelsQueryBindValue = string | number | boolean | null;; -export type IModelsQueryBindValueTuple = [string, IModelsQueryValue]; -export type IModelsQueryBindValueOpt = (IModelsQueryBindValue | null); -export type IModelsQueryFilterOption = `equals` | `starts-with` | `ends-with` | `contains` | `ne`; -export type IModelsQueryFilterOptionList = `between` | `in`; -export type IModelsQueryFilterCondition = `and` | `or` | `not` - -export type IModelsSortCreatedAt = 'newest' | 'oldest'; -export type IModelsQueryParam = { query: string; bind_values: IModelsQueryBindValue[] }; -export type IModelsFormErrorTuple = [boolean, string]; -export type IModelsFormValidationTuple = [RegExp, string]; -export type IModelsSchemaErrors = { err_s: string[]; }; -export type IModelsForm = { - label?: string; - placeholder?: string; - validateKeypress?: boolean; - preventFocusRest?: boolean; - validation: RegExp; - charset: RegExp; - hidden?: boolean; - optional?: boolean; - default?: string | number; -}; - -export type IModelQueryFilterMapValuesTuplesOption = [IModelsQueryValue, IModelsQueryFilterOption]; -export type IModelQueryFilterMapValuesTuplesOptionList = [IModelsQueryValue[], IModelsQueryFilterOptionList]; -export type IModelQueryFilterMapValuesTuples = ModelQueryFilterMapTuple<IModelQueryFilterMapValuesTuplesOption> | ModelQueryFilterMapTuple<IModelQueryFilterMapValuesTuplesOptionList>; -export type IModelQueryFilterMapValues = IModelsQueryValue | IModelQueryFilterMapValuesTuples; - -export type ModelQueryFilterMapTupleBasis = - | [IModelsQueryValue, IModelsQueryFilterOption] - | [IModelsQueryValue, IModelsQueryFilterOption, IModelsQueryFilterCondition] - | [IModelsQueryValue[], IModelsQueryFilterOptionList] - | [IModelsQueryValue[], IModelsQueryFilterOptionList, IModelsQueryFilterCondition]; - -export type ModelQueryFilterMapTuple<T extends ModelQueryFilterMapTupleBasis> = - T extends [IModelsQueryValue, IModelsQueryFilterOption] - ? [IModelsQueryValue, IModelsQueryFilterOption, IModelsQueryFilterCondition] - : T extends [IModelsQueryValue[], IModelsQueryFilterOptionList] - ? [IModelsQueryValue[], IModelsQueryFilterOptionList, IModelsQueryFilterCondition] - : T; - -export type IModelQueryFilterMap<ModelFilter extends object> = { - [K in keyof ModelFilter]: ModelFilter[K] | [ModelFilter[K], IModelsQueryFilterOption] | [ModelFilter[K], IModelsQueryFilterOption, IModelsQueryFilterCondition] | [ModelFilter[K][], IModelsQueryFilterOptionList] | [ModelFilter[K][], IModelsQueryFilterOptionList, IModelsQueryFilterCondition]; -}; -export type IModelQueryFilterMapParsed = { query_values: string[]; bind_values: IModelsQueryValue[]; }; - -export const parse_model_query_value = (val: IModelsQueryValue): IModelsQueryBindValue => { - if (typeof val === `boolean`) return val ? '1' : '0'; - else if (typeof val === `number`) return val; - else if (typeof val === `string` && val) return val; - return null; -}; - -export const is_model_query_filter_option = (value: string): value is IModelsQueryFilterOption => { - return ['equals', 'starts-with', 'ends-with', 'contains', 'ne'].includes(value); -} - -export const is_model_query_filter_option_list = (value: string): value is IModelsQueryFilterOptionList => { - return ['between', 'in'].includes(value); -} - -export const is_model_query_values = (value: unknown): value is IModelsQueryValue => { - return typeof value === `string` || typeof value === `number` || typeof value === `boolean`; -} - -export const list_model_query_values_assert = (arr: (IModelsQueryValue | undefined)[]): (IModelsQueryValue)[] => { - return arr.filter((item): item is string | number | boolean => item !== undefined); -} - -export const parse_model_filter_map = <T extends object>(opts: IModelQueryFilterMap<T>): IModelQueryFilterMapParsed => { - const bind_values: IModelsQueryValue[] = []; - const query_values: string[] = []; - - for (const [index, entry] of Object.entries(opts).entries()) { - const [field, filters] = entry as [string, IModelQueryFilterMapValues]; - - if (is_model_query_values(filters)) { - query_values.push(`${field} = ?`); - bind_values.push(filters); - } else if (Array.isArray(filters)) { - const [filters_val, filters_opt] = filters; - const filter_condition = index === 0 ? `` : typeof filters[2] === `undefined` ? `AND ` : ` ${filters[2]}`; - if (is_model_query_values(filters_val) && is_model_query_filter_option(filters_opt)) { - switch (filters_opt) { - case `starts-with`: { - query_values.push(`${filter_condition}${field} LIKE ?`); - bind_values.push(`${filters[0]}%`); - } break; - case `ends-with`: { - query_values.push(`${filter_condition}${field} LIKE ?`); - bind_values.push(`%${filters[0]}`); - } break; - case `contains`: { - query_values.push(`${filter_condition}${field} LIKE ?`); - bind_values.push(`%${filters[0]}%`); - } break; - case `ne`: { - query_values.push(`${filter_condition}${field} != ?`); - bind_values.push(`${filters[0]}`); - } break; - case `equals`: { - query_values.push(`${filter_condition}${field} = ?`); - bind_values.push(filters[0]); - } break; - default: - throw new Error("util.model.parse_model_filter_map.invalid_condition"); - }; - } else if (is_model_query_filter_option_list(filters_opt)) { - switch (filters_opt) { - case `between`: { - query_values.push(`${filter_condition}${field} BETWEEN ? AND ?`); - bind_values.push(...filters[0].slice(0, 2)); - } break; - case `in`: { - query_values.push(`${filter_condition}${field} IN (${`? `.repeat(filters[0].length).trim().split(" ").join(", ")})`); - bind_values.push(...list_model_query_values_assert(filters[0])); - } break; - default: - throw new Error("util.model.parse_model_filter_map.invalid_condition"); - }; - } - } - } - if (!query_values.length) throw new Error("Error: invalid filter."); - if (!bind_values.length) throw new Error("Error: invalid filter."); - return { query_values, bind_values }; -}; diff --git a/utils/src/models/farm.ts b/utils/src/models/farm.ts @@ -1,20 +0,0 @@ -import type { LocationBasis } from "$root"; - -export type FarmExtended = { - farm: FarmBasis; - location?: LocationBasis; - lots?: FarmLotBasis[]; -}; - -export type FarmBasis = { - id: string; - name: string; - area?: string; - area_unit?: string; -}; - -export type FarmLotBasis = { - id: string; - location?: LocationBasis; -}; - diff --git a/utils/src/models/location.ts b/utils/src/models/location.ts @@ -1,7 +0,0 @@ -import { GeolocationAddress, GeolocationPoint } from "$root"; - -export type LocationBasis = { - id: string; - point: GeolocationPoint; - address?: GeolocationAddress; -}; -\ No newline at end of file diff --git a/utils/src/number.ts b/utils/src/number.ts @@ -1,13 +0,0 @@ -export const parse_int = (val: string, fallback: number = 0): number => { - const num = parseInt(val); - return isNaN(num) ? fallback : num; -}; - -export const parse_float = (val: string, fallback: number = 0): number => { - const num = parseFloat(val); - return isNaN(num) ? fallback : num; -}; - -export const num_str = (num: number): string => num.toString(); - -export const num_interval_range = (min: number, max: number): number => Math.floor(Math.random() * (max - min + 1)) + min; diff --git a/utils/src/object.ts b/utils/src/object.ts @@ -1,17 +0,0 @@ -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]) -}; - -export const obj_truthy_fields = (obj: Record<string, string>): boolean => { - return Object.values(obj).every(Boolean); -}; - -export const obj_result = (obj: any): string | undefined => { - if (`result` in obj && typeof obj.result === `string`) return obj.result; - return undefined; -}; - -export const obj_results_str = (obj: any): string[] | undefined => { - if (Array.isArray(obj.results)) return obj.results.map(String); - return undefined; -}; -\ No newline at end of file diff --git a/utils/src/response.ts b/utils/src/response.ts @@ -1,32 +0,0 @@ -import type { NotifyMessage } from "$root"; - -export const is_err_response = (response: any): response is { err: string } => { - return "err" in response && typeof response.err === "string"; -} - -export const is_pass_response = (response: any): response is { pass: true } => { - return "pass" in response && response.pass === true; -} - -export const is_result_response = (response: any): response is { result: string } => { - return "result" in response && typeof response.result === "string"; -} - -export const is_results_response = (response: any): response is { results: string[] } => { - return "results" in response && Array.isArray(response.results); -} - -export const is_error_response = (response: any): response is { error: string } => { - return "error" in response && response.error; -} - -export const is_message_response = (response: any): response is NotifyMessage => { - return ( - typeof response === "object" && - response !== null && - "message" in response && - typeof response.message === "string" && - (response.ok === undefined || typeof response.ok === "string") && - (response.cancel === undefined || typeof response.cancel === "string") - ); -}; -\ No newline at end of file diff --git a/utils/src/time.ts b/utils/src/time.ts @@ -1,5 +0,0 @@ -export const time_now_ms = (): number => Math.floor(new Date().getTime() / 1000); - -export const time_created_on = (): string => new Date().toISOString(); - -export const year_curr = (): string => new Date().getFullYear().toString(); -\ No newline at end of file diff --git a/utils/src/trade.ts b/utils/src/trade.ts @@ -1,233 +0,0 @@ -import { parse_int, parse_mass_unit, type MassUnit } from "$root"; - -export type TradeKey = `coffee` | `cacao` | `maca`; - -export type TradeQuantityMass = { - mass: number; - mass_unit: MassUnit; -} -export type TradeQuantity = TradeQuantityMass & { - label: string; -}; - -export const fmt_trade_quantity_tup = (obj: TradeQuantity): string => `${obj.mass}-${obj.mass_unit}-${obj.label}`; - -export const parse_trade_quantity_tup = (sel_key: string): TradeQuantity | undefined => { - const [qty_mass, qty_mass_u, qty_label] = sel_key.split(`-`); - const mass_unit = parse_mass_unit(qty_mass_u); - if (!qty_mass || !qty_mass_u || !qty_label || !mass_unit) return undefined; - return { - mass: parse_int(qty_mass, 1), - mass_unit, - label: qty_label - } -}; - -export const trade_keys: TradeKey[] = [`coffee`, `cacao`, `maca`] as const; - -const trade_quantity_default: TradeQuantity[] = [ - { - label: `bag`, - mass: 10, - mass_unit: `kg` - }, - { - label: `bag`, - mass: 5, - mass_unit: `kg` - }, - { - label: `bag`, - mass: 25, - mass_unit: `kg` - }, -]; - -const TRADE_PROCESS_DEFAULT = `natural`; - -//@todo -const trade_process_default: string[] = [ - TRADE_PROCESS_DEFAULT, - `dried`, - `roasted` -]; - -export type TradeParam = { - default: { - quantity: TradeQuantity[]; - process: string[]; - }, - key: Record<TradeKey, { - quantity: TradeQuantity[]; - process: string[]; - flavor: string[]; - }>; -}; - -export const trade: TradeParam = { - default: { - quantity: trade_quantity_default, - process: trade_process_default, - }, - key: { - coffee: { - quantity: [ - { - label: `bag`, - mass: 60, - mass_unit: `kg` - }, - { - label: `bag`, - mass: 69, - mass_unit: `kg` - }, - { - label: `bag`, - mass: 30, - mass_unit: `kg` - }, - ], - process: [ - `washed`, - `natural`, - `honey`, - `semi_washed`, - `wet_hulled`, - `dry`, - `pulped_natural`, - `carbonic_maceration` - ], - flavor: [ - `natural`, - `bergamot`, - `jasmine honeysuckle`, - `orange`, - `blueberry`, - `apricot`, - `black tea`, - `raspberry`, - `nougat`, - `milk chocolate`, - `peach`, - `vanilla`, - `berry`, - `nut`, - `brown sugar`, - `grape`, - `raisin`, - `red apple`, - `sweet bread pastry`, - `pineapple`, - `star fruit`, - `mango`, - `grapefruit`, - `nectarine`, - `red grape`, - `maple syrup`, - `dark chocolate`, - `orange blossom`, - `marshmallow`, - `mandarin`, - `dried dates`, - `prune`, - `green apple`, - `licorice/anise`, - `cranberry`, - `caramel`, - `chocolate`, - `lemon`, - `golden raisin`, - `black cherry`, - `plum`, - `black currant`, - `roses`, - `cola`, - `banana`, - `red currant`, - `white grape`, - `green tea`, - `lychee`, - `tamarind`, - `dried fig`, - `green grape`, - `sugar cane`, - `cherry`, - `magnolia`, - `tropical fruit`, - `ctrius`, - `stronefruit` - ], - }, - cacao: { - quantity: [ - ...trade_quantity_default - ], - process: [ - `raw`, - `fermented`, - `dried`, - `roasted`, - `cocoa_powder`, - `cocoa_butter`, - `chocolate` - ], - flavor: [] - }, - maca: { - quantity: [ - { - label: `bag`, - mass: 1, - mass_unit: `kg` - }, - { - label: `bag`, - mass: 100, - mass_unit: `g` - }, - ...trade_quantity_default - ], - process: [ - `raw`, - `powdered`, - `roasted`, - `gelatinized`, - `capsules` - ], - flavor: [] - } - } -}; - -export function parse_trade_key(val?: string): TradeKey | undefined { - switch (val) { - case "coffee": - case "cacao": - case "maca": - return val; - default: - return undefined; - }; -}; - -export const trade_key_default_process = (val?: string): string => { - const key = parse_trade_key(val); - if (!key) return TRADE_PROCESS_DEFAULT; - return trade.key[key].process[0]; -}; - -export function parse_trade_mass_tuple(val?: string): [number, MassUnit, string] | undefined { - if (!val) return; - const vals = val.split('-'); - if (vals.length !== 3) return; - const mass = vals[0]; - const mass_unit = vals[1]; - const label = vals[2]; - const amt = parseInt(mass || `0`, 10); - if (isNaN(amt) || amt <= 0) return; - const units = parse_mass_unit(mass_unit); - if (!units) return; - if (typeof label !== `string` || !label) return; - return [amt, units, label] -} -\ No newline at end of file diff --git a/utils/src/types.ts b/utils/src/types.ts @@ -1,41 +0,0 @@ -import { ErrorMessage } from "$root"; - -export type FieldRecord = Record<string, string>; -export type NotifyMessage = { - message: string; - ok?: string; - cancel?: string; -}; - -export type GeometryPoint = { - type: string; - coordinates: number[]; -}; - -export type GeometryPolygon = { - type: string; - coordinates: number[][][]; -}; - -export type GeolocationPointTuple = [number, number]; - -export type ResultId = { id: string; }; -export type ResultPass = { pass: true; }; -export type ResultsList<T> = { results: T[]; }; -export type ResultObj<T> = { result: T; }; -export type ResultPublicKey = { public_key: string; }; -export type ResultSecretKey = { secret_key: string; }; - -export type ResolveError<T> = T | ErrorMessage<string>; -export type ResolveErrorMsg<TRes, TMsg extends string> = TRes | ErrorMessage<TMsg>; - -export type FileBytesFormat = `kb` | `mb` | `gb`; -export type FileMimeType = string; -export type FilePath = { file_path: string; file_name: string; mime_type: FileMimeType; } - -export type CallbackPromise = () => Promise<void>; -export type CallbackPromiseFigureResult<Ti, Tr> = (value: Ti) => Promise<Tr | undefined>; -export type CallbackPromiseFull<Ti, Tr> = (value: Ti) => Promise<Tr>; -export type CallbackPromiseGeneric<T> = (value: T) => Promise<void>; -export type CallbackPromiseReturn<T> = () => Promise<T>; -export type CallbackPromiseResult<Tr> = () => Promise<Tr | undefined>; -\ No newline at end of file diff --git a/utils/src/types/lib.ts b/utils/src/types/lib.ts @@ -0,0 +1,18 @@ +export type CallbackPromise = () => Promise<void>; +export type CallbackPromiseFigureResult<Ti, Tr> = (value: Ti) => Promise<Tr | undefined>; +export type CallbackPromiseFull<Ti, Tr> = (value: Ti) => Promise<Tr>; +export type CallbackPromiseGeneric<T> = (value: T) => Promise<void>; +export type CallbackPromiseReturn<T> = () => Promise<T>; +export type CallbackPromiseResult<Tr> = () => Promise<Tr | undefined>; + +export type ErrorMessage<T extends string> = { err: T }; + +export type ResultId = { id: string; }; +export type ResultPass = { pass: true; }; +export type ResultsList<T> = { results: T[]; }; +export type ResultObj<T> = { result: T; }; +export type ResultPublicKey = { public_key: string; }; +export type ResultSecretKey = { secret_key: string; }; + +export type ResolveError<T> = T | ErrorMessage<string>; +export type ResolveErrorMsg<TRes, TMsg extends string> = TRes | ErrorMessage<TMsg>; diff --git a/utils/src/unit.ts b/utils/src/unit.ts @@ -1,56 +0,0 @@ -import type { zf_area_unit, zf_mass_unit } from "$root"; -import { z } from "zod"; - -export type AreaUnit = z.infer<typeof zf_area_unit>; -export const area_units: AreaUnit[] = [`ac`, `ha`, `ft2`, `m2`] as const; - -export type MassUnit = z.infer<typeof zf_mass_unit>; -export const mass_units: MassUnit[] = [`kg`, `lb`, `g`] as const; - -export function parse_mass_unit_default(val?: string): MassUnit { - const unit = parse_mass_unit(val); - return unit ?? `kg` -} - -export function parse_mass_unit(val?: string): MassUnit | undefined { - switch (val) { - case `kg`: - case `lb`: - case `g`: - return val; - default: - return undefined; - }; -}; - -export function mass_to_g(val: number, unit: string): number { - const mass_unit = parse_mass_unit(unit); - switch (mass_unit) { - case `kg`: - return val * 1000; - case `lb`: - return val * 453.592; - case `g`: - return val; - default: - throw new Error(`unsupported unit ${unit}`); - } -} - - -export function parse_area_unit_default(val?: string): AreaUnit { - const unit = parse_area_unit(val); - return unit ?? `ac` -} - -export function parse_area_unit(val?: string): AreaUnit | undefined { - switch (val) { - case `ac`: - case `ha`: - case `ft2`: - case `m2`: - return val; - default: - return undefined; - }; -}; diff --git a/utils/src/uuid.ts b/utils/src/uuid.ts @@ -1,5 +0,0 @@ -import { v4 } from "uuid"; - -export function uuidv4() { - return v4(); -}; -\ No newline at end of file diff --git a/utils/tsconfig.base.json b/utils/tsconfig.base.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2019", + "strict": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "sourceMap": false, + "rootDir": "src" + } +} diff --git a/utils/tsconfig.cjs.json b/utils/tsconfig.cjs.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist/cjs", + "module": "CommonJS", + "declaration": false, + "moduleResolution": "Node" + }, + "include": ["src"] +} diff --git a/utils/tsconfig.esm.json b/utils/tsconfig.esm.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist/esm", + "module": "NodeNext", + "declaration": true, + "declarationDir": "./dist/types", + "moduleResolution": "NodeNext", + "emitDeclarationOnly": false + }, + "include": ["src"] +} diff --git a/utils/tsconfig.json b/utils/tsconfig.json @@ -1,28 +0,0 @@ -{ - "compilerOptions": { - "strict": true, - "target": "es2021", - "lib": [ - "es2021", - "dom" - ], - "module": "ESNext", - "moduleResolution": "node", - "declaration": true, - "declarationMap": true, - "outDir": "./dist", - "esModuleInterop": true, - "skipLibCheck": true, - "baseUrl": ".", - "paths": { - "$root": ["src/index.js"] - } - }, - "include": [ - "src" - ], - "exclude": [ - "node_modules", - "dist" - ], -} -\ No newline at end of file diff --git a/utils/tsup.config.ts b/utils/tsup.config.ts @@ -1,11 +0,0 @@ -import { defineConfig } from "tsup"; - -export default defineConfig({ - entry: ['src/index.ts'], - format: ['esm', 'cjs'], - dts: true, - outDir: 'dist', - splitting: false, - clean: true, - sourcemap: true, -});