import React, { useState, useEffect, useRef, useCallback, useMemo, useContext, Fragment, createContext,} from "react";import { transform } from "sucrase"; import { infiniteQueryOptions, mutationOptions, queryOptions, skipToken, useInfiniteQuery, useMutation, useQuery, useQueryClient,} from "./hooks";import * as Components from "./components";import * as QueryHooks from "./hooks"; export type EvaluatedComponent = | { component: React.ComponentType; config: Record<string, unknown> } | { error: string }; const createGeneratedCodeRequire = () => (specifier: string): unknown => { if (specifier === "react") return React; if (specifier === "@tanstack/react-query") return QueryHooks; if (specifier === "recharts" || specifier === "lucide-react" || specifier === "./components") { return Components; } throw new Error( `Generated UI cannot import "${specifier}". Everything needed is already in scope; remove the import.`, ); }; const blockedNetworkPrimitive = (name: string) => function blockedNetworkPrimitive() { throw new Error(`${name} is disabled in generated UI. Use tools.* via useQuery/useMutation.`); }; /** Compile JSX source to plain JS using Sucrase. */export function compileJsx(code: string): string { const result = transform(code, { transforms: ["jsx", "typescript", "imports"], jsxRuntime: "classic", production: true, }); return result.code;} /** * Evaluate compiled JS in a scoped context providing React, hooks, * components, tools proxy, useQuery/useMutation, and Lucide icons. */export function evaluateComponent( compiled: string, tools: Record<string, unknown>,): EvaluatedComponent { const module = { exports: {} as Record<string, unknown> | React.ComponentType }; const exports = module.exports; const scope: Record<string, unknown> = buildGeneratedCodeScope({ module, exports, tools }); const scopeKeys = Object.keys(scope); const scopeValues = scopeKeys.map((k) => scope[k]); // Execute the compiled code and look for a component + optional config. // We check well-known names and common module export shapes so generated // code stays resilient to `export default function App()` and friends. const wrappedCode = ` "use strict"; ${compiled} var __moduleExports = module && module.exports; var __defaultExport = __moduleExports && typeof __moduleExports === "object" && "default" in __moduleExports ? __moduleExports.default : exports && exports.default; var __comp = null; if (typeof App === "function") __comp = App; else if (typeof Component === "function") __comp = Component; else if (typeof Main === "function") __comp = Main; else if (typeof __defaultExport === "function") __comp = __defaultExport; else if (typeof __moduleExports === "function") __comp = __moduleExports; else if (exports && typeof exports.App === "function") __comp = exports.App; else if (exports && typeof exports.Component === "function") __comp = exports.Component; else if (exports && typeof exports.Main === "function") __comp = exports.Main; var __cfg = typeof config === "object" && config !== null ? config : {}; return { component: __comp, config: __cfg }; `; try { // eslint-disable-next-line no-new-func const factory = new Function(...scopeKeys, wrappedCode); const result = factory(...scopeValues) as { component: React.ComponentType | null; config: Record<string, unknown>; }; if (!result.component) { return { error: "No component found. Export a function named App." }; } return { component: result.component, config: result.config }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); console.error("[executor-shell] Failed to evaluate component:", err); return { error: `Evaluation error: ${msg}` }; }} /** * The single source of truth for what generated code may reference without * declaring it. `evaluateComponent` passes these as the `new Function` * parameter list, so a name here IS in scope in the iframe and a name missing * here is NOT — which is why `create-artifact`'s redeclaration validator is pinned * against `PROVIDED_SCOPE_NAMES` by test rather than hand-maintained twice. */function buildGeneratedCodeScope(bindings: { module: unknown; exports: unknown; tools: Record<string, unknown>;}): Record<string, unknown> { const { module, exports, tools } = bindings; return { // React core React, useState, useEffect, useRef, useCallback, useMemo, useContext, Fragment, createContext, // Common module globals for resilient generated code evaluation. module, exports, require: createGeneratedCodeRequire(), // Defense in depth for direct network attempts. The inner iframe CSP is // the browser boundary; these shadows produce clearer runtime errors. fetch: blockedNetworkPrimitive("fetch"), XMLHttpRequest: blockedNetworkPrimitive("XMLHttpRequest"), WebSocket: blockedNetworkPrimitive("WebSocket"), EventSource: blockedNetworkPrimitive("EventSource"), Worker: blockedNetworkPrimitive("Worker"), SharedWorker: blockedNetworkPrimitive("SharedWorker"), // Data fetching useQuery, useInfiniteQuery, useMutation, useQueryClient, queryOptions, infiniteQueryOptions, mutationOptions, skipToken, // The tools proxy is the ONLY way generated code reaches an integration. // There is deliberately no `run(code)` escape hatch: arbitrary code is // opaque to the invalidation helpers (a hand-rolled `queryKey` defeats // `queryFilter`) and to artifact analysis, which reads `tools.<path>` // references out of the source. Pagination, the one thing `run` was // reached for, is declarative through `.infiniteQueryOptions(...)`. tools, // All UI components, icons, chart primitives ...Components, };} /** * Every name generated code may use without declaring it, derived from the * real evaluation scope rather than re-listed by hand. `create-artifact`'s * redeclaration guard consumes this, so adding a component to `components.ts` * automatically protects it from being shadowed. */export const PROVIDED_SCOPE_NAMES: readonly string[] = Object.keys( buildGeneratedCodeScope({ module: { exports: {} }, exports: {}, tools: {}, }),);
import React, {
useState,
useEffect,
useRef,
useCallback,
useMemo,
useContext,
Fragment,
createContext,
} from "react";
import { transform } from "sucrase";
import {
infiniteQueryOptions,
mutationOptions,
queryOptions,
skipToken,
useInfiniteQuery,
useMutation,
useQuery,
useQueryClient,
} from "./hooks";
import * as Components from "./components";
import * as QueryHooks from "./hooks";
export type EvaluatedComponent =
| { component: React.ComponentType; config: Record<string, unknown> }
| { error: string };
const createGeneratedCodeRequire =
() =>
(specifier: string): unknown => {
if (specifier === "react") return React;
if (specifier === "@tanstack/react-query") return QueryHooks;
if (specifier === "recharts" || specifier === "lucide-react" || specifier === "./components") {
return Components;
}
throw new Error(
`Generated UI cannot import "${specifier}". Everything needed is already in scope; remove the import.`,
);
};
const blockedNetworkPrimitive = (name: string) =>
function blockedNetworkPrimitive() {
throw new Error(`${name} is disabled in generated UI. Use tools.* via useQuery/useMutation.`);
};
/** Compile JSX source to plain JS using Sucrase. */
export function compileJsx(code: string): string {
const result = transform(code, {
transforms: ["jsx", "typescript", "imports"],
jsxRuntime: "classic",
production: true,
});
return result.code;
}
/**
* Evaluate compiled JS in a scoped context providing React, hooks,
* components, tools proxy, useQuery/useMutation, and Lucide icons.
*/
export function evaluateComponent(
compiled: string,
tools: Record<string, unknown>,
): EvaluatedComponent {
const module = { exports: {} as Record<string, unknown> | React.ComponentType };
const exports = module.exports;
const scope: Record<string, unknown> = buildGeneratedCodeScope({ module, exports, tools });
const scopeKeys = Object.keys(scope);
const scopeValues = scopeKeys.map((k) => scope[k]);
// Execute the compiled code and look for a component + optional config.
// We check well-known names and common module export shapes so generated
// code stays resilient to `export default function App()` and friends.
const wrappedCode = `
"use strict";
${compiled}
var __moduleExports = module && module.exports;
var __defaultExport =
__moduleExports && typeof __moduleExports === "object" && "default" in __moduleExports
? __moduleExports.default
: exports && exports.default;
var __comp = null;
if (typeof App === "function") __comp = App;
else if (typeof Component === "function") __comp = Component;
else if (typeof Main === "function") __comp = Main;
else if (typeof __defaultExport === "function") __comp = __defaultExport;
else if (typeof __moduleExports === "function") __comp = __moduleExports;
else if (exports && typeof exports.App === "function") __comp = exports.App;
else if (exports && typeof exports.Component === "function") __comp = exports.Component;
else if (exports && typeof exports.Main === "function") __comp = exports.Main;
var __cfg = typeof config === "object" && config !== null ? config : {};
return { component: __comp, config: __cfg };
`;
try {
// eslint-disable-next-line no-new-func
const factory = new Function(...scopeKeys, wrappedCode);
const result = factory(...scopeValues) as {
component: React.ComponentType | null;
config: Record<string, unknown>;
};
if (!result.component) {
return { error: "No component found. Export a function named App." };
}
return { component: result.component, config: result.config };
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.error("[executor-shell] Failed to evaluate component:", err);
return { error: `Evaluation error: ${msg}` };
}
}
/**
* The single source of truth for what generated code may reference without
* declaring it. `evaluateComponent` passes these as the `new Function`
* parameter list, so a name here IS in scope in the iframe and a name missing
* here is NOT — which is why `create-artifact`'s redeclaration validator is pinned
* against `PROVIDED_SCOPE_NAMES` by test rather than hand-maintained twice.
*/
function buildGeneratedCodeScope(bindings: {
module: unknown;
exports: unknown;
tools: Record<string, unknown>;
}): Record<string, unknown> {
const { module, exports, tools } = bindings;
return {
// React core
React,
useState,
useEffect,
useRef,
useCallback,
useMemo,
useContext,
Fragment,
createContext,
// Common module globals for resilient generated code evaluation.
module,
exports,
require: createGeneratedCodeRequire(),
// Defense in depth for direct network attempts. The inner iframe CSP is
// the browser boundary; these shadows produce clearer runtime errors.
fetch: blockedNetworkPrimitive("fetch"),
XMLHttpRequest: blockedNetworkPrimitive("XMLHttpRequest"),
WebSocket: blockedNetworkPrimitive("WebSocket"),
EventSource: blockedNetworkPrimitive("EventSource"),
Worker: blockedNetworkPrimitive("Worker"),
SharedWorker: blockedNetworkPrimitive("SharedWorker"),
// Data fetching
useQuery,
useInfiniteQuery,
useMutation,
useQueryClient,
queryOptions,
infiniteQueryOptions,
mutationOptions,
skipToken,
// The tools proxy is the ONLY way generated code reaches an integration.
// There is deliberately no `run(code)` escape hatch: arbitrary code is
// opaque to the invalidation helpers (a hand-rolled `queryKey` defeats
// `queryFilter`) and to artifact analysis, which reads `tools.<path>`
// references out of the source. Pagination, the one thing `run` was
// reached for, is declarative through `.infiniteQueryOptions(...)`.
tools,
// All UI components, icons, chart primitives
...Components,
};
}
/**
* Every name generated code may use without declaring it, derived from the
* real evaluation scope rather than re-listed by hand. `create-artifact`'s
* redeclaration guard consumes this, so adding a component to `components.ts`
* automatically protects it from being shadowed.
*/
export const PROVIDED_SCOPE_NAMES: readonly string[] = Object.keys(
buildGeneratedCodeScope({
module: { exports: {} },
exports: {},
tools: {},
}),
);