UNPKG

wowok_agent

Version:

Agent for WoWok: Unlock Co-Creation, Lighting Transaction, Empower Potential.

296 lines (255 loc) 9.17 kB
/** * manage address name and tags locally */ import path from "path"; import os from "os"; import { Level } from "level"; import { isBrowser, retry_db } from "../common.js"; import { ERROR, Errors, IsValidAddress, TagName } from "wowok"; export interface MarkData { address: string; tags?: string[]; } export interface InfoData { default: string; others?: string[]; } export interface LocalMarkFilter { name?: string; tags?: string[]; address?: string; } export const LocalMarkLocation = 'wowok-mark'; export const LocalInfoLocation = 'wowok-info'; export const LocalMarkNameMaxLength = 32; export const LocalInfoNameDefault = 'Address of delivery'; export class LocalMark { static _instance: any; private location: string; constructor() { this.location = LocalMarkLocation; if (!isBrowser()) { this.location = path.join(path.join(os.homedir(), '.wowok'), LocalMarkLocation); } } static Instance() : LocalMark { if (!LocalMark._instance) { LocalMark._instance = new LocalMark(); }; return LocalMark._instance } // useAddressIfNameExist true: use address as the name if the name exist; // otherwise, use this name and change the original name to its address. async put(name:string | undefined | null, mark:MarkData, useAddressIfNameExist?:boolean) : Promise<string> { // object address invalid if (!IsValidAddress(mark.address) && mark.address !== '0x2' && mark.address !== '0x6') { ERROR(Errors.InvalidParam, `LocalMark.put.mark.address: ${mark.address}`) }; return await retry_db (this.location, async(storage:Level) => { // use address as name if name is undefined or null if (name === undefined || name === null) { await storage.put(mark.address, JSON.stringify(mark)); return mark.address } if (name.length > LocalMarkNameMaxLength) { name = name.substring(0, LocalMarkNameMaxLength); }; const r = await storage.get(name); if (r) { if (useAddressIfNameExist) { await storage.put(mark.address, JSON.stringify(mark)); return mark.address } else { const obj = JSON.parse(r) as MarkData; await storage.put(obj.address, r) } } await storage.put(name, JSON.stringify(mark)); return name }) } async get(name?: string|null) : Promise<MarkData | undefined> { if (name === undefined || name === null) { return undefined; } return await retry_db(this.location, async(storage:Level) => { const r = await storage.get(name); if (r) { return JSON.parse(r); } }) } async get_address(name_or_address?: string | null) : Promise<string | undefined> { if (IsValidAddress(name_or_address)) { return name_or_address!; } if (name_or_address !== undefined && name_or_address !== null) { return (await this.get(name_or_address))?.address; } } async get_many_address(name_or_addresses: (string | null | undefined)[]) : Promise<(string | undefined)[]> { const check = (v: string | null | undefined) : boolean => { return v!==undefined && v!==null && !IsValidAddress(v) } const q = await retry_db(this.location, async(storage:Level) => { return (await storage.getMany(name_or_addresses.filter(v => check(v)) as string[])); }); return name_or_addresses.map(v => { if (check(v)) { const r = q.shift(); if (r) { return JSON.parse(q.shift()!)?.address; } } return v }) } async get_many_address2(name_or_addresses: (string | null | undefined)[]) : Promise<string[]> { return (await this.get_many_address(name_or_addresses)).filter((v):v is string => v!==undefined && v!== null) as string[] } async del(name:string) { await retry_db(this.location, async(storage:Level) => { await storage.del(name); }); } async clear() { await retry_db(this.location, async(storage:Level) => { await storage.clear(); }) } async rename(name:string, new_name:string) : Promise<boolean> { if (new_name.length > LocalMarkNameMaxLength) { new_name = new_name.substring(0, LocalMarkNameMaxLength); }; return await retry_db(this.location, async(storage:Level) => { const r = (await storage.getMany([name, new_name])); if (r[0] && !r[1]) { await storage.put(new_name, r[0]); await storage.del(name); return true; } return false; }) } async swap_name(name1:string, name2:string) : Promise<boolean> { return await retry_db(this.location, async(storage:Level) => { const r = await storage.getMany([name1, name2]); if (r[0] && r[1]) { await storage.put(name1, r[1]); await storage.put(name2, r[0]); return true; } return false; }) } async set_tags(name:string, tags:string[] | undefined) : Promise<boolean> { return await retry_db(this.location, async(storage:Level) => { const r = await storage.get(name); if (r) { const obj = JSON.parse(r) as MarkData; obj.tags = tags; await storage.put(name, JSON.stringify(obj)); return true; } return false; }) } async list(filter?: LocalMarkFilter) : Promise<QueryNameData[]> { if (filter && filter.tags) filter.tags = filter.tags.filter(v => v !== '' && v); return await retry_db(this.location, async(storage:Level) => { return (await storage.iterator().all()).filter(v => { const obj = JSON.parse(v[1]) as MarkData; if (filter?.name && v[0] !== filter.name) return false; if (filter?.address && obj.address !== filter.address) return false; if (filter?.tags && filter.tags.length > 0) { if (!obj.tags || obj.tags.length === 0) return false; for (let i = 0; i < filter.tags.length; ++ i) { if (!obj.tags.includes(filter.tags[i])) { return false } } } return true; }).map(v => {return {name:v[0], data:v[1]}}); }) } } export interface QueryNameData { name: string; data: any; }; export class LocalInfo { static _instance: any; private location: string; constructor() { this.location = LocalInfoLocation; if (!isBrowser()) { this.location = path.join(path.join(os.homedir(), '.wowok'), LocalInfoLocation); } } static Instance() : LocalInfo { if (!LocalInfo._instance) { LocalInfo._instance = new LocalInfo(); }; return LocalInfo._instance } async put(name:string = LocalInfoNameDefault, content:string, bDefault:boolean=true) : Promise<void> { await retry_db(this.location, async(storage:Level) => { const r = await storage.get(name); if (r) { const obj = JSON.parse(r) as InfoData; obj.others = obj.others ? [...obj.others, content] : [content]; if (bDefault) { obj.default = content; } await storage.put(name, JSON.stringify(obj)); } else { const obj : InfoData = {default:content, others:[content]}; await storage.put(name, JSON.stringify(obj)); } }) } async get(name: string = LocalInfoNameDefault) : Promise<InfoData | undefined> { return await retry_db(this.location, async(storage:Level) => { const r = (await storage.get(name)); if (r) { return JSON.parse(r); } }) } async get_default(name: string = LocalInfoNameDefault) : Promise<string | undefined> { return await retry_db(this.location, async(storage:Level) => { const r = await storage.get(name); if (r) { return (JSON.parse(r) as InfoData)?.default; } }) } async del(name:string = LocalInfoNameDefault) : Promise<void> { return await retry_db(this.location, async(storage:Level) => { await storage.del(name); }) } async del_content(name:string = LocalInfoNameDefault, index:number) : Promise<boolean> { return await retry_db(this.location, async(storage:Level) => { const r = await storage.get(name); if (r) { const obj = JSON.parse(r) as InfoData; if (obj.others && index < obj.others.length) { obj.others.splice(index, 1); await storage.put(name, JSON.stringify(obj)); return true; } } return false }) } async clear() { return await retry_db(this.location, async(storage:Level) => { await storage.clear(); }) } async list() : Promise<QueryNameData[]> { return await retry_db(this.location, async(storage:Level) => { return (await storage.iterator().all()).map(v => {return {name:v[0], data:v[1]}}); }) } }