Replace Comlink in Playground CLI with lifecycle-aware RPC - #4251
Replace Comlink in Playground CLI with lifecycle-aware RPC#4251brandonpayton wants to merge 14 commits into
Conversation
brandonpayton
left a comment
There was a problem hiding this comment.
Note for Codex:
This PR is ridiculously large. We need to do this more gradually. For this PR, let's leave our comlink fork in place and used by most of the repo.
As a first attempt, let's just switch RPC implementations for Playground CLI. If that still seems like too much, we will further limit the scope of this PR.
Let's also try to make this PR as simple as possible without compromising the integrity of the system or its holistic purpose.
|
I narrowed this to the Playground CLI rollout requested in the review:
The review-focused changes are split into scope restoration, CLI adoption, Final CI run 31143630752 The run's sole red job was — Codex |
1810fb5 to
3faef79
Compare
|
Rebased this PR onto current I also followed up on the remaining CI failure:
The evidence identifies the original failure as transient runner/CDN behavior, — Codex |
Motivation for the change, related issues
This is the first, deliberately narrow adoption of a lifecycle-aware RPC
implementation. It moves only Playground CLI's worker boundaries while keeping
the existing
@php-wasm/universalpackage-root RPC implementation and all otherrepository consumers unchanged.
The CLI exercises asynchronous worker calls and the synchronous
FileLockManagerchannel without requiring a coordinated browser/client/remote rollout.
What changed
@php-wasm/universal/playground-rpcentry with ESM, CommonJS,declarations, and source maps.
run-cliworkerchannel to that entry.
FileLockManagerchannel its ownMessagePort, matching thesynchronous transport's dedicated-port requirement.
workers, Studio, Telex, and other consumers on the existing implementation.
packages/php-wasm/universal/.The new entry uses the marker
wordpress-playground-rpcand protocol version 1.Here, “version” means the on-the-wire envelope format—not the npm package version.
Messages include a session identity, request correlation, kind, and protocol
version. Peers that recognize the marker but use different versions fail
explicitly. Legacy peers use a different marker and do not participate in that
exchange; the separate package entry prevents that pairing in the CLI.
The CLI packages both ends of each adopted channel together, so it upgrades them
as one unit. The new and legacy entries are not wire-compatible and must not share
a channel.
Behavior and compatibility
The adopted CLI paths support asynchronous calls and nested property reads,
receiver preservation, callbacks and callback failures, structured-cloned values,
explicit transfer lists and policies, streams, PHP response values, explicit
release, endpoint termination, and bounded synchronous calls.
Each consumed endpoint owns one session. Release, abort, port closure, or an
observed worker exit rejects pending and future work, detaches listeners, closes
owned ports, and errors returned streams. Repeated cleanup is safe. CLI disposal
awaits remote disposal, explicitly releases proxies, and still terminates every
worker and cleans up server/temp resources if another cleanup step fails.
A final review also closed two nested-lifetime gaps: a synchronous remote proxy can
be re-exposed to a nested worker, and a MessagePort-backed stream can be cancelled
before its producer emits its first chunk.
No CLI call site demonstrated a need for remote assignment, remote construction,
generic proxy marking, automatic finalization, synthetic
.bind()behavior, orper-operation cancellation, so those general-purpose proxy features are not added.
No CLI public command, option, or return shape is intended to change. Telex,
Studio, browser Playground, direct package-root consumers, and separately deployed
iframe/worker peers are unaffected by this staged adoption. wp-env and other CLI
consumers use the new implementation internally when they use this CLI build.
CI failures addressed
The original revision's built-package CommonJS job timed out for every PHP version.
I reproduced that failure from the packed artifacts on Node 22 and traced it to a
valid
SharedArrayBuffercrossing Jest's VM realm and failing aninstanceofcheck. Transport validation now uses intrinsic brand checks for cross-realm
SharedArrayBuffer,ArrayBuffer,Uint8Array, andReadableStreamvalues, witha
node:vmregression test. The packed suite subsequently passed all PHP versions.The first narrowed post-push run exposed a separate timing assumption in the sync
endpoint-loss test. It made a blocking call before Node delivered the port-close
event and therefore observed the documented bounded timeout. The test now
subscribes before worker termination and waits for that observable close event
before asserting immediate endpoint rejection.
The original revision's browser RPC failures exercised browser integration that
has been removed from this narrowed PR. An unrelated cookie-expiration assertion
was not changed.
Local validation
every packaged PHP version.
require, Node dynamicimport, and Chromium.playground-rpcimports passed in both CommonJS and ESM.passed.
LICENSE, byte-for-byte.Fresh CI
The PR was rebased through GitHub's update-branch API onto
trunkat98392c708; no force-push command was used. The rebased head is3faef7989.GitHub Actions run 31215115734
completed successfully with no failed jobs. All 26 checks scoped to this rollout
passed:
The rebased run also passed all three regular Chromium shards and the dedicated
Chromium storage lane added on
trunk.The preceding run's Chromium shard 3 had failed before tests because the
Playwright browser download received HTTP 403
AccessDeniedfrom the CDN. Afresh-runner retry passed both browser installation and the full shard without a
repository change, confirming transient runner/CDN behavior rather than an RPC or
E2E test failure.
Records and artifact boundary
See
RPC-COMPATIBILITY.md,RPC-PROTOCOL.md, andRPC-PROVENANCE.mdinpackages/php-wasm/universal/for the detailed boundary and recorded processhistory. The provenance record preserves engineering evidence and does not claim
legal clearance.
Because this PR intentionally retains the legacy implementation, it does not make
a repository-wide or package-wide claim that legacy implementation signatures
have been removed. That cleanup belongs to a later migration after the remaining
consumers move.