From 468bf246c2b7f07c866b8dc195ad5149cc7de62d Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sat, 8 Aug 2026 11:53:26 -0400 Subject: [PATCH] fix(playground): materialize command responses (#2236) Snapshot response text while crossing the Playground runtime boundary so proxy-backed values cannot mutate before plugin-state consumers parse them. AI assistance: OpenAI gpt-5.6-sol via OpenCode diagnosed the lazy response boundary, implemented the materialization fix and regression, and ran the focused test, build, typecheck, and diff checks. Chris Huber reviewed and owns the change. --- .../src/playground-runtime.ts | 7 +++++- tests/plugin-state-command.test.ts | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/runtime-playground/src/playground-runtime.ts b/packages/runtime-playground/src/playground-runtime.ts index 245c092d8..62b2b92a9 100644 --- a/packages/runtime-playground/src/playground-runtime.ts +++ b/packages/runtime-playground/src/playground-runtime.ts @@ -233,6 +233,10 @@ export interface PlaygroundRuntimeBackendOptions { cliModule?: PlaygroundCliModule } +export function materializePlaygroundRunResponse(response: PlaygroundRunResponse): PlaygroundRunResponse { + return { ...response, text: response.text } +} + class PlaygroundRuntime implements Runtime { private status: RuntimeInfo["status"] = "created" private readonly runtimeId = id("runtime") @@ -1788,7 +1792,8 @@ class PlaygroundRuntime implements Runtime { const response = await this.executeRequestWorker(server, options.code, requestWorkerEnvironment, this.executionSignals.getStore()) return { text: response.text, exitCode: response.ok ? 0 : 1, ...(!response.ok ? { errors: response.text } : {}) } } - return await abortable(server.playground.run(options), this.executionSignals.getStore()) + const response = await abortable(server.playground.run(options), this.executionSignals.getStore()) + return materializePlaygroundRunResponse(response) } catch (error) { const payload = "code" in options ? options.code : options.scriptPath throw new PlaygroundCommandCrashError(command, error, { diff --git a/tests/plugin-state-command.test.ts b/tests/plugin-state-command.test.ts index 774015720..a7d09a357 100644 --- a/tests/plugin-state-command.test.ts +++ b/tests/plugin-state-command.test.ts @@ -1,6 +1,8 @@ import assert from "node:assert/strict" import { commandRegistry } from "../packages/runtime-core/src/command-registry.js" import { pluginStateInputFromArgs, pluginStatePhpCode } from "../packages/runtime-playground/src/plugin-state-command-handlers.js" +import { materializePlaygroundRunResponse } from "../packages/runtime-playground/src/playground-runtime.js" +import { runPluginStateCommand } from "../packages/runtime-playground/src/wordpress-command-runners.js" const command = commandRegistry.find((definition) => definition.id === "wordpress.plugin-state") const ensureCommand = commandRegistry.find((definition) => definition.id === "wordpress.ensure-plugin-active") @@ -37,4 +39,25 @@ assert.match(php, /networkActivePluginsAfter/) assert.match(php, /artifactRefs/) assert.doesNotMatch(JSON.stringify(command), /homeboy|woocommerce|hbx/i) +const pluginStateJson = JSON.stringify({ schema: "wp-codebox/wordpress-plugin-state/v1", active_plugins: ["example/example.php"] }) +let responseAvailable = true +const lazyResponse = new Proxy({ exitCode: 0 }, { + get(target, property, receiver) { + return property === "text" ? responseAvailable ? pluginStateJson : "\0" : Reflect.get(target, property, receiver) + }, +}) +const materializedResponse = materializePlaygroundRunResponse(lazyResponse as never) +responseAvailable = false +assert.equal(typeof materializedResponse.text, "string") +assert.deepEqual(JSON.parse(materializedResponse.text), JSON.parse(pluginStateJson)) + +const pluginStateOutput = await runPluginStateCommand({ + runPlaygroundCommand: async () => materializedResponse, + runtimeSpec: { environment: { kind: "wordpress" } } as never, + server: {} as never, + spec: { command: "wordpress.plugin-state", args: ["plugin=example"] } as never, +}) +assert.equal(typeof pluginStateOutput, "string") +assert.deepEqual(JSON.parse(pluginStateOutput), JSON.parse(pluginStateJson)) + console.log("plugin-state command ok")