import BaseAdapter , { AbstractBaseAdapter , StorageProxy } from "./base" ; import { RecordStatus } from "../types" ; import { getDeepKey } from "../utils" ; export default class Memory < B extends { id : string ; last_modified ? : number ; _status ? : RecordStatus ; } > extends BaseAdapter < B > implements AbstractBaseAdapter < B > { private _records : { [ key ] : B ; } ; private _lastModified : number | null ; private _metadata : { [ key ] : any ; } | null ; constructor ( ) { super ( ) ; this . _records = { } ; this . _lastModified = null ; this . _metadata = null ; } public async clear ( ) { this . _records = { } ; } public async get ( id ) { if ( this . _records . hasOwnProperty ( id ) ) { return this . _records [ id ] ; } throw new Error ( `Record with id ${ id } not found` ) ; } public async list ( params ) { return Object . values ( this . _records ) . filter ( ( record ) => { if ( ! params . filters ) { return true ; } let matches = true ; Object . entries ( params . filters ) . forEach ( ( [ key , value ] ) => { const recordValue = getDeepKey ( record , key ) ; if ( Array . isArray ( value ) ) { if ( ! value . includes ( recordValue ) ) { matches = false ; } } else if ( recordValue !== value ) { matches = false ; } } ) ; return matches ; } ) . sort ( ( a , b ) => { let descendingOrder = true ; let key = "last_modified" ; if ( params . order ) { descendingOrder = params . order . startsWith ( "-" ) ; key = descendingOrder ? params . order . substring ( 1 ) : params . order ; } if ( a [ key as keyof B ] < b [ key as keyof B ] ) { return descendingOrder ? 1 : - 1 ; } else if ( a [ key as keyof B ] > b [ key as keyof B ] ) { return descendingOrder ? - 1 : 1 ; } return 0 ; } ) ; } public async execute < T > ( callback , options = { preload : [ ] } ) { const originalRecords = { ... this . _records } ; const rollback = ( ) => { this . _records = { ... originalRecords } ; } ; let result ; const runCallback = ( preloaded = { } ) => { const proxy = this . proxy ( preloaded ) ; const returned = callback ( proxy ) ; result = returned ; } ; try { if ( ! options . preload ) { runCallback ( ) ; } else { const preloadedRecords = options . preload . map ( ( id ) => { return this . _records [ id . toString ( ) ] ; } ) . filter ( Boolean ) . reduce < { [ key ] : B ; } > ( ( acc , record ) => { acc [ record . id ] = record ; return acc ; } , { } ) ; runCallback ( preloadedRecords ) ; } } catch ( err ) { rollback ( ) ; throw err ; } return result ! ; } public async loadDump ( records ) { return this . importBulk ( records ) ; } public async importBulk ( records ) { try { await this . execute ( ( transaction ) => { records . forEach ( ( record ) => { transaction . update ( record ) ; } ) ; } ) ; const previousLastModified = await this . getLastModified ( ) ; const lastModified = Math . max ( ... records . map ( ( record ) => record . last_modified ! ) ) ; if ( previousLastModified && lastModified > previousLastModified ) { await this . saveLastModified ( lastModified ) ; } return records ; } catch ( e ) { throw new Error ( "error while importing" ) ; } return [ ] ; } public async saveLastModified ( lastModified ) { this . _lastModified = lastModified ; return this . _lastModified ; } public async getLastModified ( ) { return this . _lastModified ; } public async saveMetadata ( metadata ) { this . _metadata = metadata ; return this . _metadata ; } public async getMetadata < T > ( ) { return this . _metadata as T ; } private proxy ( preloaded = { } ) { const create = ( record ) => { if ( this . _records . hasOwnProperty ( record . id ) ) { throw new Error ( `record with id ${ record . id } already exists` ) ; } this . _records [ record . id ] = record ; } ; const update = ( record ) => { this . _records [ record . id ] = record ; return record . id ; } ; const _delete = ( id ) => { delete this . _records [ id ] ; } ; const get = ( id ) => { return preloaded [ id ] ; } ; return { create , update , delete : _delete , get , } ; } }