import fs from 'fs' ; import path from 'path' ; import crypto from 'crypto' ; import mkdirp from 'mkdirp' ; import rimraf from 'rimraf' ; import { IStorageAdapter , IStorageAdapterConfig } from './IStorageAdapter' ; import { StorageEntryAlreadyExistsError } from './errors' ; const NO_SUCH_FILE = 'ENOENT' ; const FILE_EXISTS = 'EEXIST' ; /**
 * Implementation of  IStorageAdapter that uses file system for
 * persistence. For use in Node.js.
  export default class FileSystemStorageAdapter implements IStorageAdapter { private config : IStorageAdapterConfig ; param { IStorageAdapterConfig } config constructor ( config ) { this . config = config ; mkdirp . sync ( path . resolve ( this . config . dir ) ) ; } inheritDoc store ( key , data ) { return new Promise < void > ( ( resolve , reject ) => { const file = this . resolveFilePath ( key ) ; fs . writeFile ( file , data , { flag : 'wx' } , err => { if ( err && err . code === FILE_EXISTS ) { return reject ( new StorageEntryAlreadyExistsError ( ) ) ; } resolve ( ) ; } ) ; } ) ; } inheritDoc load ( key ) { return Promise . resolve ( ) . then ( ( ) => { const filename = this . resolveFilePath ( key ) ; return readFileAsync ( filename ) ; } ) ; } inheritDoc exists ( key ) { return new Promise < boolean > ( ( resolve , reject ) => { const file = this . resolveFilePath ( key ) ; fs . access ( file , err => { if ( err ) { if ( err . code === NO_SUCH_FILE ) { return resolve ( false ) ; } return reject ( err ) ; } resolve ( true ) ; } ) ; } ) ; } inheritDoc remove ( key ) { return new Promise < boolean > ( ( resolve , reject ) => { const file = this . resolveFilePath ( key ) ; fs . unlink ( file , err => { if ( err ) { if ( err . code === NO_SUCH_FILE ) { return resolve ( false ) ; } return reject ( err ) ; } resolve ( true ) ; } ) ; } ) ; } inheritDoc update ( key , data ) { return new Promise ( ( resolve , reject ) => { const file = this . resolveFilePath ( key ) ; fs . writeFile ( file , data , { flag : 'w' } , err => { if ( err ) { return reject ( err ) ; } resolve ( ) ; } ) ; } ) ; } inheritDoc clear ( ) { return new Promise < void > ( ( resolve , reject ) => { rimraf ( this . config . dir ! , err => { if ( err ) { return reject ( err ) ; } resolve ( ) ; } ) ; } ) ; } inheritDoc list ( ) { return new Promise ( ( resolve , reject ) => { fs . readdir ( this . config . dir , ( err , files ) => { if ( err ) { return reject ( err ) ; } Promise . all ( files . map ( filename => readFileAsync ( path . resolve ( this . config . dir ! , filename ) ) ) ) . then ( contents => { const entries = contents . filter ( content => content !== null ) as string [ ] ; resolve ( entries ) ; } ) . catch ( reject ) ; } ) ; } ) ; } private resolveFilePath ( key ) { return path . resolve ( this . config . dir ! , this . hash ( key ) ) ; } private hash ( data ) { return crypto . createHash ( 'sha256' ) . update ( data ) . digest ( 'hex' ) ; } } function readFileAsync ( filename ) { return new Promise ( ( resolve , reject ) => { fs . readFile ( filename , ( err , data ) => { if ( err ) { if ( err . code === NO_SUCH_FILE ) { return resolve ( null as any ) ; } return reject ( err ) ; } resolve ( data . toString ( 'utf8' ) ) ; } ) ; } ) ; }