Skip to content

Conversation

adamziel
Copy link
Collaborator

@adamziel adamziel commented Sep 2, 2025

🚧 Work in progress 🚧

Adds support for Blueprints v2 PHP runner in the web version of Playground. Supersedes #2487

CleanShot.2025-09-02.at.20.18.14.mp4

Implementation

Blueprints v2 Runner Integration

  • Added an experimentalBlueprintsV2Runner option to startPlaygroundWeb. When used, v2 runner is used for Blueprint execution. The progress bar is updated using progress details sourced from that runner.
  • Added a runBlueprintV2Web function – a counterpart of the runBlueprintV2 function we have in CLI. There's an ample opportunity for code reuse before merging this PR.

Infrastructure and Messaging Enhancements

  • Fixed onMessage support on Playground API client. It was broken since we've introduced PHPProcessManager – the listeners were only registered on the primary PHP instance.

Remaining work

  • Maybe add a modal "Blueprints v2 integration is still experimental, only proceed if you know what you're doing"?
  • Make sure v2 Blueprints are compatible with URL overrides, redux data stores, and other code across the Playground website
    • Avoid parsing the Blueprint in TypeScript as much as possible. Source the information from the Blueprint runner. For example, createSiteMetadata does a bunch of parsing and even calls compileBlueprint. Let's treat Blueprint as a blackbox that only the runner can peek into.
  • Clearly split running v1 from v2, make sure there's zero overlap between these code paths
  • Figure out the WASM crash (see the video)
  • Update the blueprints.phar to its latest version
  • Tests

Follow-up work

  • Nicer progress messages
  • Make sure the download progress is reflected as we go, not all at once at the end of the download
  • Reduce the size of blueprints.phar from 2MB to ~100KB by removing double bundling and using the browser's URL parser instead of the rowbot library.

Testing Instructions (or ideally a Blueprint)

TBD

@adamziel adamziel force-pushed the support-blueprints-v2-in-the-browser branch from fca1cda to 94388e6 Compare September 3, 2025 23:34
@adamziel
Copy link
Collaborator Author

adamziel commented Sep 8, 2025

This WASM crash is blocking here:

CleanShot 2025-09-03 at 14 11 19@2x

Similarly, @mho22 had to disable close() in his ICU work:

	# Calling close() on a file descriptor acquired through WASI syscalls can trigger a JS call/await
	# during a non-resumable C++ stack frame, leading to "RuntimeError: trying to suspend JS frames".
	# Since ICU maps the file into memory and does not require the descriptor after mapping
	# under our build context, skipping close(fd) avoids that suspension error.
	# NOTE: This means the file descriptor will remain open until process teardown.
	# This is acceptable here because ICU data files are loaded only once at startup.
	/root/replace.sh 's/close\(fd\);//' /root/icu/source/common/umapfile.cpp; \

I wonder if that's related to the file locking work 🤔 cc @brandonpayton. Perhaps the close() function needs to be marked as async when building dependencies?

@brandonpayton
Copy link
Member

brandonpayton commented Sep 8, 2025

I wonder if that's related to the file locking work 🤔 cc @brandonpayton. Perhaps the close() function needs to be marked as async when building dependencies?

@adamziel, is the SuspendError occurring during the E2E tests?

If so, I wouldn't expect the error specifically to file-locking code because that should only be applied for Node.js environments. BUT, we did add close, fd_close, and friends to an Asyncify list as part of that PR.

Maybe there is a clue here?
emscripten-core/emscripten#24302 (comment)

@adamziel
Copy link
Collaborator Author

adamziel commented Sep 9, 2025

@brandonpayton This solved it for me:

		# Both imports and exports are required for inter-module communication with wrapped methods, e.g., wasm_recv.
		export JSPI_ADD_IMPORTS=""; \
		export JSPI_ADD_EXPORTS=""; \
		if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
			export JSPI_ADD_IMPORTS=",fd_close"; \
			export JSPI_ADD_EXPORTS=",fd_close"; \
		fi; \
		export ASYNCIFY_FLAGS=" -s ASYNCIFY=2 -sSUPPORT_LONGJMP=wasm -fwasm-exceptions -sJSPI_IMPORTS=js_open_process,js_fd_read,js_waitpid,js_process_status,js_create_input_device,wasm_setsockopt,wasm_shutdown,wasm_close,wasm_recv,__syscall_fcntl64,js_flock,js_release_file_locks,js_waitpid$JSPI_ADD_IMPORTS -sJSPI_EXPORTS=php_wasm_init,wasm_sleep,wasm_read,emscripten_sleep,wasm_sapi_handle_request,wasm_sapi_request_shutdown,wasm_poll_socket,wrap_select,__wrap_select,select,php_pollfd_for,fflush,wasm_popen,wasm_read,wasm_php_exec,run_cli,wasm_recv$JSPI_ADD_EXPORTS -s EXTRA_EXPORTED_RUNTIME_METHODS=ccall,PROXYFS,wasmExports,_malloc,setErrNo "; \

My theory is it works in Node because fd_close returns a promise there, but not in the browser because fd_close is synchronous there. It seems like we should not mark non-asynchronous functions as JSPI_IMPORTS or JSPI_EXPORTS. I wonder if we should make any other functions conditional.

@mho22
Copy link
Collaborator

mho22 commented Sep 9, 2025

@adamziel This should probably also solve my issue in intl/Dockerfile :

/root/replace.sh 's/close\(fd\);//' /root/icu/source/common/umapfile.cpp; \

Right?

@adamziel
Copy link
Collaborator Author

adamziel commented Sep 9, 2025

@mho22 potentially yes 🤞 🤞 🤞

@adamziel
Copy link
Collaborator Author

Closing in! Failures summary:

1 failed
[chromium] › blueprints.spec.ts:89:5 › ?blueprint-url=... should work with ZIP bundles ─────────
option php
should load PHP 7.4 when requested:
AssertionError: Timed out retrying after 120000ms: expected '[ <h1.p>, 2 more... ]' to contain 'PHP Version 7.4'

adamziel added a commit that referenced this pull request Sep 12, 2025
## Motivation for the change, related issues

Solves WASM error that came up in #2586:

```
trying to suspend JS frames SuspendError: trying to suspend JS frames at php.wasm.close (php.wasm-0836494a) at php.wasm. rand_pool_cleanup (php.wasm-0836494a)
```

## Implementation details

Stop marking `fd_close` as JSPI import/export for the web builds. Only
do it for Node.js builds.

My theory is it works in Node because `fd_close` returns a promise
there, but not in the browser where `fd_close` is synchronous. It seems
like we should not mark non-asynchronous functions as JSPI_IMPORTS or
JSPI_EXPORTS.

## Testing Instructions (or ideally a Blueprint)

CI
@adamziel adamziel force-pushed the support-blueprints-v2-in-the-browser branch 2 times, most recently from 2b39020 to 550186f Compare September 12, 2025 10:24
adamziel added a commit that referenced this pull request Sep 12, 2025
## Motivation for the change, related issues

The `onBeforeBlueprint` is unused in this repo and downstream
dependencies that I know of. This PR removes it – it adds complexity to
[Blueprints v2 integration on
playground.wordpress.net](#2586)

## Testing Instructions (or ideally a Blueprint)

CI
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants