wallet-storage
Version:
BRC100 conforming wallet, wallet storage and wallet signer components
223 lines (201 loc) • 6.91 kB
text/typescript
import {
sdk,
table,
validateSecondsSinceEpoch,
verifyOneOrNone,
verifyTruthy
} from '../index.client'
import { getSyncChunk } from './methods/getSyncChunk'
/**
* The `StorageReader` abstract class is the base of the concrete wallet storage provider classes.
*
* It is the minimal interface required to read all wallet state records and is the base class for sync readers.
*
* The next class in the heirarchy is the `StorageReaderWriter` which supports sync readers and writers.
*
* The last class in the heirarchy is the `Storage` class which supports all active wallet operations.
*
* The ability to construct a properly configured instance of this class implies authentication.
* As such there are no user specific authenticated access checks implied in the implementation of any of these methods.
*/
export abstract class StorageReader implements sdk.StorageSyncReader {
chain: sdk.Chain
_settings?: table.Settings
whenLastAccess?: Date
get dbtype(): DBType | undefined {
return this._settings?.dbtype
}
constructor(options: StorageReaderOptions) {
this.chain = options.chain
}
isAvailable(): boolean {
return !!this._settings
}
async makeAvailable(): Promise<table.Settings> {
return (this._settings = await this.readSettings())
}
getSettings(): table.Settings {
if (!this._settings)
throw new sdk.WERR_INVALID_OPERATION(
'must call "makeAvailable" before accessing "settings"'
)
return this._settings
}
isStorageProvider(): boolean {
return false
}
abstract destroy(): Promise<void>
abstract transaction<T>(
scope: (trx: sdk.TrxToken) => Promise<T>,
trx?: sdk.TrxToken
): Promise<T>
abstract readSettings(trx?: sdk.TrxToken): Promise<table.Settings>
abstract findCertificateFields(
args: sdk.FindCertificateFieldsArgs
): Promise<table.CertificateField[]>
abstract findCertificates(
args: sdk.FindCertificatesArgs
): Promise<table.Certificate[]>
abstract findCommissions(
args: sdk.FindCommissionsArgs
): Promise<table.Commission[]>
abstract findMonitorEvents(
args: sdk.FindMonitorEventsArgs
): Promise<table.MonitorEvent[]>
abstract findOutputBaskets(
args: sdk.FindOutputBasketsArgs
): Promise<table.OutputBasket[]>
abstract findOutputs(args: sdk.FindOutputsArgs): Promise<table.Output[]>
abstract findOutputTags(
args: sdk.FindOutputTagsArgs
): Promise<table.OutputTag[]>
abstract findSyncStates(
args: sdk.FindSyncStatesArgs
): Promise<table.SyncState[]>
abstract findTransactions(
args: sdk.FindTransactionsArgs
): Promise<table.Transaction[]>
abstract findTxLabels(args: sdk.FindTxLabelsArgs): Promise<table.TxLabel[]>
abstract findUsers(args: sdk.FindUsersArgs): Promise<table.User[]>
abstract countCertificateFields(
args: sdk.FindCertificateFieldsArgs
): Promise<number>
abstract countCertificates(args: sdk.FindCertificatesArgs): Promise<number>
abstract countCommissions(args: sdk.FindCommissionsArgs): Promise<number>
abstract countMonitorEvents(args: sdk.FindMonitorEventsArgs): Promise<number>
abstract countOutputBaskets(args: sdk.FindOutputBasketsArgs): Promise<number>
abstract countOutputs(args: sdk.FindOutputsArgs): Promise<number>
abstract countOutputTags(args: sdk.FindOutputTagsArgs): Promise<number>
abstract countSyncStates(args: sdk.FindSyncStatesArgs): Promise<number>
abstract countTransactions(args: sdk.FindTransactionsArgs): Promise<number>
abstract countTxLabels(args: sdk.FindTxLabelsArgs): Promise<number>
abstract countUsers(args: sdk.FindUsersArgs): Promise<number>
abstract getProvenTxsForUser(
args: sdk.FindForUserSincePagedArgs
): Promise<table.ProvenTx[]>
abstract getProvenTxReqsForUser(
args: sdk.FindForUserSincePagedArgs
): Promise<table.ProvenTxReq[]>
abstract getTxLabelMapsForUser(
args: sdk.FindForUserSincePagedArgs
): Promise<table.TxLabelMap[]>
abstract getOutputTagMapsForUser(
args: sdk.FindForUserSincePagedArgs
): Promise<table.OutputTagMap[]>
async findUserByIdentityKey(key: string): Promise<table.User | undefined> {
return verifyOneOrNone(
await this.findUsers({ partial: { identityKey: key } })
)
}
async getSyncChunk(args: sdk.RequestSyncChunkArgs): Promise<sdk.SyncChunk> {
return getSyncChunk(this, args)
}
/**
* Force dates to strings on SQLite and Date objects on MySQL
* @param date
* @returns
*/
validateEntityDate(date: Date | string | number): Date | string {
if (!this.dbtype)
throw new sdk.WERR_INTERNAL(
'must call verifyReadyForDatabaseAccess first'
)
let r: Date | string = this.validateDate(date)
switch (this.dbtype) {
case 'MySQL':
break
case 'SQLite':
r = r.toISOString()
break
default:
throw new sdk.WERR_INTERNAL(`Invalid dateScheme ${this.dbtype}`)
}
return r
}
/**
*
* @param date
* @param useNowAsDefault if true and date is null or undefiend, set to current time.
* @returns
*/
validateOptionalEntityDate(
date: Date | string | number | null | undefined,
useNowAsDefault?: boolean
): Date | string | undefined {
if (!this.dbtype)
throw new sdk.WERR_INTERNAL(
'must call verifyReadyForDatabaseAccess first'
)
let r: Date | string | undefined = this.validateOptionalDate(date)
if (!r && useNowAsDefault) r = new Date()
switch (this.dbtype) {
case 'MySQL':
break
case 'SQLite':
if (r) r = r.toISOString()
break
default:
throw new sdk.WERR_INTERNAL(`Invalid dateScheme ${this.dbtype}`)
}
return r
}
validateDate(date: Date | string | number): Date {
let r: Date
if (date instanceof Date) r = date
else r = new Date(date)
return r
}
validateOptionalDate(
date: Date | string | number | null | undefined
): Date | undefined {
if (date === null || date === undefined) return undefined
return this.validateDate(date)
}
validateDateForWhere(date: Date | string | number): Date | string | number {
if (!this.dbtype)
throw new sdk.WERR_INTERNAL(
'must call verifyReadyForDatabaseAccess first'
)
if (typeof date === 'number') date = validateSecondsSinceEpoch(date)
const vdate = verifyTruthy(this.validateDate(date))
let r: Date | string | number
switch (this.dbtype) {
case 'MySQL':
r = vdate
break
case 'SQLite':
r = vdate.toISOString()
break
default:
throw new sdk.WERR_INTERNAL(`Invalid dateScheme ${this.dbtype}`)
}
return r
}
}
export interface StorageReaderOptions {
chain: sdk.Chain
}
export type DBType = 'SQLite' | 'MySQL'
type DbEntityTimeStamp<T extends sdk.EntityTimeStamp> = {
[K in keyof T]: T[K] extends Date ? Date | string : T[K]
}