import { Subprocess, spawn } from "bun"; import { waitUntil } from "web-utils"; import { SAction } from "../actions"; import { code } from "../editor/code/util-code"; import { docs } from "../entity/docs"; import { snapshot } from "../entity/snapshot"; import { SyncConnection } from "../type"; import { dirAsync } from "fs-jetpack"; import path from "path"; import { gunzipAsync } from "../entity/zlib"; const decoder = new TextDecoder(); const code_startup = { process: {} as Record, }; export const code_action: SAction["code"]["action"] = async function ( this: SyncConnection, arg ) { const { type } = arg; switch (type) { case "startup-check": { const cs = code_startup.process[arg.site_id]; if (!cs) { const pkg_file = Bun.file( code.path(arg.site_id, "site", "src", "package.json") ); const pkg_json = await pkg_file.json(); if (!pkg_json.scripts || !pkg_json.scripts.startup) { return { type, status: "disabled" }; } return { type, status: "stopped" }; } return { type, status: cs.killed ? "stopped" : "running" }; } case "startup-run": { const cs = code_startup.process[arg.site_id]; if (!cs) { code_startup.process[arg.site_id] = spawn({ cmd: ["npm", "run", "startup"], cwd: code.path(arg.site_id, "site", "src"), stdio: ["pipe", "inherit", "inherit"], }); code_startup.process[arg.site_id].exited.then((e) => { delete code_startup.process[arg.site_id]; }); await waitUntil(1000); } break; } case "startup-stop": { const cs = code_startup.process[arg.site_id]; if (cs && !cs.killed) { cs.kill(); await waitUntil(1000); } break; } case "flush-page-cache": { const { page_id } = arg; snapshot.del("page", page_id); if (docs.page[page_id] && docs.page[page_id].doc) { docs.page[page_id].doc.destroy(); } delete docs.page[page_id]; break; } case "check-typings": { const dir = code.path(arg.site_id, "site", "src", "typings"); try { if ( (await Bun.file(path.join(dir, "hash")).text()) === arg.hash.toString() ) { return { type: "check-typings", hash: true }; } } catch (e) {} return { type: "check-typings", hash: false }; } case "push-typings": { const dir = code.path(arg.site_id, "site", "src", "typings"); await dirAsync(dir); await dirAsync(path.join(dir, "runtime")); Bun.write(Bun.file(path.join(dir, "hash")), arg.hash.toString()); const res = JSON.parse(decoder.decode(await gunzipAsync(arg.body))); await Bun.write(Bun.file(path.join(dir, "api.d.ts")), res.api); await Bun.write( Bun.file(path.join(dir, "prisma.d.ts")), res.prisma["prisma.d.ts"] ); await Bun.write( Bun.file(path.join(dir, "runtime/index.d.ts")), res.prisma["runtime/index.d.ts"] ); await Bun.write( Bun.file(path.join(dir, "runtime/library.d.ts")), res.prisma["runtime/library.d.ts"] ); await Bun.write( Bun.file(path.join(dir, "global.d.ts")), `\ import type * as SRVAPI from "gen/srv/api/srv"; import { Server, WebSocketHandler } from "bun"; import prisma from "./prisma"; declare global { const db: prisma.PrismaClient; type Api = typeof SRVAPI; type ApiName = keyof Api; const api: { [k in ApiName]: Awaited["_"]["api"] }; type PrasiServer = { ws?: WebSocketHandler<{ url: string }>; http: (arg: { url: { raw: URL; pathname: string }; req: Request; server: Server; mode: "dev" | "prod"; handle: (req: Request) => Promise; index: { head: string[]; body: string[]; render: () => string }; }) => Promise; }; function createServer(arg: PrasiServer): PrasiServer; } ` ); Bun.spawn({ cmd: ["chmod", "-R", "777", "."], cwd: code.path(arg.site_id, "site", "src"), }); break; } } };