import React, { useState, useEffect } from "react"; import Header from "./components/Header"; import Footer from "./components/Footer"; import MainPage from "./components/MainPage"; import ApiKeyPage from "./components/ApiKeyPage"; import "./App.css"; // Utility function to generate a cryptographically secure API key function generateApiKey() { const array = new Uint8Array(16); // Use crypto.getRandomValues if available (browser), fallback for tests if (typeof crypto !== "undefined" && crypto.getRandomValues) { crypto.getRandomValues(array); } else { // Fallback for test environments - not cryptographically secure for (let i = 0; i < array.length; i++) { array[i] = Math.floor(Math.random() * 256); } } return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join( "", ); } // JMESPath Testing Tool - Main Application Component function App() { const [currentPage, setCurrentPage] = useState("main"); // 'main' or 'apikey' const [theme, setTheme] = useState(() => { // Load theme from localStorage or default to 'auto' return localStorage.getItem("theme") || "auto"; }); const [showReloadButton, setShowReloadButton] = useState(false); const [currentStateGuid, setCurrentStateGuid] = useState(null); const [jmespathExpression, setJmespathExpression] = useState("people[0].name"); const [jsonData, setJsonData] = useState(`{ "people": [ { "name": "John Doe", "age": 30, "city": "New York" }, { "name": "Jane Smith", "age": 25, "city": "Los Angeles" } ], "total": 2 }`); const [apiKey, setApiKey] = useState(() => { // Load API key from localStorage or generate new one const stored = localStorage.getItem("jmespath-api-key"); if (stored && /^[0-9a-f]{32}$/i.test(stored)) { return stored; } const newKey = generateApiKey(); localStorage.setItem("jmespath-api-key", newKey); return newKey; }); // Theme management useEffect(() => { const applyTheme = (selectedTheme) => { const effectiveTheme = selectedTheme === "auto" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : selectedTheme; document.documentElement.setAttribute("data-bs-theme", effectiveTheme); }; applyTheme(theme); // Save theme preference localStorage.setItem("theme", theme); }, [theme]); // Get headers for API requests const getApiHeaders = () => { return { Accept: "application/json", "X-API-Key": apiKey, }; }; // Load sample data from API on startup and setup periodic state checking useEffect(() => { loadSampleData(); // Check for state changes every 5 seconds const interval = setInterval(checkStateChange, 5000); return () => clearInterval(interval); // eslint-disable-next-line react-hooks/exhaustive-deps }, [apiKey]); // Check if state has changed (new data uploaded) const checkStateChange = async () => { try { const response = await fetch("/api/v1/state", { headers: getApiHeaders(), }); if (response.ok) { const stateData = await response.json(); if (stateData.state && stateData.state !== currentStateGuid) { setShowReloadButton(true); } } } catch (error) { // Silently handle state check errors } }; // Load sample data from API const loadSampleData = async () => { try { setShowReloadButton(false); const response = await fetch("/api/v1/sample", { headers: getApiHeaders(), }); if (response.ok) { const data = await response.json(); if (data) { setJsonData(JSON.stringify(data, null, 2)); } // Update current state GUID const stateResponse = await fetch("/api/v1/state", { headers: getApiHeaders(), }); if (stateResponse.ok) { const stateData = await stateResponse.json(); setCurrentStateGuid(stateData.state); } } } catch (error) { console.error("Failed to load sample data:", error); } }; // Regenerate API key const regenerateApiKey = () => { const newKey = generateApiKey(); setApiKey(newKey); localStorage.setItem("jmespath-api-key", newKey); setShowReloadButton(false); setCurrentStateGuid(null); }; const handleThemeChange = (newTheme) => { setTheme(newTheme); }; const handlePageChange = (newPage) => { setCurrentPage(newPage); }; return (