From a19c80bf6e0124eef2332e0a206ae86f95b4ed2f Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Thu, 23 Jul 2026 03:34:42 +0800 Subject: [PATCH] fix(code-runtime): preserve typed failures after mutation --- .../code-runtime-worker/src/bootstrap.ts | 15 +++++++++++++-- .../code-runtime-worker/tests/runtime.spec.ts | 9 +++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/packages/code-runtime/code-runtime-worker/src/bootstrap.ts b/packages/code-runtime/code-runtime-worker/src/bootstrap.ts index 3b0db6b59c..b654b04fec 100644 --- a/packages/code-runtime/code-runtime-worker/src/bootstrap.ts +++ b/packages/code-runtime/code-runtime-worker/src/bootstrap.ts @@ -10,6 +10,17 @@ import type { DoneMessage, ReplyMessage, WorkerBootData, WorkerToHost } from './ import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from './output-json.ts' import { decodeWorkerJson, encodeWorkerJson, snapshotCodeJsonValue } from './worker-json.ts' +const capturedObjectCreate = Object.create +const capturedObjectDefineProperty = Object.defineProperty + +/** Define one public binding-error field without consulting mutable globals or descriptor prototypes. */ +function defineBindingErrorField(error: Error, key: string, value: string): void { + const attributes = capturedObjectCreate(null) as PropertyDescriptor + attributes.enumerable = true + attributes.value = value + capturedObjectDefineProperty(error, key, attributes) +} + /** The port surface the bootstrap needs — satisfied by `parentPort` and by the tests' fake. */ export interface BootstrapPort { postMessage(message: WorkerToHost): void @@ -236,8 +247,8 @@ function makeBindingErrorClass( return class BindingCallError extends Error { constructor(memberName: string, message: string) { super(message) - Object.defineProperty(this, 'name', { enumerable: true, value: descriptor.name }) - Object.defineProperty(this, descriptor.memberNameProperty, { enumerable: true, value: memberName }) + defineBindingErrorField(this, 'name', descriptor.name) + defineBindingErrorField(this, descriptor.memberNameProperty, memberName) } } } diff --git a/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts b/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts index ff75dc65b7..f938a6802a 100644 --- a/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts +++ b/packages/code-runtime/code-runtime-worker/tests/runtime.spec.ts @@ -681,14 +681,19 @@ describe('WorkerCodeRuntime — hostile programs (real workers)', () => { objectPrototype.constructor = arrayPrototype.constructor = null; globalThis.Array = globalThis.Buffer = globalThis.Function = globalThis.Number = globalThis.Object = globalThis.Reflect = globalThis.Set = globalThis.String = undefined; const echoed = await tools.echo({ request: ['€', 1] }); - return { echoed, completion: { ok: true, amount: 42 } }; + let failure; + try { await tools.fail({}) } catch (error) { + failure = { typed: error instanceof ToolCallError, name: error.name, toolName: error.toolName, message: error.message }; + } + return { echoed, failure, completion: { ok: true, amount: 42 } }; `, - bindings: tools({ echo: async args => args }), + bindings: tools({ echo: async args => args, fail: async () => { throw new Error('nope') } }), }) expect(result).toEqual({ logs: [], value: { echoed: { request: ['€', 1] }, + failure: { typed: true, name: 'ToolCallError', toolName: 'fail', message: 'nope' }, completion: { ok: true, amount: 42 }, }, })