fix(code-runtime): preserve typed failures after mutation
This commit is contained in:
@@ -10,6 +10,17 @@ import type { DoneMessage, ReplyMessage, WorkerBootData, WorkerToHost } from './
|
|||||||
import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from './output-json.ts'
|
import { jsonStringBytesUpTo, jsonValueBytesUpTo, truncateJsonStringBytes } from './output-json.ts'
|
||||||
import { decodeWorkerJson, encodeWorkerJson, snapshotCodeJsonValue } from './worker-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. */
|
/** The port surface the bootstrap needs — satisfied by `parentPort` and by the tests' fake. */
|
||||||
export interface BootstrapPort {
|
export interface BootstrapPort {
|
||||||
postMessage(message: WorkerToHost): void
|
postMessage(message: WorkerToHost): void
|
||||||
@@ -236,8 +247,8 @@ function makeBindingErrorClass(
|
|||||||
return class BindingCallError extends Error {
|
return class BindingCallError extends Error {
|
||||||
constructor(memberName: string, message: string) {
|
constructor(memberName: string, message: string) {
|
||||||
super(message)
|
super(message)
|
||||||
Object.defineProperty(this, 'name', { enumerable: true, value: descriptor.name })
|
defineBindingErrorField(this, 'name', descriptor.name)
|
||||||
Object.defineProperty(this, descriptor.memberNameProperty, { enumerable: true, value: memberName })
|
defineBindingErrorField(this, descriptor.memberNameProperty, memberName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -681,14 +681,19 @@ describe('WorkerCodeRuntime — hostile programs (real workers)', () => {
|
|||||||
objectPrototype.constructor = arrayPrototype.constructor = null;
|
objectPrototype.constructor = arrayPrototype.constructor = null;
|
||||||
globalThis.Array = globalThis.Buffer = globalThis.Function = globalThis.Number = globalThis.Object = globalThis.Reflect = globalThis.Set = globalThis.String = undefined;
|
globalThis.Array = globalThis.Buffer = globalThis.Function = globalThis.Number = globalThis.Object = globalThis.Reflect = globalThis.Set = globalThis.String = undefined;
|
||||||
const echoed = await tools.echo({ request: ['€', 1] });
|
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({
|
expect(result).toEqual({
|
||||||
logs: [],
|
logs: [],
|
||||||
value: {
|
value: {
|
||||||
echoed: { request: ['€', 1] },
|
echoed: { request: ['€', 1] },
|
||||||
|
failure: { typed: true, name: 'ToolCallError', toolName: 'fail', message: 'nope' },
|
||||||
completion: { ok: true, amount: 42 },
|
completion: { ok: true, amount: 42 },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user