import { each , IIterator , map , toArray , toObject } from '@lumino/algorithm' ; import { UUID } from '@lumino/coreutils' ; import { Datastore , Schema , Record , validateSchema } from '@lumino/datastore' ; import { IMessageHandler , Message , MessageLoop , ConflatableMessage } from '@lumino/messaging' ; import { ISignal , Signal } from '@lumino/signaling' ; import { IDatastore } from '../interface' ; import { Table } from './table' ; export class Dummystore implements IDatastore , IMessageHandler { static create ( options ) { let { schemas } = options ; Private . validateSchemas ( schemas ) ; let context = { inTransaction : false , transactionId : '' , version : 0 , storeId : 0 , change : { } , patch : { } , } ; let tables = { } as { [ key ] : Table < Schema > ; } ; if ( options . restoreState ) { let state = JSON . parse ( options . restoreState ) ; each ( schemas , s => { tables [ s . id ] = Table . recreate ( s , context , state [ s . id ] || [ ] ) ; } ) ; } else { each ( schemas , s => { tables [ s . id ] = Table . create ( s , context ) ; } ) ; } return new Dummystore ( context , tables ) ; } /**
     * Dispose of the resources held by the datastore.
     */ dispose ( ) { if ( this . _disposed ) { return ; } this . _disposed = true ; Signal . clearData ( this ) ; } /**
     * Whether the datastore has been disposed.
     */ get isDisposed ( ) : boolean { return this . _disposed ; } /**
     * A signal emitted when changes are made to the store.
     *
     * #### Notes
     * This signal is emitted either at the end of a local mutation,
     * or after a remote mutation has been applied. The storeId can
     * be used to determine its source.
     *
     * The payload represents the set of local changes that were made
     * to bring the store to its current state.
     */ get changed ( ) : ISignal < IDatastore , Datastore . IChangedArgs > { return this . _changed ; } /**
     * The unique id of the store.
     *
     * #### Notes
     * The id is unique among all other collaborating peers.
     */ get id ( ) : number { return this . _context . storeId ; } /**
     * Whether a transaction is currently in progress.
     */ get inTransaction ( ) : boolean { return this . _context . inTransaction ; } /**
     * The current version of the datastore.
     *
     * #### Notes
     * This version is automatically increased for each transaction
     * to the store. However, it might not increase linearly (i.e.
     * it might make jumps).
     */ get version ( ) : number { return this . _context . version ; } returns iter ( ) { return map ( Object . keys ( this . _tables ) , key => this . _tables [ key ] ) ; } param schema returns throws get < S extends Schema > ( schema ) { let t = this . _tables [ schema . id ] ; if ( t === undefined ) { throw new Error ( `No table found for schema with id: ${ schema . id }` ) ; } return t as Table < S > ; } returns throws beginTransaction ( ) { let newVersion = this . _context . version + 1 ; let id = UUID . uuid4 ( ) ; this . _initTransaction ( id , newVersion ) ; MessageLoop . postMessage ( this , new ConflatableMessage ( 'transaction-begun' ) ) ; return id ; } /**
     * Completes a transaction.
     *
     * #### Notes
     * This completes a transaction previously started with
     * `beginTransaction`. If a change has occurred, the
     * `changed` signal will be emitted.
     */ endTransaction ( ) { this . _finalizeTransaction ( ) ; let { change , storeId , transactionId } = this . _context ; if ( ! Private . isChangeEmpty ( this . _context . change ) ) { this . _changed . emit ( { storeId , transactionId , type : 'transaction' , change , } ) ; } } /**
     * Handle a message.
     */ processMessage ( msg ) { switch ( msg . type ) { case 'transaction-begun' : if ( this . _context . inTransaction ) { console . warn ( `Automatically ending transaction (did you forget to end it?): ${ this . _context . transactionId }` ) ; this . endTransaction ( ) ; } break ; default : break ; } } param transactionId returns throws undo ( transactionId ) { throw Error ( 'Dummy store does not support undo' ) ; } param transactionId returns throws redo ( transactionId ) { throw Error ( 'Dummy store does not support redo' ) ; } returns toString ( ) { return JSON . stringify ( toObject ( map ( this , ( table ) : [ string , Record < Schema > [ ] ] => { return [ table . schema . id , toArray ( table ) ] ; } ) ) ) ; } param id param tables private constructor ( context , tables ) { this . _context = context ; this . _tables = tables ; } param id param newVersion throws private _initTransaction ( id , newVersion ) { let context = this . _context as Private . MutableContext ; if ( context . inTransaction ) { throw new Error ( `Already in a transaction: ${ this . _context . transactionId }` ) ; } context . inTransaction = true ; context . change = { } ; context . transactionId = id ; context . version = newVersion ; } throws private _finalizeTransaction ( ) { let context = this . _context as Private . MutableContext ; if ( ! context . inTransaction ) { throw new Error ( 'No transaction in progress.' ) ; } context . inTransaction = false ; } private _disposed = false ; private _tables : { [ key ] : Table < Schema > ; } ; private _context : Datastore . Context ; private _changed = new Signal < IDatastore , Datastore . IChangedArgs > ( this ) ; } export namespace Dummystore { export interface IOptions { /**
         * The table schemas of the datastore.
         */ schemas : ReadonlyArray < Schema > ; /**
         * Initialize the state to a previously serialized one.
         */ restoreState ? : string ; } } namespace Private { /**
     * Validates all schemas, and throws an error if any are invalid.
     */ export function validateSchemas ( schemas ) { let errors = [ ] ; for ( let s of schemas ) { let err = validateSchema ( s ) ; if ( err . length ) { errors . push ( `Schema '${ s . id }' validation failed: \n${ err . join ( '\n' ) }` ) ; } } if ( errors . length ) { throw new Error ( errors . join ( '\n\n' ) ) ; } } export type MutableContext = { /**
         * Whether the datastore currently in a transaction.
         */ inTransaction : boolean ; /**
         * The id of the current transaction.
         */ transactionId : string ; /**
         * The current version of the datastore.
         */ version : number ; /**
         * The unique id of the datastore.
         */ storeId : number ; /**
         * The current change object of the transaction.
         */ change : Datastore . MutableChange ; } ; /**
     * Checks if a change is empty.
     */ export function isChangeEmpty ( change ) { return Object . keys ( change ) . length === 0 ; } }