import "@here/harp-fetch" ; import { getUrlOrigin , LoggerManager } from "@here/harp-utils" ; import { isWorkerBootstrapRequest , WorkerBootstrapResponse } from "./WorkerBootstrapDefs" ; const logger = LoggerManager . instance . create ( "WorkerLoader" ) ; /**
 * Set of `Worker` loading and initialization helpers:
 *  - starting Worker from URL with fallback to XHR+blob  WorkerLoader . startWorker *  - waiting for proper worker initialization, see  WorkerLoader . waitWorkerInitialized export class WorkerLoader { static directlyFallbackToBlobBasedLoading : boolean = false ; static sourceLoaderCache = new Map < string , Promise < string > > ( ) ; static dependencyUrlMapping : { [ name ] : string ; } = { } ; param scriptUrl param timeout static startWorker ( scriptUrl , timeout = 10000 ) { if ( scriptUrl . startsWith ( "blob:" ) ) { return this . startWorkerImmediately ( scriptUrl , timeout ) ; } if ( this . directlyFallbackToBlobBasedLoading ) { return this . startWorkerBlob ( scriptUrl , timeout ) ; } return this . startWorkerImmediately ( scriptUrl , timeout ) . catch ( error => { if ( typeof window !== "undefined" ) { const pageUrl = window . location . href ; const fullScriptUrl = new URL ( scriptUrl , pageUrl ) . href ; if ( getUrlOrigin ( fullScriptUrl ) === getUrlOrigin ( pageUrl ) ) { throw error ; } logger . log ( "#startWorker: cross-origin worker construction failed, trying load with blob" ) ; this . directlyFallbackToBlobBasedLoading = true ; return WorkerLoader . startWorkerBlob ( scriptUrl , timeout ) ; } else { throw error ; } } ) ; } param scriptUrl static startWorkerImmediately ( scriptUrl , timeout ) { try { const worker = new Worker ( scriptUrl ) ; return this . waitWorkerInitialized ( worker , timeout ) ; } catch ( error ) { return Promise . reject ( error ) ; } } param scriptUrl static startWorkerBlob ( scriptUrl , timeout ) { return this . fetchScriptSourceToBlobUrl ( scriptUrl ) . then ( blobUrl => { return this . startWorkerImmediately ( blobUrl , timeout ) ; } ) ; } param scriptUrl return static fetchScriptSourceToBlobUrl ( scriptUrl ) { let loadingPromise = this . sourceLoaderCache . get ( scriptUrl ) ; if ( loadingPromise !== undefined ) { return loadingPromise ; } loadingPromise = fetch ( scriptUrl ) . then ( response => response . text ( ) ) . catch ( error => { throw new Error ( `WorkerLoader#fetchScriptSourceToBlob: failed to load worker script: ${ error }` ) ; } ) . then ( scriptSource => { this . sourceLoaderCache . delete ( scriptUrl ) ; const blob = new Blob ( [ scriptSource ] , { type : "application/javascript" } ) ; return URL . createObjectURL ( blob ) ; } ) ; this . sourceLoaderCache . set ( scriptUrl , loadingPromise ) ; return loadingPromise ; } param worker param timeout returns static waitWorkerInitialized ( worker , timeout ) { return new Promise < Worker > ( ( resolve , reject ) => { const firstMessageCallback = ( event ) => { const message = event . data ; if ( isWorkerBootstrapRequest ( message ) ) { const dependencies = message . dependencies ; const resolvedDependencies = [ ] ; for ( const dependency of dependencies ) { const resolved = this . dependencyUrlMapping [ dependency ] ; if ( ! resolved ) { cleanup ( ) ; reject ( new Error ( `#waitWorkerInitialized: Unable to resolve '${ dependency }'` + ` as needed by worker script.` ) ) ; return ; } resolvedDependencies . push ( resolved ) ; } const response = { type : "worker-bootstrap-response" , resolvedDependencies } ; worker . postMessage ( response ) ; return ; } cleanup ( ) ; resolve ( worker ) ; setTimeout ( ( ) => { worker . dispatchEvent ( event ) ; } , 0 ) ; } ; const errorCallback = ( error ) => { cleanup ( ) ; let message = "Error during worker initialization" ; if ( error . message ) { message = message + `: ${ error . message }` ; } if ( typeof error . filename === "string" && typeof error . lineno === "number" ) { message = message + ` in ${ error . filename }:${ error . lineno }` ; } reject ( new Error ( message ) ) ; } ; const cleanup = ( ) => { clearTimeout ( timerId ) ; worker . removeEventListener ( "message" , firstMessageCallback ) ; worker . removeEventListener ( "error" , errorCallback ) ; } ; worker . addEventListener ( "error" , errorCallback ) ; worker . addEventListener ( "message" , firstMessageCallback ) ; const timerId = setTimeout ( ( ) => { cleanup ( ) ; reject ( new Error ( "Timeout exceeded when waiting for first message from worker." ) ) ; } , timeout ) ; } ) ; } }