diff --git a/typescript/packages/common-runtime/index.d.ts b/typescript/packages/common-runtime/index.d.ts new file mode 100644 index 000000000..bc40c105c --- /dev/null +++ b/typescript/packages/common-runtime/index.d.ts @@ -0,0 +1 @@ +export * from "./common_runtime.js"; diff --git a/typescript/packages/lookslike-high-level/bundle-type-defs.js b/typescript/packages/lookslike-high-level/bundle-type-defs.js new file mode 100644 index 000000000..dc7ad6b2b --- /dev/null +++ b/typescript/packages/lookslike-high-level/bundle-type-defs.js @@ -0,0 +1,89 @@ +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +import fs from 'fs-extra'; +import path from 'path'; +import {glob} from 'glob'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const outputPath = path.resolve(__dirname, 'src', 'virtualTypeDefs.js'); + +const modules = [ + '@commontools/common-html', + '@commontools/common-builder', + '@commontools/common-runner', + '@commontools/common-runtime', + '@commontools/common-propagator', +]; + +// Collect lib.d.ts from TypeScript +const tsLibDir = path.join( + path.dirname(path.dirname(new URL(await import.meta.resolve('typescript')).pathname)), + 'lib/' +); +const tsLibPath = path.join( + tsLibDir, + 'lib.es2015.d.ts' +); +console.log('tsLibPath', tsLibPath); +const libDts = fs.readFileSync(tsLibPath, 'utf-8'); + +// Collect module .d.ts files +const moduleTypeDefs = {}; + +modules.forEach(async (module) => { + let dtsPath; + if (module === '@commontools/common-runtime') { + dtsPath = path.resolve(__dirname, '..', 'common-runtime'); + } else if (module.startsWith('@commontools/')) { + dtsPath = path.resolve(__dirname, '..', module.replace('@commontools/', ''), 'lib'); + } else if (module.startsWith('../') || module.startsWith('./')) { + dtsPath = path.resolve(__dirname, module.replace('.js', '.d.ts')); + } + + const dtsFiles = module === '@commontools/common-runtime' + ? ['common_runtime.d.ts', 'common_runtime_bg.wasm.d.ts', 'index.d.ts'] + : glob.sync('*.d.ts', { cwd: dtsPath }); + + dtsFiles.forEach((file) => { + console.log('file', file); + moduleTypeDefs[`node_modules/${module}/${file}`] = fs.readFileSync(path.join(dtsPath, file), 'utf-8'); + }); +}); + +const libFiles = glob.sync('lib.*.d.ts', { cwd: tsLibDir }); +libFiles.forEach((libFile) => { + const content = fs.readFileSync(path.join(tsLibDir, libFile), 'utf-8'); + moduleTypeDefs[libFile] = content; +}); + +// Corrected data.d.ts +moduleTypeDefs['../data.d.ts'] = ` +export type Recipe = { + // Define the properties of Recipe here + name: string; + // ... other properties +}; + +export function launch(recipe: Recipe, props: any): void; +`; + +// Add this new entry for the JavaScript file +moduleTypeDefs['../data.js'] = ` +// This file can be empty or contain any necessary JavaScript code +`; + +// Combine all type definitions +const virtualTypeDefs = { + 'lib.es2015.d.ts': libDts, + ...moduleTypeDefs, +}; + +// Generate the virtualTypeDefs.js file +const fileContent = `export const virtualTypeDefs = ${JSON.stringify(virtualTypeDefs, null, 2)};`; + +fs.ensureDirSync(path.dirname(outputPath)); +fs.writeFileSync(outputPath, fileContent, 'utf-8'); + +console.log('Bundled type definitions successfully.'); \ No newline at end of file diff --git a/typescript/packages/lookslike-high-level/package.json b/typescript/packages/lookslike-high-level/package.json index d69c28f7b..ef37cbbd5 100644 --- a/typescript/packages/lookslike-high-level/package.json +++ b/typescript/packages/lookslike-high-level/package.json @@ -21,6 +21,8 @@ }, "homepage": "https://github.com/commontoolsinc/labs#readme", "devDependencies": { + "fs-extra": "^11.2.0", + "glob": "^11.0.0", "jsdom": "^25.0.0", "typescript": "^5.2.2", "vite": "^5.2.0", @@ -35,10 +37,14 @@ "../common-frp:build", "../common-html:build", "../lookslike-sagas:build", - "../llm-client:build" + "../llm-client:build", + "bundle-type-defs" ], "command": "vite build" }, + "bundle-type-defs": { + "command": "node bundle-type-defs.js" + }, "clean": { "command": "rm -rf ./lib ./dist ./.wireit ./node_modules" } diff --git a/typescript/packages/lookslike-high-level/src/components/window-manager.ts b/typescript/packages/lookslike-high-level/src/components/window-manager.ts index d6998504e..827954c37 100644 --- a/typescript/packages/lookslike-high-level/src/components/window-manager.ts +++ b/typescript/packages/lookslike-high-level/src/components/window-manager.ts @@ -7,6 +7,7 @@ import { Charm, ID, UI, NAME, addCharms, launch } from "../data.js"; import { CellImpl, isCell, charmById } from "@commontools/common-runner"; import { repeat } from "lit/directives/repeat.js"; import { iframe } from "../recipes/iframe.js"; +import { runRemote } from "../runRemote.js"; @customElement("common-window-manager") export class CommonWindowManager extends LitElement { @@ -88,6 +89,33 @@ export class CommonWindowManager extends LitElement { const shiftHeld = event.detail.shiftHeld; console.log("Unibox submitted:", value); + if (value.startsWith("https://commoner.m4ke.workers.dev/")) { + runRemote({ url: value }); + return; + } + + if (value === '' && shiftHeld) { + const charmValues = charm.getAsProxy(); + let fieldsToInclude = Object.entries(charmValues).reduce( + (acc, [key, value]) => { + if (!key.startsWith("$") && !key.startsWith("_")) { + acc[key] = value + } + return acc; + }, + {} as any + ); + const data = JSON.stringify(fieldsToInclude, null, 2); + fetch('https://commoner.m4ke.workers.dev/', { + method: 'POST', + body: data, + }).then(r => r.json()).then(({hash}) => { + // console.log('rv', `https://commoner.m4ke.workers.dev/${hash}`) + window.open(`https://commoner.m4ke.workers.dev/${hash}`, '_blank'); + }) + return; + } + if (shiftHeld) { charm.asSimpleCell(["addToPrompt"]).send({ prompt: value } as any); } else { diff --git a/typescript/packages/lookslike-high-level/src/data.ts b/typescript/packages/lookslike-high-level/src/data.ts index c3b00ee1c..32a164b7c 100644 --- a/typescript/packages/lookslike-high-level/src/data.ts +++ b/typescript/packages/lookslike-high-level/src/data.ts @@ -35,6 +35,8 @@ import { jsonImporter } from "./recipes/jsonImport.js"; import { prompt } from "./recipes/prompts.js"; import { wiki } from "./recipes/wiki.js"; import { helloIsolated } from "./recipes/helloIsolated.js"; +import { coder } from "./recipes/coder.js"; +import { runz } from "./recipes/runz.js"; export type Charm = { [ID]: number; @@ -66,42 +68,27 @@ export function addCharms(newCharms: CellImpl[]) { } addCharms([ - run(iframe, { - title: "two way binding counter", - prompt: "counter", - data: { counter: 0 }, - }), - run(importCalendar, {}), - run(queryCollections, { - url: "/api/data/", - }), - run(todoList, { - title: "My TODOs", - items: ["Buy groceries", "Walk the dog", "Wash the car"].map((item) => ({ - title: item, - done: false, - })), - }), - run(todoList, { - title: "My grocery shopping list", - items: ["milk", "eggs", "bread"].map((item) => ({ - title: item, - done: false, - })), - }), - run(ticket, { - title: "Reservation for 'Counterstrike the Musical'", - show: "Counterstrike the Musical", - date: getFridayAndMondayDateStrings().startDate, - location: "New York", - }), - run(routine, { - title: "Morning routine", - // TODO: A lot more missing here, this is just to drive the suggestion. - locations: ["coffee shop with great baristas"], - }), - run(counters, {}), -]); + // run(coder, { + // title: "hello world", src: `const greeting: string = "Hello, TypeScript!"; + // console.log(greeting); + + // // You can also use TypeScript-specific features + // interface Person { + // name: string; + // age: number; + // } + + // const printPerson = (person: Person) => { + // console.log(person.name, 'is', person.age, 'years old'); + // }; + + // printPerson({ name: "Alice", age: 30 });`}) + // + ]); + + setTimeout(() => { + launch(runz, { hash: "ef011d2367e0421df88ef23073fa882557989d7147c3e9f50fb1c42437932e6b" , data: {items: [{title:"hello", count: 123}]}}); + }, 1000); export type RecipeManifest = { name: string; @@ -123,50 +110,6 @@ function addRecipe(recipe: Recipe) { } export const recipes: RecipeManifest[] = [ - { - name: "Explore dungeon game", - recipeId: addRecipe(dungeon), - }, - { - name: "Create a new TODO list", - recipeId: addRecipe(todoList), - }, - { - name: "Find places", - recipeId: addRecipe(localSearch), - }, - { - name: "Find a LuftBnB place to stay", - recipeId: addRecipe(luftBnBSearch), - }, - { - name: "JSON Importer", - recipeId: addRecipe(jsonImporter), - }, - { - name: "Data Designer", - recipeId: addRecipe(dataDesigner), - }, - { - name: "Create a counter", - recipeId: addRecipe(counter), - }, - { - name: "Fetch JSON from a URL", - recipeId: addRecipe(fetchExample), - }, - { - name: "Explore imagery prompts", - recipeId: addRecipe(prompt), - }, - { - name: "Explore Halucinated wiki", - recipeId: addRecipe(wiki), - }, - { - name: "Hello Isolated", - recipeId: addRecipe(helloIsolated), - }, ]; // Helper for mock data @@ -192,7 +135,7 @@ function getFridayAndMondayDateStrings() { } // Terrible hack to open a charm from a recipe -let openCharmOpener: (charmId: number) => void = () => {}; +let openCharmOpener: (charmId: number) => void = () => { }; export const openCharm = (charmId: number) => openCharmOpener(charmId); openCharm.set = (opener: (charmId: number) => void) => { openCharmOpener = opener; @@ -219,9 +162,26 @@ export function launch(recipe: Recipe, bindings: any) { ); } const charm = run(recipe, bindings); + persist(charm) // put in the collection / add a ts to make it unique id + openCharm(charm.get()[ID]); } +import {createJsonSchema} from "@commontools/common-builder"; + +function persist(charm: CellImpl) { + // let data = charm.get() + // let schema = createJsonSchema({}, data); + // let query = createDataQuery(schema) + // assert(id, "query", query) + // // asert all the things - if isCellReference is true, then check if the cell has an ID + // assert(hash, "hash") + + // from the cells, determine the schema... + // write to synopsys both the schema and the data and recipe hash + // if shared synopsys ... this ID is enough to re-hydrate the charm ... including the recipe +} + (window as any).recipes = recipes; (window as any).charms = charms; diff --git a/typescript/packages/lookslike-high-level/src/recipes/coder.ts b/typescript/packages/lookslike-high-level/src/recipes/coder.ts new file mode 100644 index 000000000..1a4a163ac --- /dev/null +++ b/typescript/packages/lookslike-high-level/src/recipes/coder.ts @@ -0,0 +1,151 @@ +import { html } from "@commontools/common-html"; +import { recipe, UI, NAME, handler, str } from "@commontools/common-builder"; +import ts from 'typescript'; +import * as commonHtml from "@commontools/common-html"; +import * as commonBuilder from "@commontools/common-builder"; +import * as commonRunner from "@commontools/common-runner"; +import * as commonData from "../data.js"; +import { virtualTypeDefs } from '../virtualTypeDefs.js'; // Adjust the path as necessary + +const runCode = handler<{}, { src: string, output: string }>( + (_, state) => { + // Custom module resolution + const customRequire = (moduleName: string) => { + switch (moduleName) { + case "@commontools/common-html": + return commonHtml; + case "@commontools/common-builder": + return commonBuilder; + case "@commontools/common-runner": + return commonRunner; + case "../data.js": + return commonData; + default: + throw new Error(`Module not found: ${moduleName}`); + } + }; + + console.log('virtualTypeDefs', virtualTypeDefs); + + // Virtual Files + const virtualFiles: { [fileName: string]: string } = virtualTypeDefs; + + // Create a virtual TypeScript compiler host + const compilerHost: ts.CompilerHost = { + fileExists: (fileName) => { + console.log('fileExists', fileName, fileName in virtualFiles, fileName === "file.ts"); + return fileName in virtualFiles || fileName === "file.ts"; + }, + getCanonicalFileName: (fileName) => fileName, + getCurrentDirectory: () => "", + getDirectories: () => [], + getDefaultLibFileName: () => "lib.es2015.d.ts", + getNewLine: () => "\n", + getSourceFile: (fileName, languageVersion) => { + if (fileName === "file.ts") { + return ts.createSourceFile(fileName, state.src, languageVersion, true); + } + if (fileName in virtualFiles) { + return ts.createSourceFile(fileName, virtualFiles[fileName], languageVersion, true); + } + return undefined; + }, + readFile: (fileName) => virtualFiles[fileName] || "", + useCaseSensitiveFileNames: () => true, + writeFile: () => {} + }; + + const compilerOptions: ts.CompilerOptions = { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2015, + noEmitOnError: true, + strict: true, + lib: ["lib.es2015.d.ts", "lib.dom.d.ts"], + }; + + const program = ts.createProgram(["file.ts"], compilerOptions, compilerHost); + + const diagnostics = ts.getPreEmitDiagnostics(program); + + if (diagnostics.length > 0) { + const errorMessages = diagnostics.map(diag => { + const message = ts.flattenDiagnosticMessageText(diag.messageText, "\n"); + if (diag.file && diag.start !== undefined) { + const { line, character } = diag.file.getLineAndCharacterOfPosition(diag.start); + return `Error ${diag.file.fileName} (${line + 1},${character + 1}): ${message}`; + } + return `Error: ${message}`; + }).join("\n"); + + console.log('errorMessages', errorMessages); + + state.output = errorMessages; + return; + } + + // Transpile the code since there are no type errors + const result = ts.transpileModule(state.src, { + compilerOptions: { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2015, + noEmitOnError: true, + strict: true, + } + }); + + const logs: string[] = []; + // const originalLog = console.log; + // console.log = (...args) => { + // logs.push(args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ')); + // }; + + try { + // Wrap the transpiled code in a function that provides the custom require and mock exports + const wrappedCode = ` + (function(require) { + const exports = {}; + ${result.outputText} + return exports; + })(${customRequire.toString()}) + `; + + const moduleExports = eval(wrappedCode); + console.log('Module exports:', JSON.stringify(moduleExports, null, 2)); + } catch (e) { + console.log('Runtime Error:', e.message); + } + + // console.log = originalLog; + state.output = logs.join('\n'); + } +); + +const updateTitle = handler<{ detail: { value: string } }, { title: string }>( + ({ detail }, state) => { (state.title = detail?.value ?? "untitled") } +); + +export const coder = recipe<{ title: string, src: string, output: string }>("code", ({ title, src, output }) => { + + // FIXME(ja): typing into the textarea doesn't update the src! Do we have a pattern + // for keeping a textarea in sync with its value? + return { + [NAME]: str`${title}`, + [UI]: html` + + + + Run +
${output}
+
`, + title, + src, + }; +}); \ No newline at end of file diff --git a/typescript/packages/lookslike-high-level/src/recipes/runz.ts b/typescript/packages/lookslike-high-level/src/recipes/runz.ts new file mode 100644 index 000000000..b79b896b9 --- /dev/null +++ b/typescript/packages/lookslike-high-level/src/recipes/runz.ts @@ -0,0 +1,167 @@ +import { html } from "@commontools/common-html"; +import { recipe, UI, NAME, handler, fetchData, str, lift } from "@commontools/common-builder"; +import { launch } from "../data.js"; +import ts from 'typescript'; +import * as commonHtml from "@commontools/common-html"; +import * as commonBuilder from "@commontools/common-builder"; +import * as commonRunner from "@commontools/common-runner"; +import * as commonData from "../data.js"; +import { virtualTypeDefs } from '../virtualTypeDefs.js'; // Adjust the path as necessary + + +// Error node_modules/@commontools/common-builder/module.d.ts (2,61): Cannot find module '@commontools/common-runtime' or its corresponding type declarations. +// Error node_modules/@commontools/common-builder/types.d.ts (1,44): Cannot find module '@commontools/common-runtime' or its corresponding type declarations. +// Error node_modules/@commontools/common-builder/utils.d.ts (12,46): Cannot find module '@commontools/common-runtime' or its corresponding type declarations. + +const runCode = lift<{ src?: string, data: any }, string>(({ src, data }) => { + if (!src || !data) { + return ''; + } + + // Custom module resolution + const customRequire = (moduleName: string) => { + switch (moduleName) { + case "@commontools/common-html": + return commonHtml; + case "@commontools/common-builder": + return commonBuilder; + case "@commontools/common-runner": + return commonRunner; + case "../data.js": + return commonData; + default: + throw new Error(`Module not found: ${moduleName}`); + } + }; + + console.log('virtualTypeDefs', Object.keys(virtualTypeDefs)); + + // Virtual Files + const virtualFiles: { [fileName: string]: string } = virtualTypeDefs; + + // Create a virtual TypeScript compiler host + const compilerHost: ts.CompilerHost = { + fileExists: (fileName) => { + + if (fileName.includes('commontools/common-runtime')) { + console.log('fileExists', fileName, fileName in virtualFiles); + } + return fileName in virtualFiles || fileName === "file.ts"; + }, + getCanonicalFileName: (fileName) => fileName, + getCurrentDirectory: () => "", + getDirectories: () => [], + getDefaultLibFileName: () => "lib.es2015.d.ts", + getNewLine: () => "\n", + getSourceFile: (fileName, languageVersion) => { + if (fileName === "file.ts") { + return ts.createSourceFile(fileName, src, languageVersion, true); + } + if (fileName in virtualFiles) { + return ts.createSourceFile(fileName, virtualFiles[fileName], languageVersion, true); + } + return undefined; + }, + readFile: (fileName) => virtualFiles[fileName] || "", + useCaseSensitiveFileNames: () => true, + writeFile: () => { } + }; + + const compilerOptions: ts.CompilerOptions = { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2015, + noEmitOnError: true, + strict: true, + lib: ["lib.es2015.d.ts", "lib.dom.d.ts"], + }; + + const program = ts.createProgram(["file.ts"], compilerOptions, compilerHost); + + const diagnostics = ts.getPreEmitDiagnostics(program); + + if (diagnostics.length > 0) { + const errorMessages = diagnostics.map(diag => { + const message = ts.flattenDiagnosticMessageText(diag.messageText, "\n"); + if (diag.file && diag.start !== undefined) { + const { line, character } = diag.file.getLineAndCharacterOfPosition(diag.start); + return `Error ${diag.file.fileName} (${line + 1},${character + 1}): ${message}`; + } + return `Error: ${message}`; + }).join("\n"); + + console.log('errorMessages', errorMessages); + + // state.output = errorMessages; + console.log('errorMessages', errorMessages); + // return; + } + + // Transpile the code since there are no type errors + const result = ts.transpileModule(src, { + compilerOptions: { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2015, + noEmitOnError: true, + strict: true, + } + }); + + const logs: string[] = []; + // const originalLog = console.log; + // console.log = (...args) => { + // logs.push(args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ')); + // }; + + try { + // Wrap the transpiled code in a function that provides the custom require and mock exports + const wrappedCode = ` + (function(require) { + const exports = {}; + ${result.outputText} + return exports; + })(${customRequire.toString()}) + `; + + const moduleExports = eval(wrappedCode); + launch(moduleExports.counters, data) + logs.push('Module exports:', JSON.stringify(moduleExports, null, 2)); + } catch (e) { + console.log('Runtime Error:', e.message); + } + + return logs.join('\n') +}); + +const updateHash = handler<{ detail: { value: string } }, { hash: string }>( + ({ detail }, state) => { (state.hash = detail?.value ?? "untitled") } +); + +const jsonify = lift(({obj}) => { + return JSON.stringify(obj, null, 2); +}); + +export const runz = recipe<{ hash: string, output: string, data: any }>("run code", ({ hash, output, data }) => { + + const url = str`https://commoner.m4ke.workers.dev/${hash}`; + const { result: src } = fetchData({ url, mode: "text" }); + output = runCode({ src, data }); + + return { + [NAME]: "run code", + [UI]: html` + + +

data

+
${jsonify({obj: data})}
+

output

+
${output}
+

src

+
${src}
+ +
`, + }; +}); \ No newline at end of file diff --git a/typescript/packages/lookslike-high-level/src/runRemote.ts b/typescript/packages/lookslike-high-level/src/runRemote.ts new file mode 100644 index 000000000..666b04d8e --- /dev/null +++ b/typescript/packages/lookslike-high-level/src/runRemote.ts @@ -0,0 +1,128 @@ +import { launch } from "./data.js"; +import ts from 'typescript'; +import * as commonHtml from "@commontools/common-html"; +import * as commonBuilder from "@commontools/common-builder"; +import * as commonRunner from "@commontools/common-runner"; +import * as commonData from "./data.js"; +import { virtualTypeDefs } from './virtualTypeDefs.js'; // Adjust the path as necessary + +const runCode = ({ src, data }: { src: string, data: any }) => { + if (!src || !data) { + return ''; + } + + // Custom module resolution + const customRequire = (moduleName: string) => { + switch (moduleName) { + case "@commontools/common-html": + return commonHtml; + case "@commontools/common-builder": + return commonBuilder; + case "@commontools/common-runner": + return commonRunner; + case "../data.js": + return commonData; + default: + throw new Error(`Module not found: ${moduleName}`); + } + }; + + // Virtual Files + const virtualFiles: { [fileName: string]: string } = virtualTypeDefs; + + // Create a virtual TypeScript compiler host + const compilerHost: ts.CompilerHost = { + fileExists: (fileName) => { + return fileName in virtualFiles || fileName === "file.ts"; + }, + getCanonicalFileName: (fileName) => fileName, + getCurrentDirectory: () => "", + getDirectories: () => [], + getDefaultLibFileName: () => "lib.es2015.d.ts", + getNewLine: () => "\n", + getSourceFile: (fileName, languageVersion) => { + if (fileName === "file.ts") { + return ts.createSourceFile(fileName, src, languageVersion, true); + } + if (fileName in virtualFiles) { + return ts.createSourceFile(fileName, virtualFiles[fileName], languageVersion, true); + } + return undefined; + }, + readFile: (fileName) => virtualFiles[fileName] || "", + useCaseSensitiveFileNames: () => true, + writeFile: () => { } + }; + + const compilerOptions: ts.CompilerOptions = { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2015, + noEmitOnError: true, + strict: true, + lib: ["lib.es2015.d.ts", "lib.dom.d.ts"], + }; + + const program = ts.createProgram(["file.ts"], compilerOptions, compilerHost); + + const diagnostics = ts.getPreEmitDiagnostics(program); + + if (diagnostics.length > 0) { + const errorMessages = diagnostics.map(diag => { + const message = ts.flattenDiagnosticMessageText(diag.messageText, "\n"); + if (diag.file && diag.start !== undefined) { + const { line, character } = diag.file.getLineAndCharacterOfPosition(diag.start); + return `Error ${diag.file.fileName} (${line + 1},${character + 1}): ${message}`; + } + return `Error: ${message}`; + }).join("\n"); + + console.log('errorMessages', errorMessages); + + // state.output = errorMessages; + console.log('errorMessages', errorMessages); + // return; + } + + // Transpile the code since there are no type errors + const result = ts.transpileModule(src, { + compilerOptions: { + module: ts.ModuleKind.CommonJS, + target: ts.ScriptTarget.ES2015, + noEmitOnError: true, + strict: true, + } + }); + + const logs: string[] = []; + // const originalLog = console.log; + // console.log = (...args) => { + // logs.push(args.map(arg => typeof arg === 'object' ? JSON.stringify(arg) : String(arg)).join(' ')); + // }; + + try { + // Wrap the transpiled code in a function that provides the custom require and mock exports + const wrappedCode = ` + (function(require) { + const exports = {}; + ${result.outputText} + return exports; + })(${customRequire.toString()}) + `; + + const { default: recipe } = eval(wrappedCode); + launch(recipe, data) + } catch (e) { + console.log('Runtime Error:', e.message); + } + + return logs.join('\n') +}; + +export const runRemote = async ({ url }: { url: string }) => { + const meta = await fetch(url).then(r => r.json()); + + const src = await fetch(meta.src).then(r => r.text()); + const data = meta.data ? (await fetch(meta.data).then(r => r.json())) : {}; + + runCode({ src, data }) +}; \ No newline at end of file