import "../_dnt.polyfills.js"; import * as dntShim from "../_dnt.shims.js"; import { Command } from "../deps/cliffy.js" import { blue, gray, yellow } from "../deps/std/fmt/colors.js" import { serve as httpServe } from "../deps/std/http.node.js" import { createCodegenHandler, createCorsHandler, createDevnetsHandler, createErrorHandler, } from "../server/mod.js" import { FsCache, InMemoryCache } from "../util/cache/mod.js" import { gracefulExit } from "../util/mod.js" import { tempDir } from "../util/tempDir.js" import { resolveNets } from "./resolveNets.js" export const serve = new Command() .description("Start the Capi server") .option("-n, --nets ", "nets.ts file path") .option("-p, --port ", "", { default: 4646 }) .option( "-o, --out ", "Directory at which disk-related operations (such as storing devnet logs and caching metadata) can occur", { default: "target/capi" }, ) .option("--target ", "target name in net.ts") .action(runServe) export interface RunServeOptions { nets?: string port: number out: string target?: string } async function runServe( this: { getLiteralArgs(): string[] }, { nets: netsPath, port, out, target }: RunServeOptions, ) { const literalArgs = this.getLiteralArgs() const nets = await resolveNets(netsPath) const devnetTempDir = await tempDir(out, "devnet") const href = `http://localhost:${port}/` const controller = new AbortController() const { signal } = controller const dataCache = new FsCache(out, signal) const tempCache = new InMemoryCache(signal) const running = await fetch(`${href}capi_cwd`) .then((r) => r.text()) .then((r) => r === dntShim.Deno.cwd()) .catch(() => false) if (!running) { const devnetsHandler = createDevnetsHandler(devnetTempDir, nets, signal) const codegenHandler = createCodegenHandler(dataCache, tempCache) const handler = createCorsHandler(createErrorHandler(async (request) => { const { pathname } = new URL(request.url) if (pathname === "/capi_cwd") { return new Response(dntShim.Deno.cwd()) } if (pathname.startsWith("/devnets/")) { return await devnetsHandler(request) } return await codegenHandler(request) })) await httpServe(handler, { hostname: "::", port: +port, signal, onError(error) { throw error }, async onListen() { console.log(blue("Created"), "Capi server instance", gray(`at ${href}`)) await onReady() }, }) } else { console.log(yellow("Reusing"), "Capi server instance", gray(`at ${href}`)) await onReady() } async function onReady() { const [bin, ...args] = literalArgs if (bin) { const command = new dntShim.Deno.Command(bin, { args, signal, env: { CAPI_SERVER: href, ...target ? { CAPI_TARGET: target } : {}, }, }) const status = await command.spawn().status gracefulExit(status.code) controller.abort() } } }