import Ajv from 'ajv' ; import addFormats from 'ajv-formats' ; import { Memoize } from 'typescript-memoize' ; import { CommitData , CommitType , StreamUtils } from '@ceramicnetwork/common' ; import type { TileDocument } from '@ceramicnetwork/stream-tile' ; import { Dispatcher } from './dispatcher' ; import type { StreamID } from '@ceramicnetwork/streamid' ; import type CID from 'cids' ; /**
 * Various utility functions
 */ export default class Utils { @ Memoize ( ) static get validator ( ) { const ajv = new Ajv ( { allErrors : true , strictTypes : false , strictTuples : false } ) ; addFormats ( ajv ) ; return ajv ; } /**
     * Awaits on condition for certain amount of time
     */ static async awaitCondition ( conditionFn , stopFunction , awaitInterval ) { while ( conditionFn ( ) ) { if ( stopFunction ( ) ) { return ; } await new Promise ( ( resolve ) => setTimeout ( resolve , awaitInterval ) ) ; } } param schema static isSchemaValid ( schema ) { Utils . validator . compile ( schema ) ; return Utils . validator . validateSchema ( schema ) as boolean ; } param content param schema static validate ( content , schema ) { const isValid = Utils . validator . validate ( schema , content ) ; if ( ! isValid ) { const errorMessages = Utils . validator . errorsText ( ) ; throw new Error ( `Validation Error: ${ errorMessages }` ) ; } } /**
     * Validate TileDocument against schema
     */ static async validateSchema ( doc ) { const schemaStreamId = doc . state ?. metadata ?. schema ; if ( schemaStreamId ) { const schemaDoc = await doc . api . loadStream < TileDocument > ( schemaStreamId ) ; if ( ! schemaDoc ) { throw new Error ( `Schema not found for ${ schemaStreamId }` ) ; } Utils . validate ( doc . content , schemaDoc . content ) ; } } /**
     * Return `CommitData` (with commit, JWS envelope, and/or anchor proof/timestamp, as applicable) for the specified CID
     */ static async getCommitData ( dispatcher , cid , timestamp ? , streamId ? ) { const commit = await dispatcher . retrieveCommit ( cid , streamId ) ; if ( ! commit ) throw new Error ( `No commit found for CID ${ cid . toString ( ) }` ) ; const commitData = { cid , type : CommitType . SIGNED , commit , timestamp } ; if ( StreamUtils . isSignedCommit ( commit ) ) { const linkedCommit = await dispatcher . retrieveCommit ( commit . link , streamId ) ; if ( ! linkedCommit ) throw new Error ( `No commit found for CID ${ commit . link . toString ( ) }` ) ; commitData . commit = linkedCommit ; commitData . envelope = commit ; } else if ( StreamUtils . isAnchorCommit ( commit ) ) { commitData . type = CommitType . ANCHOR ; commitData . proof = await dispatcher . retrieveFromIPFS ( commit . proof ) ; commitData . timestamp = commitData . proof . blockTimestamp ; } if ( ! commitData . commit . prev ) commitData . type = CommitType . GENESIS ; return commitData ; } } export class TrieNode { public key : string ; public children : Record < string , TrieNode > ; constructor ( key = '' ) { this . key = key ; this . children = { } ; } } export class PathTrie { public root : TrieNode ; constructor ( ) { this . root = new TrieNode ( ) ; } add ( path ) { const nextNodeAdd = ( node , key ) : TrieNode => { if ( ! node . children [ key ] ) node . children [ key ] = new TrieNode ( key ) ; return node . children [ key ] ; } ; if ( path . startsWith ( '/' ) ) path = path . substring ( 1 ) ; path . split ( '/' ) . reduce ( nextNodeAdd , this . root ) ; } } export const promiseTimeout = ( promise , ms , timeoutErrorMsg ) : Promise < any > => { const timeout = new Promise ( ( resolve , reject ) => { setTimeout ( ( ) => reject ( new Error ( timeoutErrorMsg ) ) , ms ) ; } ) ; return Promise . race ( [ timeout , promise ] ) ; } ;