diff --git a/packages/php-wasm/progress/src/lib/emscripten-download-monitor.ts b/packages/php-wasm/progress/src/lib/emscripten-download-monitor.ts index a398cdf477..275fd2d9d3 100644 --- a/packages/php-wasm/progress/src/lib/emscripten-download-monitor.ts +++ b/packages/php-wasm/progress/src/lib/emscripten-download-monitor.ts @@ -50,7 +50,16 @@ export class EmscriptenDownloadMonitor extends EventTarget { async monitorFetch(fetchPromise: Promise): Promise { const response = await fetchPromise; const onProgress = (event: CustomEvent) => { - this.#notify(response.url, event.detail.loaded, event.detail.total); + this.#setFileProgress( + response.url, + event.detail.loaded, + event.detail.total + ); + this.dispatchEvent( + new CustomEvent('progress', { + detail: this.progress, + }) + ); }; return cloneResponseMonitorProgress(response, onProgress); } @@ -62,7 +71,7 @@ export class EmscriptenDownloadMonitor extends EventTarget { * @param loaded The number of bytes of that file loaded so far. * @param fileSize The total number of bytes in the loaded file. */ - #notify(file: string, loaded: number, fileSize: number) { + #setFileProgress(file: string, loaded: number, fileSize: number) { const fileName = new URL(file, 'http://example.com').pathname .split('/') .pop()!; @@ -81,14 +90,13 @@ export class EmscriptenDownloadMonitor extends EventTarget { } this.#progress[fileName] = loaded; - this.dispatchEvent( - new CustomEvent('progress', { - detail: { - loaded: sumValues(this.#progress), - total: sumValues(this.#assetsSizes), - }, - }) - ); + } + + get progress() { + return { + loaded: sumValues(this.#progress), + total: sumValues(this.#assetsSizes), + }; } } diff --git a/packages/php-wasm/progress/src/lib/progress-tracker.ts b/packages/php-wasm/progress/src/lib/progress-tracker.ts index 52dece074d..9113741275 100644 --- a/packages/php-wasm/progress/src/lib/progress-tracker.ts +++ b/packages/php-wasm/progress/src/lib/progress-tracker.ts @@ -10,15 +10,17 @@ export interface ProgressTrackerOptions { fillTime?: number; } -/** - * Custom event providing information about a loading process. - */ -export type LoadingEvent = CustomEvent<{ +export type LoadingProgress = { /** The number representing how much was loaded. */ loaded: number; /** The number representing how much needs to loaded in total. */ total: number; -}>; +}; + +/** + * Custom event providing information about a loading process. + */ +export type LoadingEvent = CustomEvent; /** * Custom event providing progress details. @@ -286,13 +288,8 @@ export class ProgressTracker extends EventTarget { return this._progressObserver; } - get loadingListener() { - if (!this._loadingListener) { - this._loadingListener = (event: LoadingEvent) => { - this.set((event.detail.loaded / event.detail.total) * 100); - }; - } - return this._loadingListener; + setFromProgressDetails(details: LoadingProgress) { + this.set((details.loaded / details.total) * 100); } pipe(receiver: ProgressReceiver) { diff --git a/packages/php-wasm/universal/src/lib/php-worker.ts b/packages/php-wasm/universal/src/lib/php-worker.ts index 5d8c4e83d8..212232d158 100644 --- a/packages/php-wasm/universal/src/lib/php-worker.ts +++ b/packages/php-wasm/universal/src/lib/php-worker.ts @@ -1,4 +1,4 @@ -import { EmscriptenDownloadMonitor } from '@php-wasm/progress'; +import { EmscriptenDownloadMonitor, LoadingProgress } from '@php-wasm/progress'; import { PHP } from './php'; import { PHPRequestHandler } from './php-request-handler'; import { PHPResponse } from './php-response'; @@ -129,6 +129,10 @@ export class PHPWorker implements LimitedPHPApi { .requestHandler!.internalUrlToPath(internalUrl); } + async getDownloadProgress(): Promise { + return _private.get(this)!.monitor?.progress; + } + /** * The onDownloadProgress event listener. */ diff --git a/packages/playground/blueprints/src/lib/resources.ts b/packages/playground/blueprints/src/lib/resources.ts index 8501740cc2..a742402642 100644 --- a/packages/playground/blueprints/src/lib/resources.ts +++ b/packages/playground/blueprints/src/lib/resources.ts @@ -219,7 +219,9 @@ export abstract class FetchResource extends Resource { } response = await cloneResponseMonitorProgress( response, - this.progress?.loadingListener ?? noop + this.progress + ? (e) => this.progress?.setFromProgressDetails(e.detail) + : noop ); if (response.status !== 200) { throw new Error(`Could not download "${url}"`); diff --git a/packages/playground/client/src/index.ts b/packages/playground/client/src/index.ts index 40ab5c5fb1..aa948264dc 100644 --- a/packages/playground/client/src/index.ts +++ b/packages/playground/client/src/index.ts @@ -182,8 +182,12 @@ async function doStartPlaygroundWeb( ) as PlaygroundClient; await playground.isConnected(); progressTracker.pipe(playground); + const currentProgress = (await playground.getDownloadProgress())!; const downloadPHPandWP = progressTracker.stage(); - await playground.onDownloadProgress(downloadPHPandWP.loadingListener); + downloadPHPandWP.setFromProgressDetails(currentProgress); + await playground.onDownloadProgress((e) => { + downloadPHPandWP.setFromProgressDetails(e.detail); + }); await playground.isReady(); downloadPHPandWP.finish(); return playground; diff --git a/packages/playground/remote/src/lib/worker-thread.ts b/packages/playground/remote/src/lib/worker-thread.ts index 9c575b6c7f..cc1a4b81a6 100644 --- a/packages/playground/remote/src/lib/worker-thread.ts +++ b/packages/playground/remote/src/lib/worker-thread.ts @@ -366,21 +366,22 @@ try { LOGGED_IN_SALT: randomString(40), NONCE_SALT: randomString(40), }; - const wordPressZip = wordPressAvailableInOPFS - ? undefined - : new File([await (await wordPressRequest!).blob()], 'wp.zip'); - - const sqliteIntegrationPluginZip = new File( - [await (await sqliteIntegrationRequest).blob()], - 'sqlite.zip' - ); const knownRemoteAssetPaths = new Set(); const requestHandler = await bootWordPress({ siteUrl: setURLScope(wordPressSiteUrl, scope).toString(), createPhpRuntime, - wordPressZip, - sqliteIntegrationPluginZip, + // Do not await the WordPress download or the sqlite integration download. + // Let bootWordPress start the PHP runtime download first, and then await + // all the ZIP files right before they're used. + wordPressZip: wordPressAvailableInOPFS + ? undefined + : wordPressRequest! + .then((r) => r.blob()) + .then((b) => new File([b], 'wp.zip')), + sqliteIntegrationPluginZip: sqliteIntegrationRequest + .then((r) => r.blob()) + .then((b) => new File([b], 'sqlite.zip')), spawnHandler: spawnHandlerFactory, sapiName: startupOptions.sapiName, constants,