import type { CRUDEngine } from './CRUDEngine' ; import { RecordAlreadyExistsError , RecordNotFoundError , RecordTypeError } from './error/' ; export type MemoryStore = Record < string , Record < string , any > > ; export class MemoryEngine implements CRUDEngine { public storeName = '' ; private readonly stores : MemoryStore = { } ; private autoIncrementedPrimaryKey : number = 1 ; create < EntityType , PrimaryKey = string > ( tableName , primaryKey , entity ) { if ( entity ) { this . prepareTable ( tableName ) ; if ( primaryKey === undefined ) { primaryKey = this . autoIncrementedPrimaryKey as unknown as PrimaryKey ; this . autoIncrementedPrimaryKey += 1 ; } const record = this . stores [ this . storeName ] [ tableName ] [ primaryKey ] ; if ( record ) { const message = `Record "${ primaryKey }" already exists in "${ tableName }". You need to delete the record first if you want to overwrite it.` ; const error = new RecordAlreadyExistsError ( message ) ; return Promise . reject ( error ) ; } this . stores [ this . storeName ] [ tableName ] [ primaryKey ] = entity ; return Promise . resolve ( primaryKey ) ; } const message = `Record "${ primaryKey }" cannot be saved in "${ tableName }" because it's "undefined" or "null".` ; return Promise . reject ( new RecordTypeError ( message ) ) ; } public async clearTables ( ) { const tableNames = Object . keys ( this . stores ) ; await Promise . all ( tableNames . map ( tableName => this . deleteAll ( tableName ) ) ) ; } public async delete < PrimaryKey = string > ( tableName , primaryKey ) { this . prepareTable ( tableName ) ; delete this . stores [ this . storeName ] [ tableName ] [ primaryKey ] ; return primaryKey ; } public async deleteAll ( tableName ) { delete this . stores [ this . storeName ] [ tableName ] ; return true ; } public async init ( storeName ) { return this . assignDb ( storeName , { } ) ; } public async initWithObject < ObjectType = Object > ( storeName , object ) { return this . assignDb ( storeName , object ) ; } private assignDb < ObjectType = Object > ( storeName , object ) { this . storeName = storeName ; this . stores [ this . storeName ] = this . stores [ this . storeName ] || object ; return this . stores ; } public async isSupported ( ) { } public async purge ( ) { delete this . stores [ this . storeName ] ; } public read < EntityType = Object , PrimaryKey = string > ( tableName , primaryKey ) { this . prepareTable ( tableName ) ; if ( this . stores [ this . storeName ] [ tableName ] . hasOwnProperty ( primaryKey ) ) { return Promise . resolve ( this . stores [ this . storeName ] [ tableName ] [ primaryKey ] ) ; } const message = `Record "${ primaryKey }" in "${ tableName }" could not be found.` ; return Promise . reject ( new RecordNotFoundError ( message ) ) ; } public readAll < T > ( tableName ) { this . prepareTable ( tableName ) ; const promises = [ ] ; for ( const primaryKey of Object . keys ( this . stores [ this . storeName ] [ tableName ] ) ) { promises . push ( this . read ( tableName , primaryKey ) ) ; } return Promise . all ( promises ) ; } public readAllPrimaryKeys ( tableName ) { this . prepareTable ( tableName ) ; return Promise . resolve ( Object . keys ( this . stores [ this . storeName ] [ tableName ] ) ) ; } public async update < PrimaryKey = string , ChangesType = Object > ( tableName , primaryKey , changes ) { this . prepareTable ( tableName ) ; const entity = await this . read ( tableName , primaryKey ) ; const updatedEntity = { ... entity , ... changes } ; this . stores [ this . storeName ] [ tableName ] [ primaryKey ] = updatedEntity ; return primaryKey ; } async updateOrCreate < PrimaryKey = string , ChangesType = Object > ( tableName , primaryKey , changes ) { this . prepareTable ( tableName ) ; try { await this . update ( tableName , primaryKey , changes ) ; return primaryKey ; } catch ( error ) { if ( error instanceof RecordNotFoundError ) { const newPrimaryKey = await this . create ( tableName , primaryKey , changes ) ; return newPrimaryKey ; } throw error ; } } private prepareTable ( tableName ) { if ( ! this . stores [ this . storeName ] [ tableName ] ) { this . stores [ this . storeName ] [ tableName ] = { } ; } } }