import { describe, expect, test } from "bun:test" import { Effect, Schema } from "effect" import { CodeMode, Tool } from "../src/index.js" // `new` is supported syntax; only the callee decides whether construction succeeds. A callee without // construction support is a TypeError naming it, like JS, rather than an unsupported-syntax diagnostic // that would suggest `new` itself is unavailable. const tools = { echo: Tool.make({ description: "Echo", input: Schema.Struct({}), output: Schema.Struct({}), execute: () => Effect.succeed({}), }), } const run = (code: string) => Effect.runPromise(CodeMode.execute({ code, tools })) const value = async (code: string) => { const result = await run(code) if (!result.ok) throw new Error(`expected success, got ${result.error.kind}: ${result.error.message}`) return result.value } const error = async (code: string) => { const result = await run(code) if (result.ok) throw new Error(`expected failure, got value ${JSON.stringify(result.value)}`) return result.error } describe("new on a non-constructible callee", () => { test("built-in functions without construction point at the plain call", async () => { // Number is a real constructor in JS, so the message must not claim otherwise. const failure = await error(`return new Number(42)`) expect(failure.kind).toBe("ExecutionFailure") expect(failure.message).toStartWith( "TypeError: new Number(...) is not supported; call Number(...) without new instead.", ) expect(failure.suggestions).toBeUndefined() expect((await error(`return new String("a")`)).message).toStartWith("TypeError: new String(...) is not supported") expect((await error(`return new Math.abs(1)`)).message).toStartWith( "TypeError: new Math.abs(...) is not supported; call Math.abs(...) without new instead.", ) }) test("non-callable values are not constructors", async () => { expect((await error(`return new tools.echo()`)).message).toStartWith("TypeError: tools.echo is not a constructor.") expect((await error(`return new (1)()`)).message).toStartWith("TypeError: The called value is not a constructor.") expect((await error(`const Date = 5; return new Date()`)).message).toStartWith( "TypeError: Date is not a constructor.", ) }) test("user-defined functions explain the documented gap", async () => { const failure = await error(`function Point(x) { return { x } }; return new Point(1)`) expect(failure.message).toStartWith( "TypeError: Point cannot be constructed: user-defined constructors and classes are not supported. Call it as a function that returns a plain object instead.", ) expect((await error(`const make = () => ({}); return new make()`)).message).toStartWith( "TypeError: make cannot be constructed", ) }) test("the failure is a catchable TypeError", async () => { expect( await value(` try { new Number(1) } catch (error) { return [error.name, error instanceof TypeError] } `), ).toEqual(["TypeError", true]) }) test("an undeclared callee still fails as an unknown identifier", async () => { expect((await error(`return new Function("return 1")`)).message).toContain("Function") expect((await error(`return new Function("return 1")`)).message).not.toContain("not a constructor") }) test("classes remain unsupported syntax", async () => { const failure = await error(`class A {}; return new A()`) expect(failure.kind).toBe("UnsupportedSyntax") expect(failure.message).toStartWith( "SyntaxError: Syntax 'ClassDeclaration' is not supported. This is a restricted JavaScript-like language. Supported: ", ) expect(failure.message).toContain("Unsupported: classes, getters/setters, BigInt, and custom Symbols.") }) })