refactor(codemode): rename Sandbox terminology to CodeMode (#36768)

This commit is contained in:
Aiden Cline 2026-07-13 16:38:11 -05:00 committed by GitHub
commit ecb5754f4c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 257 additions and 257 deletions

View file

@ -13,13 +13,13 @@ import {
import { rejectCircularInsertion } from "./references.js"
import { isBlockedMember, type SafeObject } from "../tool-runtime.js"
import {
SandboxDate,
SandboxMap,
SandboxPromise,
SandboxRegExp,
SandboxSet,
SandboxURL,
SandboxURLSearchParams,
CodeModeDate,
CodeModeMap,
CodeModePromise,
CodeModeRegExp,
CodeModeSet,
CodeModeURL,
CodeModeURLSearchParams,
} from "../values.js"
import { invokeDateMethod, invokeDateStatic } from "../stdlib/date.js"
import { invokeJsonMethod } from "../stdlib/json.js"
@ -33,7 +33,7 @@ import { boundedData, coerceToNumber, coerceToString, invokeCoercion } from "../
export type CallbackRunner<R> = {
readonly invokeFunction: (fn: CodeModeFunction, args: Array<unknown>) => Effect.Effect<unknown, unknown, R>
readonly settlePromise: (promise: SandboxPromise) => Effect.Effect<unknown, unknown, never>
readonly settlePromise: (promise: CodeModePromise) => Effect.Effect<unknown, unknown, never>
}
export const invokeIntrinsic = <R>(
@ -57,22 +57,22 @@ export const invokeIntrinsic = <R>(
if (Array.isArray(ref.receiver)) {
return invokeArrayMethod(runner, ref.receiver, ref.name, args, node)
}
if (ref.receiver instanceof SandboxDate) {
if (ref.receiver instanceof CodeModeDate) {
return Effect.succeed(invokeDateMethod(ref.receiver, ref.name, node))
}
if (ref.receiver instanceof SandboxRegExp) {
if (ref.receiver instanceof CodeModeRegExp) {
return Effect.succeed(invokeRegExpMethod(ref.receiver, ref.name, args, node))
}
if (ref.receiver instanceof SandboxMap) {
if (ref.receiver instanceof CodeModeMap) {
return invokeMapMethod(runner, ref.receiver, ref.name, args, node)
}
if (ref.receiver instanceof SandboxSet) {
if (ref.receiver instanceof CodeModeSet) {
return invokeSetMethod(runner, ref.receiver, ref.name, args, node)
}
if (ref.receiver instanceof SandboxURL) {
if (ref.receiver instanceof CodeModeURL) {
return Effect.succeed(invokeURLMethod(ref.receiver, ref.name, node))
}
if (ref.receiver instanceof SandboxURLSearchParams) {
if (ref.receiver instanceof CodeModeURLSearchParams) {
return invokeURLSearchParamsMethod(runner, ref.receiver, ref.name, args, node)
}
throw new InterpreterRuntimeError(`Method '${ref.name}' is not available in CodeMode.`, node)
@ -153,7 +153,7 @@ const invokeStringMethod = (value: string, name: string, args: Array<unknown>, n
result = [value]
break
}
if (args[0] instanceof SandboxRegExp) {
if (args[0] instanceof CodeModeRegExp) {
result = value.split(args[0].regex, optNum(1))
break
}
@ -181,7 +181,7 @@ const invokeStringMethod = (value: string, name: string, args: Array<unknown>, n
break
case "replace":
case "replaceAll": {
if (args[0] instanceof SandboxRegExp) {
if (args[0] instanceof CodeModeRegExp) {
const pattern = args[0].regex
const replacement = str(1)
if (name === "replaceAll" && !pattern.global) {
@ -278,13 +278,13 @@ const invokeArrayStatic = (name: string, args: Array<unknown>, node: AstNode): u
[supportedSyntaxMessage],
)
}
if (args[0] instanceof SandboxMap) return Array.from(args[0].map.entries(), ([key, item]) => [key, item])
if (args[0] instanceof SandboxSet) return Array.from(args[0].set.values())
if (args[0] instanceof SandboxURLSearchParams) {
if (args[0] instanceof CodeModeMap) return Array.from(args[0].map.entries(), ([key, item]) => [key, item])
if (args[0] instanceof CodeModeSet) return Array.from(args[0].set.values())
if (args[0] instanceof CodeModeURLSearchParams) {
return Array.from(args[0].params.entries(), ([key, value]) => [key, value])
}
const source = args[0]
if (source instanceof SandboxPromise) {
if (source instanceof CodeModePromise) {
throw new InterpreterRuntimeError(
"Array.from received an un-awaited Promise; await it before creating the array.",
node,
@ -341,7 +341,7 @@ const invokeStringReplacer = <R>(
}
const pattern = args[0]
if (pattern instanceof SandboxRegExp) {
if (pattern instanceof CodeModeRegExp) {
if (name === "replaceAll" && !pattern.regex.global) {
throw new InterpreterRuntimeError(
`String.replaceAll requires a regular expression with the global (g) flag: write /${pattern.regex.source}/${pattern.regex.flags}g, or use String.replace to replace only the first match.`,
@ -364,7 +364,7 @@ const invokeStringReplacer = <R>(
for (const match of matches) {
const replacement = yield* apply(match.args)
const resolved =
args[1] instanceof CodeModeFunction && args[1].async && replacement instanceof SandboxPromise
args[1] instanceof CodeModeFunction && args[1].async && replacement instanceof CodeModePromise
? yield* runner.settlePromise(replacement)
: replacement
output.push(
@ -404,7 +404,7 @@ export const applyCollectionCallback = <R>(
const invokeMapMethod = <R>(
runner: CallbackRunner<R>,
target: SandboxMap,
target: CodeModeMap,
name: string,
args: Array<unknown>,
node: AstNode,
@ -446,7 +446,7 @@ const invokeMapMethod = <R>(
const invokeSetMethod = <R>(
runner: CallbackRunner<R>,
target: SandboxSet,
target: CodeModeSet,
name: string,
args: Array<unknown>,
node: AstNode,
@ -485,7 +485,7 @@ const invokeSetMethod = <R>(
const invokeURLSearchParamsMethod = <R>(
runner: CallbackRunner<R>,
target: SandboxURLSearchParams,
target: CodeModeURLSearchParams,
name: string,
args: Array<unknown>,
node: AstNode,

View file

@ -1,5 +1,5 @@
import type { SafeObject } from "../tool-runtime.js"
import type { SandboxPromise, SandboxURL } from "../values.js"
import type { CodeModePromise, CodeModeURL } from "../values.js"
export type SourcePosition = {
line: number
@ -35,7 +35,7 @@ export type StatementResult =
| { kind: "continue" }
export type MemberReference = {
target: SafeObject | Array<unknown> | SandboxURL
target: SafeObject | Array<unknown> | CodeModeURL
key: string | number
}
@ -71,7 +71,7 @@ export type PromiseInstanceMethodName = "then" | "catch" | "finally"
export class PromiseInstanceMethodReference {
constructor(
readonly promise: SandboxPromise,
readonly promise: CodeModePromise,
readonly name: PromiseInstanceMethodName,
) {}
}

View file

@ -17,24 +17,24 @@ import { applyCollectionCallback, type CallbackRunner } from "./methods.js"
import { typeofValue } from "./references.js"
import { spreadItems } from "../stdlib/collections.js"
import { createAggregateErrorValue } from "../stdlib/value.js"
import { SandboxPromise } from "../values.js"
import { CodeModePromise } from "../values.js"
// Observation only controls rejection reporting; program completion interrupts all promise work.
export class PromiseRuntime<R> {
private readonly active = new Set<SandboxPromise>()
private readonly ids = new WeakMap<SandboxPromise, number>()
private readonly observed = new WeakSet<SandboxPromise>()
private readonly active = new Set<CodeModePromise>()
private readonly ids = new WeakMap<CodeModePromise, number>()
private readonly observed = new WeakSet<CodeModePromise>()
private readonly failures = new Map<number, Diagnostic>()
private nextID = 0
constructor(private readonly scope: Scope.Scope) {}
create(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<SandboxPromise, never, R> {
create(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<CodeModePromise, never, R> {
return Effect.suspend(() => {
// Allocate before forking so reruns get distinct IDs and diagnostics retain creation order.
const id = this.nextID++
return Effect.map(Effect.forkIn(effect, this.scope, { startImmediately: true }), (fiber) => {
const promise = new SandboxPromise(fiber)
const promise = new CodeModePromise(fiber)
this.active.add(promise)
this.ids.set(promise, id)
fiber.addObserver((exit) => {
@ -55,14 +55,14 @@ export class PromiseRuntime<R> {
}
// Observation must be recorded when responsibility transfers, before the consumer fiber runs.
markObserved(promise: SandboxPromise): void {
markObserved(promise: CodeModePromise): void {
this.observed.add(promise)
const id = this.ids.get(promise)
this.ids.delete(promise)
if (id !== undefined) this.failures.delete(id)
}
await(promise: SandboxPromise): Effect.Effect<Exit.Exit<unknown, unknown>> {
await(promise: CodeModePromise): Effect.Effect<Exit.Exit<unknown, unknown>> {
return Fiber.await(promise.fiber)
}
@ -94,7 +94,7 @@ export const invokePromiseMethod = <R>(
): Effect.Effect<unknown, unknown, R> => {
if (ref.name === "resolve") {
const value = args[0]
return value instanceof SandboxPromise ? Effect.succeed(value) : promises.create(Effect.succeed(value))
return value instanceof CodeModePromise ? Effect.succeed(value) : promises.create(Effect.succeed(value))
}
if (ref.name === "reject") {
return promises.create(Effect.fail(new ProgramThrow(args[0])))
@ -114,19 +114,19 @@ export const invokePromiseMethod = <R>(
const items = Array.from(spread)
for (const item of items) {
if (item instanceof SandboxPromise) promises.markObserved(item)
if (item instanceof CodeModePromise) promises.markObserved(item)
}
switch (ref.name) {
case "all": {
const observations = items.map((item) =>
item instanceof SandboxPromise ? Effect.flatten(promises.await(item)) : Effect.succeed(item),
item instanceof CodeModePromise ? Effect.flatten(promises.await(item)) : Effect.succeed(item),
)
return promises.create(settleAfterTurn(Effect.all(observations, { concurrency: "unbounded" })))
}
case "allSettled": {
const observations = items.map((item) =>
item instanceof SandboxPromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
item instanceof CodeModePromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
)
return promises.create(
settleAfterTurn(
@ -168,13 +168,13 @@ export const invokePromiseMethod = <R>(
)
}
const observations = items.map((item) =>
item instanceof SandboxPromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
item instanceof CodeModePromise ? promises.await(item) : Effect.succeed(Exit.succeed(item)),
)
return promises.create(settleAfterTurn(Effect.flatten(Effect.raceAll(observations))))
}
case "any": {
const flipped = items.map((item) =>
item instanceof SandboxPromise
item instanceof CodeModePromise
? Effect.flatMap(promises.await(item), (exit) => {
if (Exit.isSuccess(exit)) return Effect.fail(new PromiseAnyFulfilled(exit.value))
if (Cause.hasInterruptsOnly(exit.cause)) return Effect.failCause(exit.cause)
@ -201,7 +201,7 @@ export const invokePromiseInstanceMethod = <R>(
ref: PromiseInstanceMethodReference,
args: Array<unknown>,
node: AstNode,
): Effect.Effect<SandboxPromise, never, R> => {
): Effect.Effect<CodeModePromise, never, R> => {
const method = `Promise.prototype.${ref.name}`
promises.markObserved(ref.promise)
if (ref.name === "finally") {
@ -217,7 +217,7 @@ export const constructPromise = <R>(
promises: PromiseRuntime<R>,
executor: unknown,
node: AstNode,
): Effect.Effect<SandboxPromise, unknown, R> => {
): Effect.Effect<CodeModePromise, unknown, R> => {
if (!(executor instanceof CodeModeFunction)) {
throw new InterpreterRuntimeError(
"new Promise(...) expects an executor function (e.g. new Promise((resolve, reject) => { ... })).",
@ -226,10 +226,10 @@ export const constructPromise = <R>(
}
return Effect.gen(function* () {
const deferred = Deferred.makeUnsafe<unknown, unknown>()
const box: { own?: SandboxPromise } = {}
const box: { own?: CodeModePromise } = {}
const promise = yield* promises.create(
Effect.flatMap(Deferred.await(deferred), (value) => {
if (!(value instanceof SandboxPromise)) return Effect.succeed(value)
if (!(value instanceof CodeModePromise)) return Effect.succeed(value)
if (value === box.own) return Effect.fail(selfResolutionError(node))
return runner.settlePromise(value)
}),
@ -281,7 +281,7 @@ const reactionHandler = (value: unknown, method: string, node: AstNode): Reactio
// Teardown bypasses handlers; settled reactions yield once so handlers never run inline.
const reactionExit = <R>(
promises: PromiseRuntime<R>,
source: SandboxPromise,
source: CodeModePromise,
): Effect.Effect<Exit.Exit<unknown, unknown>, unknown, R> =>
Effect.gen(function* () {
const exit = yield* promises.await(source)
@ -293,13 +293,13 @@ const reactionExit = <R>(
const chainReaction = <R>(
runner: CallbackRunner<R>,
promises: PromiseRuntime<R>,
source: SandboxPromise,
source: CodeModePromise,
onFulfilled: ReactionHandler | undefined,
onRejected: ReactionHandler | undefined,
method: string,
node: AstNode,
): Effect.Effect<SandboxPromise, never, R> => {
const box: { derived?: SandboxPromise } = {}
): Effect.Effect<CodeModePromise, never, R> => {
const box: { derived?: CodeModePromise } = {}
const body = Effect.gen(function* () {
const exit = yield* reactionExit(promises, source)
const handler = Exit.isSuccess(exit) ? onFulfilled : onRejected
@ -307,7 +307,7 @@ const chainReaction = <R>(
const input = Exit.isSuccess(exit) ? exit.value : caughtErrorValue(Cause.squash(exit.cause))
const result = yield* applyCollectionCallback(runner, handler, method, node)([input])
if (result === box.derived) return yield* Effect.fail(selfResolutionError(node))
if (result instanceof SandboxPromise) return yield* runner.settlePromise(result)
if (result instanceof CodeModePromise) return yield* runner.settlePromise(result)
return result
})
return Effect.map(promises.create(body), (derived) => {
@ -319,17 +319,17 @@ const chainReaction = <R>(
const chainFinally = <R>(
runner: CallbackRunner<R>,
promises: PromiseRuntime<R>,
source: SandboxPromise,
source: CodeModePromise,
cleanup: ReactionHandler | undefined,
method: string,
node: AstNode,
): Effect.Effect<SandboxPromise, never, R> =>
): Effect.Effect<CodeModePromise, never, R> =>
promises.create(
Effect.gen(function* () {
const exit = yield* reactionExit(promises, source)
if (cleanup !== undefined) {
const result = yield* applyCollectionCallback(runner, cleanup, method, node)([])
if (result instanceof SandboxPromise) yield* runner.settlePromise(result)
if (result instanceof CodeModePromise) yield* runner.settlePromise(result)
}
return yield* exit
}),

View file

@ -15,7 +15,7 @@ import {
UriFunction,
} from "./model.js"
import { ToolReference } from "../tool-runtime.js"
import { isSandboxValue, SandboxPromise } from "../values.js"
import { isCodeModeValue, CodeModePromise } from "../values.js"
export const isRuntimeReference = (value: unknown): boolean =>
value instanceof CodeModeFunction ||
@ -26,13 +26,13 @@ export const isRuntimeReference = (value: unknown): boolean =>
value instanceof PromiseNamespace ||
value instanceof PromiseMethodReference ||
value instanceof PromiseInstanceMethodReference ||
value instanceof SandboxPromise ||
value instanceof CodeModePromise ||
value instanceof CoercionFunction ||
value instanceof UriFunction ||
value instanceof SearchFunction ||
value instanceof PromiseCapabilityFunction ||
value instanceof ErrorConstructorReference ||
isSandboxValue(value)
isCodeModeValue(value)
export const containsRuntimeReference = (value: unknown, seen = new Set<object>()): boolean => {
if (isRuntimeReference(value)) return true
@ -46,9 +46,9 @@ export const containsRuntimeReference = (value: unknown, seen = new Set<object>(
return contains
}
// Sandbox values are data here, not opaque interpreter references.
// CodeMode values are data here, not opaque interpreter references.
export const containsOpaqueReference = (value: unknown, seen = new Set<object>()): boolean => {
if (isSandboxValue(value)) return false
if (isCodeModeValue(value)) return false
if (isRuntimeReference(value)) return true
if (value === null || typeof value !== "object") return false
if (seen.has(value)) return false

View file

@ -73,14 +73,14 @@ import {
valueConstructors,
} from "../stdlib/value.js"
import {
isSandboxValue,
SandboxDate,
SandboxMap,
SandboxPromise,
SandboxRegExp,
SandboxSet,
SandboxURL,
SandboxURLSearchParams,
isCodeModeValue,
CodeModeDate,
CodeModeMap,
CodeModePromise,
CodeModeRegExp,
CodeModeSet,
CodeModeURL,
CodeModeURLSearchParams,
} from "../values.js"
const instanceofValue = (lhs: unknown, rhs: unknown, node: AstNode): boolean => {
@ -91,24 +91,24 @@ const instanceofValue = (lhs: unknown, rhs: unknown, node: AstNode): boolean =>
if (rhs instanceof GlobalNamespace) {
switch (rhs.name) {
case "Date":
return lhs instanceof SandboxDate
return lhs instanceof CodeModeDate
case "RegExp":
return lhs instanceof SandboxRegExp
return lhs instanceof CodeModeRegExp
case "Map":
return lhs instanceof SandboxMap
return lhs instanceof CodeModeMap
case "Set":
return lhs instanceof SandboxSet
return lhs instanceof CodeModeSet
case "URL":
return lhs instanceof SandboxURL
return lhs instanceof CodeModeURL
case "URLSearchParams":
return lhs instanceof SandboxURLSearchParams
return lhs instanceof CodeModeURLSearchParams
case "Array":
return Array.isArray(lhs)
case "Object":
return lhs !== null && (typeof lhs === "object" || typeofValue(lhs) === "function")
}
}
if (rhs instanceof PromiseNamespace) return lhs instanceof SandboxPromise
if (rhs instanceof PromiseNamespace) return lhs instanceof CodeModePromise
if (rhs instanceof CoercionFunction && (rhs.name === "Number" || rhs.name === "String" || rhs.name === "Boolean")) {
return false
}
@ -226,7 +226,7 @@ export class Interpreter<R> {
}
// The implicit async body adopts returned promises before copy-out.
if (value instanceof SandboxPromise) value = yield* self.settlePromise(value)
if (value instanceof CodeModePromise) value = yield* self.settlePromise(value)
return value
}).pipe(Effect.ensuring(Effect.sync(() => self.scopes.pop())))
}
@ -235,16 +235,16 @@ export class Interpreter<R> {
private createToolCallPromise(
path: ReadonlyArray<string>,
args: Array<unknown>,
): Effect.Effect<SandboxPromise, never, R> {
): Effect.Effect<CodeModePromise, never, R> {
return this.createPromise(Effect.suspend(() => this.invokeTool(path, args)))
}
private createPromise(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<SandboxPromise, never, R> {
private createPromise(effect: Effect.Effect<unknown, unknown, R>): Effect.Effect<CodeModePromise, never, R> {
return this.promises.create(effect)
}
// Fiber exits make settlement idempotent; yielding prevents inline continuation.
private settlePromise(promise: SandboxPromise): Effect.Effect<unknown, unknown, never> {
private settlePromise(promise: CodeModePromise): Effect.Effect<unknown, unknown, never> {
const promises = this.promises
return Effect.suspend(() => {
promises.markObserved(promise)
@ -971,7 +971,7 @@ export class Interpreter<R> {
// Await always suspends, including for plain values.
const self = this
return Effect.flatMap(this.evaluateExpression(getNode(node, "argument")), (value) =>
value instanceof SandboxPromise ? self.settlePromise(value) : Effect.as(Effect.yieldNow, value),
value instanceof CodeModePromise ? self.settlePromise(value) : Effect.as(Effect.yieldNow, value),
)
}
case "NewExpression":
@ -1019,23 +1019,23 @@ export class Interpreter<R> {
throw unsupportedSyntax("NewExpression", node)
}
private constructDate(args: Array<unknown>): SandboxDate {
if (args.length === 0) return new SandboxDate(Date.now())
private constructDate(args: Array<unknown>): CodeModeDate {
if (args.length === 0) return new CodeModeDate(Date.now())
if (args.length === 1) {
const arg = args[0]
if (arg instanceof SandboxDate) return new SandboxDate(arg.time)
if (typeof arg === "number") return new SandboxDate(new Date(arg).getTime())
if (typeof arg === "string") return new SandboxDate(Date.parse(arg))
return new SandboxDate(Number.NaN)
if (arg instanceof CodeModeDate) return new CodeModeDate(arg.time)
if (typeof arg === "number") return new CodeModeDate(new Date(arg).getTime())
if (typeof arg === "string") return new CodeModeDate(Date.parse(arg))
return new CodeModeDate(Number.NaN)
}
const parts = args.map((arg) => coerceToNumber(arg))
return new SandboxDate(new Date(...(parts as [number, number])).getTime())
return new CodeModeDate(new Date(...(parts as [number, number])).getTime())
}
private constructRegExp(args: Array<unknown>, node: AstNode): SandboxRegExp {
private constructRegExp(args: Array<unknown>, node: AstNode): CodeModeRegExp {
const first = args[0]
const pattern =
first instanceof SandboxRegExp ? first.regex.source : first === undefined ? "" : coerceToString(first)
first instanceof CodeModeRegExp ? first.regex.source : first === undefined ? "" : coerceToString(first)
const flagsArg = args[1]
if (flagsArg !== undefined && typeof flagsArg !== "string") {
throw new InterpreterRuntimeError(
@ -1043,9 +1043,9 @@ export class Interpreter<R> {
node,
)
}
const flags = flagsArg ?? (first instanceof SandboxRegExp ? first.regex.flags : "")
const flags = flagsArg ?? (first instanceof CodeModeRegExp ? first.regex.flags : "")
try {
return new SandboxRegExp(pattern, flags)
return new CodeModeRegExp(pattern, flags)
} catch (error) {
const reason = regexFailureReason(error)
throw new InterpreterRuntimeError(
@ -1057,12 +1057,12 @@ export class Interpreter<R> {
}
}
private constructMap(init: unknown, node: AstNode): SandboxMap {
const target = new SandboxMap()
private constructMap(init: unknown, node: AstNode): CodeModeMap {
const target = new CodeModeMap()
if (init === undefined || init === null) return target
const entries = Array.isArray(init)
? init
: init instanceof SandboxMap
: init instanceof CodeModeMap
? Array.from(init.map.entries(), ([key, item]): Array<unknown> => [key, item])
: undefined
if (entries === undefined) {
@ -1080,12 +1080,12 @@ export class Interpreter<R> {
return target
}
private constructSet(init: unknown, node: AstNode): SandboxSet {
const target = new SandboxSet()
private constructSet(init: unknown, node: AstNode): CodeModeSet {
const target = new CodeModeSet()
if (init === undefined || init === null) return target
const items = Array.isArray(init)
? init
: init instanceof SandboxSet
: init instanceof CodeModeSet
? Array.from(init.set.values())
: typeof init === "string"
? Array.from(init)
@ -1097,7 +1097,7 @@ export class Interpreter<R> {
return target
}
private constructURL(args: Array<unknown>, node: AstNode): SandboxURL {
private constructURL(args: Array<unknown>, node: AstNode): CodeModeURL {
if (args.length === 0) {
throw new InterpreterRuntimeError("new URL(...) requires a URL string and an optional base URL.", node).as(
"TypeError",
@ -1106,7 +1106,7 @@ export class Interpreter<R> {
const input = urlArgument(args[0], "new URL input")
const base = args[1] === undefined ? undefined : urlArgument(args[1], "new URL base")
try {
return new SandboxURL(new URL(input, base))
return new CodeModeURL(new URL(input, base))
} catch {
throw new InterpreterRuntimeError(
`new URL(...) received an invalid URL${base === undefined ? "" : " or base URL"}.`,
@ -1115,16 +1115,16 @@ export class Interpreter<R> {
}
}
private constructURLSearchParams(init: unknown, node: AstNode): SandboxURLSearchParams {
if (init === undefined) return new SandboxURLSearchParams(new URLSearchParams())
if (init instanceof SandboxURLSearchParams) {
return new SandboxURLSearchParams(new URLSearchParams(init.params))
private constructURLSearchParams(init: unknown, node: AstNode): CodeModeURLSearchParams {
if (init === undefined) return new CodeModeURLSearchParams(new URLSearchParams())
if (init instanceof CodeModeURLSearchParams) {
return new CodeModeURLSearchParams(new URLSearchParams(init.params))
}
if (typeof init === "string") return new SandboxURLSearchParams(new URLSearchParams(init))
if (typeof init === "string") return new CodeModeURLSearchParams(new URLSearchParams(init))
if (init === null || typeof init === "number" || typeof init === "boolean") {
return new SandboxURLSearchParams(new URLSearchParams(coerceToString(init)))
return new CodeModeURLSearchParams(new URLSearchParams(coerceToString(init)))
}
if (init instanceof SandboxMap) {
if (init instanceof CodeModeMap) {
return this.constructURLSearchParams(
Array.from(init.map.entries(), ([key, value]) => [key, value]),
node,
@ -1143,9 +1143,9 @@ export class Interpreter<R> {
string,
]
})
return new SandboxURLSearchParams(new URLSearchParams(entries))
return new CodeModeURLSearchParams(new URLSearchParams(entries))
}
if (isSandboxValue(init)) return new SandboxURLSearchParams(new URLSearchParams())
if (isCodeModeValue(init)) return new CodeModeURLSearchParams(new URLSearchParams())
const data = boundedData(init, "new URLSearchParams input")
if (data === null || typeof data !== "object") {
throw new InterpreterRuntimeError(
@ -1153,7 +1153,7 @@ export class Interpreter<R> {
node,
).as("TypeError")
}
return new SandboxURLSearchParams(
return new CodeModeURLSearchParams(
new URLSearchParams(Object.fromEntries(Object.entries(data).map(([key, value]) => [key, coerceToString(value)]))),
)
}
@ -1176,7 +1176,7 @@ export class Interpreter<R> {
// Null-prototype data needs explicit primitive coercion; identity and `in` retain raw objects.
// Dates use string coercion for `+` and epoch time elsewhere.
const coerceOperand = (operand: unknown): unknown => {
if (operand instanceof SandboxDate) return operator === "+" ? coerceToString(operand) : operand.time
if (operand instanceof CodeModeDate) return operator === "+" ? coerceToString(operand) : operand.time
return operand !== null && typeof operand === "object" ? coerceToString(operand) : operand
}
const bothObjects = lhs !== null && typeof lhs === "object" && rhs !== null && typeof rhs === "object"
@ -1261,7 +1261,7 @@ export class Interpreter<R> {
throw new InterpreterRuntimeError("Unary operators require data values in CodeMode.", node, "InvalidDataValue")
}
const operand =
value instanceof SandboxDate
value instanceof CodeModeDate
? value.time
: value !== null && typeof value === "object"
? coerceToString(value)
@ -1520,11 +1520,11 @@ export class Interpreter<R> {
})
if (!fn.async) return run
// The initial yield assigns `box.own` before the body can self-resolve.
const box: { own?: SandboxPromise } = {}
const box: { own?: CodeModePromise } = {}
return Effect.map(
this.createPromise(
Effect.flatMap(run, (value) => {
if (!(value instanceof SandboxPromise)) return Effect.succeed(value)
if (!(value instanceof CodeModePromise)) return Effect.succeed(value)
if (value === box.own) return Effect.fail(selfResolutionError())
return invocation.settlePromise(value)
}),
@ -1546,7 +1546,7 @@ export class Interpreter<R> {
if (property.type === "SpreadElement") {
const spread = yield* self.evaluateExpression(getNode(property, "argument"))
if (spread === null || spread === undefined || isSandboxValue(spread)) continue
if (spread === null || spread === undefined || isCodeModeValue(spread)) continue
if (typeof spread !== "object" || Array.isArray(spread) || isRuntimeReference(spread)) {
throw new InterpreterRuntimeError(
"Object spread requires a data object in CodeMode.",
@ -1748,28 +1748,28 @@ export class Interpreter<R> {
if (objectValue.name === "String" && stringStatics.has(key)) return new GlobalMethodReference("String", key)
}
if (objectValue instanceof SandboxDate) {
if (objectValue instanceof CodeModeDate) {
if (typeof key === "string" && dateMethods.has(key)) return new IntrinsicReference(objectValue, key)
return new ComputedValue(undefined)
}
if (objectValue instanceof SandboxRegExp) {
if (objectValue instanceof CodeModeRegExp) {
if (typeof key === "string" && regexpProperties.has(key)) {
return new ComputedValue((objectValue.regex as unknown as Record<string, unknown>)[key])
}
if (typeof key === "string" && regexpMethods.has(key)) return new IntrinsicReference(objectValue, key)
return new ComputedValue(undefined)
}
if (objectValue instanceof SandboxMap) {
if (objectValue instanceof CodeModeMap) {
if (key === "size") return new ComputedValue(objectValue.map.size)
if (typeof key === "string" && mapMethods.has(key)) return new IntrinsicReference(objectValue, key)
return new ComputedValue(undefined)
}
if (objectValue instanceof SandboxSet) {
if (objectValue instanceof CodeModeSet) {
if (key === "size") return new ComputedValue(objectValue.set.size)
if (typeof key === "string" && setMethods.has(key)) return new IntrinsicReference(objectValue, key)
return new ComputedValue(undefined)
}
if (objectValue instanceof SandboxURL) {
if (objectValue instanceof CodeModeURL) {
if (key === "searchParams") {
return new ComputedValue(objectValue.searchParams)
}
@ -1777,7 +1777,7 @@ export class Interpreter<R> {
if (typeof key === "string" && urlProperties.has(key)) return { target: objectValue, key }
return new ComputedValue(undefined)
}
if (objectValue instanceof SandboxURLSearchParams) {
if (objectValue instanceof CodeModeURLSearchParams) {
if (key === "size") return new ComputedValue(objectValue.params.size)
if (typeof key === "string" && urlSearchParamsMethods.has(key)) {
return new IntrinsicReference(objectValue, key)
@ -1786,7 +1786,7 @@ export class Interpreter<R> {
}
// Reject unknown promise properties so a missing await cannot hide.
if (objectValue instanceof SandboxPromise) {
if (objectValue instanceof CodeModePromise) {
if (key === "then" || key === "catch" || key === "finally") {
return new PromiseInstanceMethodReference(objectValue, key)
}
@ -1851,7 +1851,7 @@ export class Interpreter<R> {
}
return reference.key === "length" ? reference.target.length : reference.target[Number(reference.key)]
}
if (reference.target instanceof SandboxURL) {
if (reference.target instanceof CodeModeURL) {
return (reference.target.url as unknown as Record<string, unknown>)[String(reference.key)]
}
return reference.target[String(reference.key)]
@ -1891,7 +1891,7 @@ export class Interpreter<R> {
}
const key = Array.isArray(reference.target) ? Number(reference.key) : String(reference.key)
const current =
reference.target instanceof SandboxURL
reference.target instanceof CodeModeURL
? (reference.target.url as unknown as Record<string, unknown>)[key]
: (reference.target as Record<PropertyKey, unknown>)[key]
const { write, next, result } = yield* compute(current)
@ -1915,7 +1915,7 @@ export class Interpreter<R> {
target[index] = next
return
}
if (reference.target instanceof SandboxURL) {
if (reference.target instanceof CodeModeURL) {
const property = key as string
if (!urlWritableProperties.has(property)) {
throw new InterpreterRuntimeError(`URL.${property} is read-only.`, node).as("TypeError")