soilai
Version:
Smart-brained text-based code-gen
208 lines (207 loc) • 9.29 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
//@ts-check
const http_1 = require("http");
const constants_1 = require("../constants");
const soilai_request_1 = require("./soilai-request");
const find_file_1 = require("./find-file");
const uuid_1 = require("uuid");
const new_page_1 = require("./new-page");
const dotenv_1 = require("dotenv");
const net_1 = __importDefault(require("net"));
(0, dotenv_1.config)({ path: `.env.development` });
const soilAiDebug = ((_a = process.env.DEBUG) === null || _a === void 0 ? void 0 : _a.includes("soilai"))
? (message, data) => {
if (data)
console.log(message, data);
else
console.log(message);
}
: () => { };
function getResponseEnd(res) {
return function responseStatus(status = 200) {
soilAiDebug(`Setting response status: ${status}`);
if (!res.headersSent)
res.writeHead(status);
return function responseEnd(data) {
soilAiDebug(`Ending response with data: ${data ? JSON.stringify(data) : "No data"}`);
res.end(data ? JSON.stringify(data) : undefined);
return res;
};
};
}
const requestQueue = new Map();
const processingFiles = new Set();
const processQueue = (filePath, apiKey) => __awaiter(void 0, void 0, void 0, function* () {
if (processingFiles.has(filePath)) {
soilAiDebug(`Already processing file: ${filePath}`);
return;
}
const queue = requestQueue.get(filePath);
if (!queue || queue.length === 0) {
soilAiDebug(`No requests in queue for file: ${filePath}`);
return;
}
soilAiDebug(`Processing queue for file: ${filePath}`);
processingFiles.add(filePath);
while (queue.length > 0) {
const { data, resolve, reject } = queue.shift();
try {
soilAiDebug("Processing data with soilId:", data.soilId);
const fileData = yield (0, find_file_1.findFileWithSoilId)(data.soilId);
if (!fileData)
throw new Error("File with Soil ID not found");
const { modifiedFileContents } = yield (0, soilai_request_1.postToSoilAi)(Object.assign(Object.assign({}, fileData), { message: data.message }), apiKey);
if (!modifiedFileContents.includes(`data-soil-id="${data.soilId}"`)) {
throw new Error("Error: soilId not found in modified file contents");
}
soilAiDebug(`Writing modified contents to file: ${fileData.filePath}`);
yield (0, find_file_1.writeToFile)(fileData.filePath, modifiedFileContents);
resolve({ success: true });
}
catch (error) {
soilAiDebug(`Error processing queue for file: ${filePath}`, error);
if (error instanceof Error) {
reject({ success: false, error: error.message });
}
}
}
soilAiDebug(`Finished processing queue for file: ${filePath}`);
processingFiles.delete(filePath);
});
const server = (0, http_1.createServer)((req, res) => {
soilAiDebug(`Soil server: ${req.method} ${req.url}`);
// Set CORS headers
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE");
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With,content-type");
res.setHeader("Content-Type", "application/json");
const apiKey = process.env.SOILAI_API_KEY;
if (!apiKey) {
soilAiDebug("SOILAI_API_KEY is not defined in .env.development");
throw Error("SOILAI_API_KEY is not defined in .env.development");
}
const responseStatus = getResponseEnd(res);
if (req.method === "GET" && req.url === "/") {
soilAiDebug("Received GET request at root endpoint");
return responseStatus()();
}
if (req.method === "OPTIONS") {
soilAiDebug("Received OPTIONS request");
return responseStatus(204)();
}
if (req.method !== "POST" || req.url !== "/") {
soilAiDebug(`Invalid request: ${req.method} ${req.url}`);
return responseStatus(404)({ success: false, error: "Not found" });
}
let body = "";
req.on("data", (chunk) => {
soilAiDebug("Received chunk of data");
body += chunk.toString();
});
req.on("end", () => __awaiter(void 0, void 0, void 0, function* () {
soilAiDebug("Request data fully received");
try {
const { message, soilId, pathname } = JSON.parse(body);
soilAiDebug("Parsed request body:", { message, soilId, pathname });
if (typeof message !== "string") {
soilAiDebug("Message is required and must be a string");
throw Error("Message is required");
}
if (!soilId || (typeof soilId !== "string" && pathname)) {
soilAiDebug("Creating new Soil ID and file");
const newSoilId = (0, uuid_1.v4)();
const newFileContents = (0, new_page_1.getNewNextFile)(newSoilId);
const newFilePath = `./app${pathname}/page.tsx`;
soilAiDebug(`Posting to SoilAI for new file: ${newFilePath}`);
const { modifiedFileContents: modifiedNewFileContents } = yield (0, soilai_request_1.postToSoilAi)({
message,
fileContents: newFileContents,
filePath: newFilePath,
fileExt: "tsx",
soilId: newSoilId,
}, apiKey);
if (!modifiedNewFileContents.includes(newSoilId)) {
soilAiDebug("Error: soilId not found in modified file contents");
throw new Error("Error: soilId not found in modified file contents");
}
soilAiDebug(`Writing new file to path: ${newFilePath}`);
yield (0, find_file_1.writeToFile)(newFilePath, modifiedNewFileContents);
}
else {
soilAiDebug(`Adding request to queue for Soil ID: ${soilId}`);
const fileData = yield (0, find_file_1.findFileWithSoilId)(soilId);
if (!fileData) {
soilAiDebug("File with Soil ID not found");
throw Error("File with Soil ID not found");
}
const filePath = fileData.filePath;
// Create a new promise to handle the response
const queuePromise = new Promise((resolve, reject) => {
// Add request to the queue
if (!requestQueue.has(filePath)) {
requestQueue.set(filePath, []);
}
requestQueue
.get(filePath)
.push({ data: Object.assign(Object.assign({}, fileData), { message }), resolve, reject });
// Start processing the queue
processQueue(filePath, apiKey);
});
// Send the response when the queue promise resolves or rejects
queuePromise.then(responseStatus()).catch((error) => {
soilAiDebug("Error resolving queue promise:", error);
responseStatus(400)(error);
});
}
}
catch (error) {
soilAiDebug("Error in request handling:", error);
if (error instanceof Error)
return responseStatus(400)({ success: false, error: error.message });
}
}));
});
function checkPortAvailability(port) {
return new Promise((resolve) => {
const server = net_1.default
.createServer()
.once("error", () => resolve(false))
.once("listening", () => {
server.close();
resolve(true);
})
.listen(port);
});
}
function findAvailablePort(startPort) {
return __awaiter(this, void 0, void 0, function* () {
let port = startPort;
while (true) {
const isAvailable = yield checkPortAvailability(port);
if (isAvailable) {
return port;
}
port++;
}
});
}
findAvailablePort(constants_1.PORT).then((availablePort) => {
server.listen(availablePort, () => {
console.log(`Soil dev server is listening on port ${availablePort}`);
});
});