UNPKG

phion

Version:

Phion Development Agent and Vite Plugin for seamless code sync and auto-deploy

230 lines (229 loc) 8.56 kB
// src/plugin.ts import { readFileSync, existsSync } from "fs"; import { resolve, join, dirname } from "path"; import { fileURLToPath } from "url"; var __filename = fileURLToPath(import.meta.url); var __dirname = dirname(__filename); var getPluginVersion = () => { try { const packageJsonPath = join(__dirname, "..", "package.json"); const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); return packageJson.version; } catch (error) { console.warn("[Phion] Could not read version from package.json, using fallback"); return "0.0.1"; } }; var PLUGIN_VERSION = getPluginVersion(); var DEFAULT_UPDATE_ENDPOINT = process.env.TOOLBAR_UPDATE_ENDPOINT || "http://localhost:3004/api/toolbar"; var findToolbarBundle = () => { const __filename2 = fileURLToPath(import.meta.url); const __dirname2 = dirname(__filename2); const locations = [ // Direct in dist/toolbar folder (new package structure) join(__dirname2, "toolbar", "index.global.js"), // In node_modules join(dirname(dirname(__dirname2)), "dist", "toolbar", "index.global.js"), // One level up (when used as dependency) join(dirname(dirname(dirname(__dirname2))), "phion", "dist", "toolbar", "index.global.js") ]; for (const location of locations) { if (existsSync(location)) { return location; } } console.warn("[Phion] Could not find toolbar bundle at any of these locations:", locations); return null; }; function phionPlugin(options = {}) { const { configPath = "phion.config.json", websocketUrl = "wss://api.phion.dev", autoUpdate = true, updateEndpoint = DEFAULT_UPDATE_ENDPOINT } = options; let config = null; let toolbarEnabled = false; let cachedToolbarCode = null; let lastUpdateCheck = 0; const UPDATE_CACHE_DURATION = 0; async function checkForUpdates() { if (!autoUpdate || Date.now() - lastUpdateCheck < UPDATE_CACHE_DURATION) { return null; } try { const channel = config?.toolbar?.updateChannel || "stable"; const response = await fetch(`${updateEndpoint}/check`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ currentVersion: PLUGIN_VERSION, channel, projectId: config?.projectId }) }); if (response.ok) { lastUpdateCheck = Date.now(); return await response.json(); } } catch (error) { if (config?.debug) { console.warn("[phion-plugin] Update check failed:", error); } } return null; } async function downloadToolbarUpdate(version) { try { const response = await fetch(version.url); if (response.ok) { const code = await response.text(); if (version.checksum) { const actualChecksum = Buffer.from(code).toString("base64").slice(0, 8); if (actualChecksum !== version.checksum.slice(0, 8)) { console.warn("[phion-plugin] Checksum mismatch, using local version"); return null; } } cachedToolbarCode = code; if (config?.debug) { console.log(`[phion-plugin] Updated to toolbar version ${version.version}`); } return code; } } catch (error) { console.warn("[phion-plugin] Failed to download toolbar update:", error); } return null; } return { name: "vite-plugin-phion", configResolved(resolvedConfig) { const configFilePath = resolve(resolvedConfig.root, configPath); if (existsSync(configFilePath)) { try { const configContent = readFileSync(configFilePath, "utf-8"); config = JSON.parse(configContent); toolbarEnabled = config?.toolbar?.enabled !== false && process.env.PHION_TOOLBAR !== "false"; } catch (error) { console.warn("[phion-plugin] Failed to read config:", error); } } }, configureServer(server) { if (!toolbarEnabled || !config) return; server.middlewares.use("/phion/config.js", (req, res, next) => { try { const toolbarConfig = { projectId: config?.projectId || "", websocketUrl: config?.wsUrl || websocketUrl, // ✅ Всегда из конфига в первую очередь position: config?.toolbar?.position || "top", version: PLUGIN_VERSION, autoUpdate: config?.toolbar?.autoUpdate !== false, updateChannel: config?.toolbar?.updateChannel || "stable", debug: config?.debug || false }; res.writeHead(200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache, no-store, must-revalidate, max-age=0", Pragma: "no-cache", Expires: "0", ETag: `"${Date.now()}-${Math.random()}"`, "Last-Modified": (/* @__PURE__ */ new Date()).toUTCString() }); res.end(`window.PHION_CONFIG = ${JSON.stringify(toolbarConfig)};`); } catch (err) { console.error("[Phion] Error serving config:", err); res.writeHead(500, { "Content-Type": "text/plain" }); res.end("Error generating toolbar config"); } }); server.middlewares.use("/phion/toolbar.js", async (req, res, next) => { try { let toolbarCode = cachedToolbarCode; try { if (config?.toolbar?.autoUpdate !== false) { const updateCheck = await checkForUpdates(); if (updateCheck?.hasUpdate && updateCheck.latestVersion) { const updatedCode = await downloadToolbarUpdate(updateCheck.latestVersion); if (updatedCode) { toolbarCode = updatedCode; } } } } catch (fetchError) { } if (!toolbarCode) { const toolbarPath = findToolbarBundle(); if (toolbarPath) { toolbarCode = readFileSync(toolbarPath, "utf-8"); } } if (toolbarCode) { res.writeHead(200, { "Content-Type": "application/javascript", "Cache-Control": "no-cache, no-store, must-revalidate, max-age=0", Pragma: "no-cache", Expires: "0", ETag: `"${Date.now()}-${Math.random()}"`, "Last-Modified": (/* @__PURE__ */ new Date()).toUTCString() }); res.end(toolbarCode); } else { console.error("[Phion] Toolbar bundle not found"); res.writeHead(404, { "Content-Type": "text/plain" }); res.end("Toolbar bundle not found"); } } catch (err) { console.error("[Phion] Error serving toolbar:", err); res.writeHead(500, { "Content-Type": "text/plain" }); res.end("Error loading toolbar"); } }); server.middlewares.use("/phion/api/update-check", async (req, res) => { if (req.method !== "POST") { res.statusCode = 405; res.end("Method not allowed"); return; } try { const updateCheck = await checkForUpdates(); res.setHeader("Content-Type", "application/json"); res.end( JSON.stringify(updateCheck || { hasUpdate: false, currentVersion: PLUGIN_VERSION }) ); } catch (error) { res.statusCode = 500; res.end(JSON.stringify({ error: "Update check failed" })); } }); }, transformIndexHtml(html) { if (!toolbarEnabled || !config) return html; const cacheBuster = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}-${process.pid}`; const toolbarScript = ` <script> // Clear any cached toolbar resources if ('caches' in window) { caches.keys().then(names => { names.forEach(name => { if (name.includes('phion') || name.includes('toolbar')) { caches.delete(name); } }); }); } </script> <script src="/phion/config.js?v=${cacheBuster}&_cb=${Date.now()}&rand=${Math.random()}"></script> <script src="/phion/toolbar.js?v=${cacheBuster}&_cb=${Date.now()}&rand=${Math.random()}"></script> `; return html.replace("</body>", `${toolbarScript}</body>`); } }; } var plugin_default = phionPlugin; export { plugin_default as default, phionPlugin };