+ {history.map((entry) => {
+ const outputValue = formatOutput(entry.output);
+ const inputValue = typeof entry.input === "string" ? entry.input : JSON.stringify(entry.input, null, 2);
+ const isCopiedInput = copyStatus.id === entry.id && copyStatus.field === "input";
+ const isCopiedOutput = copyStatus.id === entry.id && copyStatus.field === "output";
- {/* Input */}
-
-
- Input
-
- {entry.input ? (
-
- ) : (
-
- No input provided.
+ return (
+
+
+
+
{entry.type || "Prompt"}
+
{entry.timestamp}
+
+
+
+
+
+
+
+
+
+
+
+
Input
+
Original prompt data
+
+
+ {inputValue ? (
+
+ ) : (
+
+ No input recorded for this entry.
+
+ )}
- )}
-
- {/* Output */}
-
-
- Output
-
- {entry.output ? (
-
- ) : (
-
- No output generated.
+
+
+
+
Output
+
Generated result
+
+
+ {outputValue ? (
+
+ ) : (
+
+ No output recorded for this entry.
+
+ )}
- )}
+
-
- ))}
+ );
+ })}
)}
diff --git a/src/lib/promptHistoryService.js b/src/lib/promptHistoryService.js
new file mode 100644
index 0000000..a64e69a
--- /dev/null
+++ b/src/lib/promptHistoryService.js
@@ -0,0 +1,29 @@
+const STORAGE_KEY = "promptHistory";
+
+export function loadLocalHistory() {
+ if (typeof localStorage === "undefined") return [];
+ try {
+ return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
+ } catch {
+ return [];
+ }
+}
+
+export function savePromptHistory(entry) {
+ const history = loadLocalHistory();
+ const nextEntry = {
+ id: entry.id ?? Date.now(),
+ type: entry.type ?? "Prompt",
+ input: entry.input ?? "",
+ output: entry.output ?? "",
+ timestamp: entry.timestamp ?? new Date().toLocaleString(),
+ };
+
+ localStorage.setItem(STORAGE_KEY, JSON.stringify([nextEntry, ...history]));
+ return nextEntry;
+}
+
+export function clearLocalHistory() {
+ if (typeof localStorage === "undefined") return;
+ localStorage.removeItem(STORAGE_KEY);
+}
diff --git a/src/lib/supabaseClient.js b/src/lib/supabaseClient.js
new file mode 100644
index 0000000..f255ec6
--- /dev/null
+++ b/src/lib/supabaseClient.js
@@ -0,0 +1,10 @@
+import { createClient } from "@supabase/supabase-js";
+
+const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
+const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
+
+if (!supabaseUrl || !supabaseAnonKey) {
+ console.warn("Supabase environment variables are missing. Prompt history will still work locally.");
+}
+
+export const supabase = createClient(supabaseUrl, supabaseAnonKey);
diff --git a/src/pages/AutomationWriter.jsx b/src/pages/AutomationWriter.jsx
index 9995606..a721d81 100644
--- a/src/pages/AutomationWriter.jsx
+++ b/src/pages/AutomationWriter.jsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { createClient } from "@supabase/supabase-js";
import Editor from "@monaco-editor/react";
+import { savePromptHistory } from "../lib/promptHistoryService";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
@@ -29,7 +30,15 @@ export default function AutomationWriter() {
if (error) throw error;
- setResponse(data?.automationScript || "");
+ const script = data?.automationScript || "";
+ setResponse(script);
+ savePromptHistory({
+ id: Date.now(),
+ type: "Automation Writer",
+ input: useCase,
+ output: script,
+ timestamp: new Date().toLocaleString(),
+ });
} catch (e) {
setErr(e.message || "Something went wrong");
} finally {
diff --git a/src/pages/BugFixer.jsx b/src/pages/BugFixer.jsx
index 5352dcc..d9fff17 100644
--- a/src/pages/BugFixer.jsx
+++ b/src/pages/BugFixer.jsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { createClient } from "@supabase/supabase-js";
import Editor from "@monaco-editor/react";
+import { savePromptHistory } from "../lib/promptHistoryService";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
@@ -39,6 +40,13 @@ export default function BugFixer() {
if (error) throw error;
setResult(data);
+ savePromptHistory({
+ id: Date.now(),
+ type: "Bug Fixer",
+ input: inputCode,
+ output: data?.fixedCode || data?.summary || JSON.stringify(data),
+ timestamp: new Date().toLocaleString(),
+ });
} catch (e) {
setErr(e.message || "Something went wrong");
} finally {
diff --git a/src/pages/CodeMigration.jsx b/src/pages/CodeMigration.jsx
index 58b98f5..db58591 100644
--- a/src/pages/CodeMigration.jsx
+++ b/src/pages/CodeMigration.jsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { createClient } from "@supabase/supabase-js";
import Editor from "@monaco-editor/react";
+import { savePromptHistory } from "../lib/promptHistoryService";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
@@ -40,6 +41,13 @@ export default function CodeMigration() {
if (error) throw error;
setMigration(data);
+ savePromptHistory({
+ id: Date.now(),
+ type: "Code Migration",
+ input: inputCode,
+ output: data?.migratedCode || JSON.stringify(data, null, 2),
+ timestamp: new Date().toLocaleString(),
+ });
} catch (e) {
setErr(e.message || "Something went wrong");
} finally {
diff --git a/src/pages/CodeOptimisation.jsx b/src/pages/CodeOptimisation.jsx
index 667ff14..26c088c 100644
--- a/src/pages/CodeOptimisation.jsx
+++ b/src/pages/CodeOptimisation.jsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { createClient } from "@supabase/supabase-js";
import Editor from "@monaco-editor/react";
+import { savePromptHistory } from "../lib/promptHistoryService";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
@@ -40,8 +41,6 @@ export default function CodeOptimizer() {
setResult(data);
- // Save to local prompt history (optional)
- const history = JSON.parse(localStorage.getItem("promptHistory") || "[]");
const newEntry = {
id: Date.now(),
type: "Code Optimization",
@@ -49,7 +48,8 @@ export default function CodeOptimizer() {
output: data?.optimizedCode || "",
timestamp: new Date().toLocaleString(),
};
- localStorage.setItem("promptHistory", JSON.stringify([newEntry, ...history]));
+
+ savePromptHistory(newEntry);
} catch (e) {
setErr(e.message || "Something went wrong");
} finally {
diff --git a/src/pages/CodeReview.jsx b/src/pages/CodeReview.jsx
index 9b6b25d..9cc8ff6 100644
--- a/src/pages/CodeReview.jsx
+++ b/src/pages/CodeReview.jsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { createClient } from "@supabase/supabase-js";
import Editor from "@monaco-editor/react";
+import { savePromptHistory } from "../lib/promptHistoryService";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
@@ -39,6 +40,13 @@ export default function CodeReview() {
if (error) throw error;
setReview(data);
+ savePromptHistory({
+ id: Date.now(),
+ type: "Code Review",
+ input: inputCode,
+ output: JSON.stringify(data, null, 2),
+ timestamp: new Date().toLocaleString(),
+ });
} catch (e) {
setErr(e.message || "Something went wrong");
} finally {
diff --git a/src/pages/TestCaseGen.jsx b/src/pages/TestCaseGen.jsx
index 21a2e5a..a02de63 100644
--- a/src/pages/TestCaseGen.jsx
+++ b/src/pages/TestCaseGen.jsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { createClient } from "@supabase/supabase-js";
import Editor from "@monaco-editor/react";
+import { savePromptHistory } from "../lib/promptHistoryService";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
diff --git a/src/pages/TestCasesFromFilesGenerator.jsx b/src/pages/TestCasesFromFilesGenerator.jsx
index e54c021..dc77b1a 100644
--- a/src/pages/TestCasesFromFilesGenerator.jsx
+++ b/src/pages/TestCasesFromFilesGenerator.jsx
@@ -1,6 +1,7 @@
import { useState, useEffect, useRef } from "react";
import { createClient } from "@supabase/supabase-js";
import * as XLSX from "xlsx";
+import { savePromptHistory } from "../lib/promptHistoryService";
import { ChevronDown, ChevronUp } from "lucide-react";
import mammoth from "mammoth";
import { PDFDocument, rgb } from "pdf-lib";
@@ -131,6 +132,13 @@ export default function FileTestCaseGen() {
if (error) throw new Error(error.message);
setResponse(data);
+ savePromptHistory({
+ id: Date.now(),
+ type: "File Test Case Generator",
+ input: file.name,
+ output: JSON.stringify(data, null, 2),
+ timestamp: new Date().toLocaleString(),
+ });
} catch (e) {
if (e.name === "AbortError") {
setErr("Generation stopped by user.");
diff --git a/src/pages/TestDataGen.jsx b/src/pages/TestDataGen.jsx
index 5b89d12..6f8eea1 100644
--- a/src/pages/TestDataGen.jsx
+++ b/src/pages/TestDataGen.jsx
@@ -1,6 +1,7 @@
import { useState } from "react";
import { createClient } from "@supabase/supabase-js";
import Editor from "@monaco-editor/react";
+import { savePromptHistory } from "../lib/promptHistoryService";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
@@ -40,6 +41,13 @@ export default function TestDataGen() {
if (error) throw error;
setResponse(data);
+ savePromptHistory({
+ id: Date.now(),
+ type: "Test Data Generator",
+ input: inputCode,
+ output: data?.testData || JSON.stringify(data, null, 2),
+ timestamp: new Date().toLocaleString(),
+ });
} catch (e) {
setErr(e.message || "Something went wrong");
} finally {
diff --git a/startify b/startify
new file mode 160000
index 0000000..c9673b7
--- /dev/null
+++ b/startify
@@ -0,0 +1 @@
+Subproject commit c9673b7025501d3d49beda01a60e79ab350e1043
diff --git a/supabase/functions/Code-Migration/index.ts b/supabase/functions/Code-Migration/index.ts
new file mode 100644
index 0000000..ffc442d
--- /dev/null
+++ b/supabase/functions/Code-Migration/index.ts
@@ -0,0 +1,124 @@
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+};
+// helpers
+function stripFences(s) {
+ if (!s) return "";
+ return s.replace(/^\s*```(?:json|[a-zA-Z]+)?\s*/i, "").replace(/\s*```\s*$/i, "").trim();
+}
+function safeParseJSON(s) {
+ try {
+ return JSON.parse(s);
+ } catch {
+ return null;
+ }
+}
+Deno.serve(async (req)=>{
+ if (req.method === "OPTIONS") return new Response("ok", {
+ headers: corsHeaders
+ });
+ try {
+ const { sourceLanguage, targetLanguage, code } = await req.json();
+ if (!sourceLanguage || !targetLanguage || !code) {
+ return new Response(JSON.stringify({
+ error: "Missing sourceLanguage, targetLanguage, or code"
+ }), {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ if (!GEMINI_API_KEY) {
+ return new Response(JSON.stringify({
+ error: "Missing GEMINI_API_KEY"
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const prompt = `
+You are a senior engineer performing a **code migration**.
+
+TASK:
+Translate the given code from ${sourceLanguage} to idiomatic ${targetLanguage}, preserving behavior and edge cases.
+
+OUTPUT (STRICT JSON ONLY):
+{
+ "migratedCode": "full translated code in ${targetLanguage}",
+ "mappingNotes": ["api/class/library mapping notes"],
+ "manualSteps": ["things the developer must do manually (libs, configs)"],
+ "limitations": ["known gaps or uncertainties"]
+}
+
+RULES:
+- Use idiomatic patterns for ${targetLanguage}.
+- Replace libraries/APIs with closest equivalents and document them in mappingNotes.
+- Keep comments meaningful if present; otherwise do not add explanations in code.
+- No markdown, no fences; escaped JSON string values only.
+
+SOURCE CODE (${sourceLanguage}):
+${code}
+ `;
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ {
+ text: prompt
+ }
+ ]
+ }
+ ]
+ })
+ });
+ if (!res.ok) {
+ const errText = await res.text();
+ return new Response(JSON.stringify({
+ error: errText
+ }), {
+ status: res.status,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const data = await res.json();
+ let raw = (data.candidates?.[0]?.content?.parts?.[0]?.text || "").trim();
+ raw = stripFences(raw);
+ const parsed = safeParseJSON(raw) || {};
+ // extra safety: strip any stray fences inside migratedCode
+ if (parsed.migratedCode) parsed.migratedCode = stripFences(String(parsed.migratedCode));
+ return new Response(JSON.stringify(parsed), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ } catch (err) {
+ return new Response(JSON.stringify({
+ error: err.message
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+});
diff --git a/supabase/functions/Code-Review/index.ts b/supabase/functions/Code-Review/index.ts
new file mode 100644
index 0000000..0d065b1
--- /dev/null
+++ b/supabase/functions/Code-Review/index.ts
@@ -0,0 +1,150 @@
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+// --- CORS headers ---
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "GET,POST,OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+};
+// --- helpers ---
+function stripFences(s) {
+ if (!s) return "";
+ return s.replace(/^\s*```(?:json|[a-zA-Z]+)?\s*/i, "").replace(/\s*```\s*$/i, "").trim();
+}
+function safeParseJSON(s) {
+ try {
+ return JSON.parse(s);
+ } catch {
+ return null;
+ }
+}
+// --- Main handler ---
+Deno.serve(async (req)=>{
+ // 🔹 Handle CORS preflight
+ if (req.method === "OPTIONS") {
+ return new Response(null, {
+ status: 204,
+ headers: {
+ ...corsHeaders
+ }
+ });
+ }
+ try {
+ const { language, code } = await req.json();
+ if (!language || !code) {
+ return new Response(JSON.stringify({
+ error: "Missing language or code"
+ }), {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ if (!GEMINI_API_KEY) {
+ return new Response(JSON.stringify({
+ error: "Missing GEMINI_API_KEY"
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ // --- Build prompt for Gemini ---
+ const prompt = `
+You are a principal engineer conducting a rigorous code review.
+
+DELIVERABLE:
+Return ONLY valid JSON with this schema:
+{
+ "summary": "2-4 sentences",
+ "score": { "readability": 0-10, "correctness": 0-10, "robustness": 0-10, "performance": 0-10, "security": 0-10, "maintainability": 0-10 },
+ "issues": [
+ {
+ "title": "short issue title",
+ "severity": "low|medium|high|critical",
+ "category": "correctness|security|performance|style|maintainability|testing",
+ "evidence": "quote or snippet",
+ "explanation": "why this matters",
+ "fix": "suggested fix snippet"
+ }
+ ],
+ "quickWins": ["bulleted actionable items"],
+ "deeperRefactors": ["bulleted larger refactors"],
+ "testingGaps": ["tests we should add"],
+ "references": ["official doc links or keywords to search"]
+}
+
+REVIEW FOCUS:
+- Catch correctness bugs, edge cases, concurrency hazards, resource leaks.
+- Security: input validation, injection, authz, secrets handling.
+- Performance: hot paths, N+1, memory, algorithmic complexity.
+- Maintainability: cohesion, coupling, naming, dead code, duplication.
+- Testing: coverage of branches, error handling, boundaries.
+- If you do not find any code suggestions, say the code looks great , no imporvements.
+CONTEXT:
+- Language: ${language}
+- Be precise but concise; do not add extra fields.
+
+
+CODE UNDER REVIEW:
+${code}
+ `;
+ // --- Call Gemini ---
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ {
+ text: prompt
+ }
+ ]
+ }
+ ]
+ })
+ });
+ if (!res.ok) {
+ const errText = await res.text();
+ return new Response(JSON.stringify({
+ error: errText
+ }), {
+ status: res.status,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const data = await res.json();
+ let raw = (data.candidates?.[0]?.content?.parts?.[0]?.text || "").trim();
+ raw = stripFences(raw);
+ const parsed = safeParseJSON(raw) || {};
+ console.log(parsed, "parsed");
+ return new Response(JSON.stringify(parsed), {
+ status: 200,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ } catch (err) {
+ return new Response(JSON.stringify({
+ error: err.message
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+});
diff --git a/supabase/functions/automation-writer/index.ts b/supabase/functions/automation-writer/index.ts
new file mode 100644
index 0000000..19ccea3
--- /dev/null
+++ b/supabase/functions/automation-writer/index.ts
@@ -0,0 +1,98 @@
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+const promptTemplate = `
+You are an expert automation engineer.
+
+The user has described an automation idea.
+Your task is to generate a clean, runnable automation script in {{LANGUAGE}}.
+
+Instructions for formatting your answer:
+1. Start with a short explanation in plain text (2–5 sentences).
+2. Then provide the script inside a fenced code block.
+ - Use triple backticks with the correct language identifier (like \`\`\`{{language.toLowerCase()}}\`\`\`).
+3. Do not include anything outside this format.
+
+Automation Idea:
+"{{IDEA}}"
+
+if you do not see anything to generate , return failed to return anythinng in explanantion
+`;
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+};
+Deno.serve(async (req)=>{
+ if (req.method === "OPTIONS") {
+ return new Response("ok", {
+ headers: corsHeaders
+ });
+ }
+ try {
+ const { language, idea } = await req.json();
+ const prompt = promptTemplate.replace(/{{LANGUAGE}}/g, language).replace(/{{IDEA}}/g, idea);
+ // 🔹 Mock fallback if no API key
+ if (!GEMINI_API_KEY) {
+ return new Response(JSON.stringify({
+ explanation: `This is a mock explanation for your ${language} automation.`,
+ code: `# mock ${language} script\nprint("Hello world!")`,
+ mock: true
+ }), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ // 🔹 Call Gemini
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ {
+ text: prompt
+ }
+ ]
+ }
+ ]
+ })
+ });
+ const data = await res.json();
+ let rawOutput = data;
+ // 🔹 Split into explanation + code
+ let explanation = rawOutput;
+ let code = "";
+ console.log(code, explanation);
+ const codeMatch = rawOutput.match(/```(\w+)?([\s\S]*?)```/);
+ if (codeMatch) {
+ explanation = rawOutput.slice(0, codeMatch.index).trim();
+ code = codeMatch[2].trim();
+ }
+ console.log(code, explanation);
+ return new Response(JSON.stringify({
+ explanation,
+ code
+ }), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ } catch (err) {
+ return new Response(JSON.stringify({
+ error: err.message
+ }), {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+});
diff --git a/supabase/functions/bug-finder-fixer/index.ts b/supabase/functions/bug-finder-fixer/index.ts
new file mode 100644
index 0000000..a46207b
--- /dev/null
+++ b/supabase/functions/bug-finder-fixer/index.ts
@@ -0,0 +1,139 @@
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+};
+// helpers
+function stripFences(s) {
+ if (!s) return "";
+ return s.replace(/^\s*```(?:json|[a-zA-Z]+)?\s*/i, "").replace(/\s*```\s*$/i, "").trim();
+}
+function safeParseJSON(s) {
+ try {
+ return JSON.parse(s);
+ } catch {
+ return null;
+ }
+}
+Deno.serve(async (req)=>{
+ if (req.method === "OPTIONS") return new Response("ok", {
+ headers: corsHeaders
+ });
+ try {
+ const { language, code } = await req.json();
+ if (!language || !code) {
+ return new Response(JSON.stringify({
+ error: "Missing language or code"
+ }), {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ if (!GEMINI_API_KEY) {
+ return new Response(JSON.stringify({
+ error: "Missing GEMINI_API_KEY"
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const prompt = `
+You are a senior engineer and debugging expert.
+
+TASK:
+Identify **bugs, vulnerabilities, and edge-case failures**, then provide a **corrected version** of the code.
+
+STRICT JSON OUTPUT ONLY:
+{
+ "summary": "1-3 sentence overview of issues & fixes",
+ "bugs": [
+ {
+ "title": "concise bug title",
+ "severity": "low|medium|high|critical",
+ "type": "logic|concurrency|security|api|performance|resource|style|other",
+ "evidence": "snippet or description that proves the bug",
+ "explanation": "why it fails and when",
+ "fix": "short explanation of the fix",
+ "patch": "unified diff or minimal code snippet showing the fix"
+ }
+ ],
+ "fixedCode": "full corrected code in ${language}"
+}
+
+GUIDELINES:
+- Prefer minimal, targeted fixes that fully resolve the root cause.
+- Consider error handling, input validation, boundary conditions, timeouts, resource cleanup.
+- Keep output deterministic; no comments outside JSON.
+- No markdown fences in any field.
+
+ORIGINAL CODE (${language}):
+${code}
+ `;
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ {
+ text: prompt
+ }
+ ]
+ }
+ ]
+ })
+ });
+ if (!res.ok) {
+ const errText = await res.text();
+ return new Response(JSON.stringify({
+ error: errText
+ }), {
+ status: res.status,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const data = await res.json();
+ let raw = (data.candidates?.[0]?.content?.parts?.[0]?.text || "").trim();
+ raw = stripFences(raw);
+ const parsed = safeParseJSON(raw) || {};
+ // Safety: strip fences inside fixedCode or patches if present
+ if (parsed.fixedCode) parsed.fixedCode = stripFences(String(parsed.fixedCode));
+ if (Array.isArray(parsed.bugs)) {
+ parsed.bugs = parsed.bugs.map((b)=>({
+ ...b,
+ patch: b?.patch ? stripFences(String(b.patch)) : b?.patch
+ }));
+ }
+ return new Response(JSON.stringify(parsed), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ } catch (err) {
+ return new Response(JSON.stringify({
+ error: err.message
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+});
diff --git a/supabase/functions/code-optmisation/index.ts b/supabase/functions/code-optmisation/index.ts
new file mode 100644
index 0000000..f9d6258
--- /dev/null
+++ b/supabase/functions/code-optmisation/index.ts
@@ -0,0 +1,133 @@
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+};
+Deno.serve(async (req)=>{
+ // Handle preflight (CORS)
+ if (req.method === "OPTIONS") {
+ return new Response("ok", {
+ headers: corsHeaders
+ });
+ }
+ try {
+ const { language, code } = await req.json();
+ if (!code || !language) {
+ return new Response(JSON.stringify({
+ error: "Missing language or code"
+ }), {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ if (!GEMINI_API_KEY) {
+ return new Response(JSON.stringify({
+ error: "Missing GEMINI_API_KEY"
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const prompt = `
+You are a **principal software engineer** tasked with optimizing the following code.
+
+DELIVERABLE:
+Return ONLY valid JSON with this schema:
+{
+ "optimizedCode": "string - full optimized code in ${language}, no markdown fences",
+ "improvements": [
+ "list of short bullet points explaining key improvements"
+ ],
+ "potentialTradeoffs": [
+ "list of possible downsides of your optimization (if any)"
+ ],
+ "references": [
+ "official docs or keywords to search for further reading"
+ ]
+}
+
+REQUIREMENTS:
+- Output must be valid JSON.
+- "optimizedCode" must contain only the improved code, not explanations or comments.
+- Focus on:
+ - Readability (clean naming, reduced complexity).
+ - Performance (remove inefficiencies, better algorithms if possible).
+ - Best practices (idiomatic style for ${language}).
+- Preserve original logic unless unsafe or redundant.
+- If the code is already optimal, return it unchanged with empty arrays for improvements/tradeoffs.
+
+CODE:
+${code}
+`;
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ {
+ text: prompt
+ }
+ ]
+ }
+ ]
+ })
+ });
+ if (!res.ok) {
+ const errText = await res.text();
+ return new Response(JSON.stringify({
+ error: errText
+ }), {
+ status: res.status,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const data = await res.json();
+ // Extract the raw text from Gemini
+ let rawText = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? "";
+ // Try to parse Gemini output as JSON
+ let parsed;
+ try {
+ parsed = JSON.parse(rawText);
+ } catch {
+ // Fallback if Gemini adds markdown fences
+ rawText = rawText.replace(/```json\n?/g, "").replace(/```$/g, "").trim();
+ parsed = JSON.parse(rawText);
+ }
+ // Clean up code field (strip accidental fences)
+ if (parsed.optimizedCode) {
+ parsed.optimizedCode = parsed.optimizedCode.replace(/```[a-zA-Z]*\n?/g, "").replace(/```$/g, "").trim();
+ }
+ return new Response(JSON.stringify(parsed), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ } catch (err) {
+ return new Response(JSON.stringify({
+ error: err.message
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+});
diff --git a/supabase/functions/file-test-gen/index.ts b/supabase/functions/file-test-gen/index.ts
new file mode 100644
index 0000000..ad6037f
--- /dev/null
+++ b/supabase/functions/file-test-gen/index.ts
@@ -0,0 +1,184 @@
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+const GEMINI_UPLOAD_URL =
+ "https://generativelanguage.googleapis.com/upload/v1beta/files?key=" + GEMINI_API_KEY;
+const GEMINI_MODEL_URL =
+ "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=" + GEMINI_API_KEY;
+
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
+};
+
+// Safe base64 → Uint8Array
+function base64ToUint8Array(base64: string) {
+ const raw = atob(base64);
+ const arr = new Uint8Array(raw.length);
+ for (let i = 0; i < raw.length; i++) arr[i] = raw.charCodeAt(i);
+ return arr;
+}
+
+Deno.serve(async (req) => {
+ if (req.method === "OPTIONS") {
+ return new Response("ok", { headers: corsHeaders });
+ }
+
+ try {
+ const { fileName, fileContent, model } = await req.json();
+ console.log("Received request:", { fileName, model });
+
+ if (!fileName || !fileContent) {
+ console.log("Missing fileName or fileContent");
+ return new Response(
+ JSON.stringify({ error: "Missing fileName or fileContent" }),
+ { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" } }
+ );
+ }
+
+ if (!GEMINI_API_KEY) {
+ console.log("Missing GEMINI_API_KEY");
+ return new Response(
+ JSON.stringify({ error: "Missing GEMINI_API_KEY" }),
+ { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
+ );
+ }
+
+ // MIME detection
+ let mimeType = "application/octet-stream";
+ const lower = fileName.toLowerCase();
+ if (lower.endsWith(".pdf")) mimeType = "application/pdf";
+ if (lower.endsWith(".docx"))
+ mimeType =
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
+ console.log("Detected MIME type:", mimeType);
+
+ // Decode Base64
+ const binary = base64ToUint8Array(fileContent);
+ console.log("Binary length:", binary.length);
+
+ // Prepare FormData for upload
+ const form = new FormData();
+ form.append(
+ "file",
+ new Blob([binary], { type: mimeType }),
+ fileName
+ );
+ console.log("FormData created with file:", fileName);
+
+ // Upload file to Gemini
+ const uploadRes = await fetch(GEMINI_UPLOAD_URL, { method: "POST", body: form });
+ console.log("Upload response status:", uploadRes.status);
+
+ if (!uploadRes.ok) {
+ const uploadErr = await uploadRes.text();
+ console.log("Upload failed:", uploadErr);
+ return new Response(
+ JSON.stringify({ error: `Upload failed: ${uploadErr}` }),
+ { status: uploadRes.status, headers: { ...corsHeaders, "Content-Type": "application/json" } }
+ );
+ }
+
+ const uploadData = await uploadRes.json();
+ console.log("Upload response data:", uploadData);
+ const fileUri = uploadData?.file?.uri;
+
+ if (!fileUri) {
+ console.log("No file URI returned after upload");
+ return new Response(
+ JSON.stringify({ error: "File upload failed, no URI returned" }),
+ { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
+ );
+ }
+
+ // Prompt
+ const prompt = `
+You are a senior QA engineer.
+Generate at least 25 detailed test cases for the uploaded document (PDF or Word DOCX).
+Make sure to generate at least 5 negative test cases.
+Also if the uploaded document is a FSD or requirement definition file, make sure to generate 1 test case for every section / requirement.
+
+DELIVERABLE:
+Return ONLY valid JSON with this exact structure:
+{
+ "testCases": [
+ {
+ "testCaseId": "TC_01",
+ "title": "string",
+ "description": "string",
+ "preconditions": "string",
+ "steps": [
+ { "stepNo": number, "action": "string", "expectedResult": "string" }
+ ]
+ }
+ ]
+}
+
+File: ${fileName}
+`;
+
+ // Call Gemini
+ console.log("Calling Gemini model with file URI:", fileUri);
+ const genRes = await fetch(GEMINI_MODEL_URL, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ { text: prompt },
+ { file_data: { mime_type: mimeType, file_uri: fileUri } },
+ ],
+ },
+ ],
+ }),
+ });
+
+ console.log("Gemini response status:", genRes.status);
+
+ if (!genRes.ok) {
+ const errText = await genRes.text();
+ console.log("Gemini model failed:", errText);
+ return new Response(
+ JSON.stringify({ error: errText }),
+ { status: genRes.status, headers: { ...corsHeaders, "Content-Type": "application/json" } }
+ );
+ }
+
+ const data = await genRes.json();
+ console.log("Gemini raw data:", data);
+
+ let rawText = data.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? "";
+ rawText = rawText.replace(/```(json|text)?\n?/gi, "").replace(/```$/g, "").trim();
+
+ let parsed;
+ try {
+ parsed = JSON.parse(rawText);
+ } catch (parseErr) {
+ console.log("Error parsing JSON from Gemini:", parseErr, "Raw text:", rawText);
+ parsed = {
+ testCases: [
+ {
+ testCaseId: "TC_ERROR",
+ title: "Error parsing test cases",
+ description: rawText.slice(0, 200),
+ preconditions: "",
+ steps: [],
+ },
+ ],
+ };
+ }
+
+ return new Response(JSON.stringify(parsed), {
+ headers: { ...corsHeaders, "Content-Type": "application/json" },
+ });
+ } catch (err) {
+ console.log("Unexpected error:", err);
+ return new Response(
+ JSON.stringify({ error: err.message }),
+ { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" } }
+ );
+ }
+});
diff --git a/supabase/functions/test-case-gen/index.ts b/supabase/functions/test-case-gen/index.ts
new file mode 100644
index 0000000..4c9fab7
--- /dev/null
+++ b/supabase/functions/test-case-gen/index.ts
@@ -0,0 +1,211 @@
+// import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+// // Load prompt.md at runtime
+// const promptTemplate = Deno.env.get("prompt") ?? "";
+// const OPENAI_API_KEY = Deno.env.get("OPENAI_API_KEY") ?? "";
+// const corsHeaders = {
+// "Access-Control-Allow-Origin": "*",
+// "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+// };
+// Deno.serve(async (req)=>{
+// if (req.method === "OPTIONS") {
+// return new Response("ok", {
+// headers: corsHeaders
+// });
+// }
+// try {
+// const { language, code } = await req.json();
+// // Replace placeholders in the prompt
+// const prompt = promptTemplate.replace(/{{LANGUAGE}}/g, language).replace(/{{CODE}}/g, code);
+// // Mock mode if no API key
+// if (!OPENAI_API_KEY) {
+// return new Response(JSON.stringify({
+// testCases: JSON.stringify([
+// `Mock test case for ${language}: Verify input is valid`,
+// "Verify expected output matches requirements",
+// "Test edge cases and invalid inputs",
+// "Ensure performance under load"
+// ]),
+// mock: true
+// }), {
+// headers: {
+// ...corsHeaders,
+// "Content-Type": "application/json"
+// }
+// });
+// }
+// console.log("Calling OpenAI...");
+// const res = await fetch("https://api.openai.com/v1/chat/completions", {
+// method: "POST",
+// headers: {
+// "Content-Type": "application/json",
+// Authorization: `Bearer ${OPENAI_API_KEY}`
+// },
+// body: JSON.stringify({
+// model: "gpt-5",
+// max_tokens: 500,
+// messages: [
+// {
+// role: "user",
+// content: prompt + code
+// }
+// ]
+// })
+// });
+// if (!res.ok) {
+// const errText = await res.text();
+// console.error("OpenAI error:", errText);
+// return new Response(JSON.stringify({
+// error: errText
+// }), {
+// status: res.status,
+// headers: {
+// ...corsHeaders,
+// "Content-Type": "application/json"
+// }
+// });
+// }
+// const data = await res.json();
+// let rawOutput = data.choices?.[0]?.message?.content ?? "[]";
+// // Try to ensure output is always valid JSON array
+// let parsed;
+// try {
+// parsed = JSON.parse(rawOutput);
+// if (!Array.isArray(parsed)) throw new Error("Not an array");
+// } catch {
+// // Fallback: wrap into array
+// parsed = [
+// rawOutput
+// ];
+// }
+// console.log("Returning test cases:", parsed);
+// return new Response(JSON.stringify({
+// testCases: JSON.stringify(parsed)
+// }), {
+// headers: {
+// ...corsHeaders,
+// "Content-Type": "application/json"
+// }
+// });
+// } catch (err) {
+// console.error("Function error:", err);
+// return new Response(JSON.stringify({
+// error: err.message
+// }), {
+// status: 400,
+// headers: {
+// ...corsHeaders,
+// "Content-Type": "application/json"
+// }
+// });
+// }
+// });
+// supabase/functions/testcase-gen/index.ts
+// functions/test-cases/index.ts
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+const promptTemplate = `
+You are an expert software tester.
+Generate runnable test cases for the provided code in {{LANGUAGE}}.
+
+STRICT RULES:
+1. Return an array of strings where each element is a FULL test case.
+2. Each test case MUST include a short comment at the top (e.g. "// Tests X scenario").
+3. Respond STRICTLY with valid JSON:
+{
+ "testCases": ["case1", "case2", "case3"]
+}
+4. Minimum of 5 test cases.
+
+Code ({{LANGUAGE}}):
+{{CODE}}
+`;
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+};
+Deno.serve(async (req)=>{
+ if (req.method === "OPTIONS") {
+ return new Response("ok", {
+ headers: corsHeaders
+ });
+ }
+ try {
+ const { language, code } = await req.json();
+ const prompt = promptTemplate.replace(/{{LANGUAGE}}/g, language).replace(/{{CODE}}/g, code);
+ if (!GEMINI_API_KEY) {
+ return new Response(JSON.stringify({
+ testCases: [
+ `// Test valid input\nassert add(2,2) == 4`,
+ `// Test negative numbers\nassert add(-1,-3) == -4`
+ ],
+ mock: true
+ }), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ {
+ text: prompt
+ }
+ ]
+ }
+ ]
+ })
+ });
+ if (!res.ok) {
+ const errText = await res.text();
+ return new Response(JSON.stringify({
+ error: errText
+ }), {
+ status: res.status,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const data = await res.json();
+ let rawOutput = data.candidates?.[0]?.content?.parts?.[0]?.text ?? "";
+ rawOutput = rawOutput.replace(/```(json|javascript|python|java|typescript|csharp)?/gi, "").trim();
+ let parsed;
+ try {
+ const obj = JSON.parse(rawOutput);
+ parsed = Array.isArray(obj.testCases) ? obj.testCases : [
+ rawOutput
+ ];
+ } catch {
+ parsed = rawOutput.split("\n").map((line)=>line.trim()).filter(Boolean);
+ }
+ return new Response(JSON.stringify({
+ testCases: parsed
+ }), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ } catch (err) {
+ return new Response(JSON.stringify({
+ error: err.message
+ }), {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+});
diff --git a/supabase/functions/test-data-gen/index.ts b/supabase/functions/test-data-gen/index.ts
new file mode 100644
index 0000000..53a47d3
--- /dev/null
+++ b/supabase/functions/test-data-gen/index.ts
@@ -0,0 +1,124 @@
+import "jsr:@supabase/functions-js/edge-runtime.d.ts";
+const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") ?? "";
+const corsHeaders = {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Methods": "POST, GET, OPTIONS",
+ "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type"
+};
+// Helpers
+function stripFences(s) {
+ if (!s) return "";
+ return s.replace(/^\s*```(?:json|[a-zA-Z]+)?\s*/i, "").replace(/\s*```\s*$/i, "").trim();
+}
+function safeParseJSON(s) {
+ try {
+ return JSON.parse(s);
+ } catch {
+ return null;
+ }
+}
+Deno.serve(async (req)=>{
+ if (req.method === "OPTIONS") {
+ return new Response("ok", {
+ headers: corsHeaders
+ });
+ }
+ try {
+ const { language, code , model } = await req.json();
+ if (!language || !code) {
+ return new Response(JSON.stringify({
+ error: "Missing language, maxLength, or code/schema"
+ }), {
+ status: 400,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ if (!GEMINI_API_KEY) {
+ return new Response(JSON.stringify({
+ error: "Missing GEMINI_API_KEY"
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const prompt = `
+You are a senior test automation engineer.
+
+TASK:
+Generate **realistic test data** in ${language} given this input schema or code:
+${code}
+
+RULES:
+- Output STRICT JSON only (no markdown, no fences).
+- Do not exceed 2000 characters in test data.
+- Use meaningful sample values (names, IDs, emails, timestamps).
+- Ensure syntax is valid for ${language}.
+- Include metadata: "manualSteps" and "limitations".
+
+OUTPUT FORMAT:
+{
+ "testData": "generated test data in ${language}",
+ "manualSteps": ["things developer must do manually (configs, libraries, etc.)"],
+ "limitations": ["known gaps or assumptions"]
+}
+`;
+ const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=${GEMINI_API_KEY}`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ body: JSON.stringify({
+ contents: [
+ {
+ role: "user",
+ parts: [
+ {
+ text: prompt
+ }
+ ]
+ }
+ ]
+ })
+ });
+ if (!res.ok) {
+ const errText = await res.text();
+ return new Response(JSON.stringify({
+ error: errText
+ }), {
+ status: res.status,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+ const data = await res.json();
+ let raw = (data.candidates?.[0]?.content?.parts?.[0]?.text || "").trim();
+ raw = stripFences(raw);
+ const parsed = safeParseJSON(raw) || {};
+ // Extra cleanup safety
+ if (parsed.testData) parsed.testData = stripFences(String(parsed.testData));
+ return new Response(JSON.stringify(parsed), {
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ } catch (err) {
+ return new Response(JSON.stringify({
+ error: err.message
+ }), {
+ status: 500,
+ headers: {
+ ...corsHeaders,
+ "Content-Type": "application/json"
+ }
+ });
+ }
+});