jetpath
Version:
A performance-first cross-runtime API framework without the boilerplate
1,409 lines (1,340 loc) • 49.5 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{NAME} API</title>
<link rel="shortcut icon" href="https://raw.githubusercontent.com/codedynasty-dev/jetpath/main/icon.png" type="image/png" />
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;500;700&family=Roboto:wght@300;400;500&display=swap">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pretty-print-json@3.0/dist/css/pretty-print-json.css">
<script src="https://cdn.jsdelivr.net/npm/pretty-print-json@3.0/dist/pretty-print-json.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" />
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" defer></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" defer></script>
<script src="https://cdn.jsdelivr.net/gh/adamvleggett/drawdown/drawdown.js"></script>
<style>
:root {
--primary-color: JETPATHCOLOR;
--primary-light: JETPATHCOLOR36;
--primary-lighter: JETPATHCOLOR2e;
--primary-dark: JETPATHCOLORb6;
--primary-border: JETPATHCOLOR5e;
--text-primary: #202124;
--text-secondary: #5f6368;
--surface-1: #ffffff;
--surface-2: #f8f9fa;
--surface-3: #f1f3f4;
--border-color: #dadce0;
--error-color: #d93025;
--success-color: #1e8e3e;
--shadow-1: 0 1px 2px 0 rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);
--radius-sm: 4px;
--radius-md: 8px;
--font-family: "Google Sans", "Roboto", -apple-system, BlinkMacSystemFont, sans-serif;
}
body {
font-family: var(--font-family);
color: var(--text-primary);
background-color: var(--surface-2);
line-height: 1.5;
font-size: 14px;
-webkit-font-smoothing: antialiased;
}
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background-color: #c1c1c1;
border-radius: 8px;
}
::-webkit-scrollbar-thumb:hover {
background-color: #a8a8a8;
}
.container-fluid {
padding-left: 25px;
padding-right: 25px;
}
header {
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 0;
z-index: 1020;
box-shadow: var(--shadow-1);
background-color: var(--primary-color) ;
color: #fff ;
margin-bottom: 20px;
display: flex;
align-items: center;
padding: 0.8rem 1.5rem;
}
.header-content {
display: flex;
align-items: center;
gap: 16px;
width: 100%;
}
.header-content h1 {
font-size: 20px;
line-height: 1.2;
color: #fff;
margin-bottom: 0;
}
.project-info {
background-color: var(--surface-1);
border-radius: var(--radius-md);
padding: 15px 20px;
margin-bottom: 20px;
box-shadow: var(--shadow-1);
border: 1px solid var(--border-color);
}
.project-info span:first-child {
font-weight: 500;
color: var(--text-secondary);
display: block;
margin-bottom: 5px;
}
.section {
max-width: 1460px;
margin: 0 auto;
margin-bottom: 2rem;
}
.section-title h2 {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--text-primary);
border-bottom: 2px solid var(--primary-color);
padding-bottom: 0.5rem;
}
.card {
background-color: var(--surface-1);
border-radius: var(--radius-md);
margin-bottom: 10px;
border: 1px solid var(--border-color);
box-shadow: none;
}
.card:hover {
box-shadow: var(--shadow-1);
}
.card-header {
background-color: var(--surface-1);
padding: 0;
border-bottom: 1px solid var(--border-color);
}
.card-header .btn-link {
padding: 12px 18px;
width: 100%;
text-align: left;
border: none;
background: none;
cursor: pointer;
display: flex;
align-items: center;
gap: 10px;
color: var(--text-primary);
font-weight: 500;
text-decoration: none;
font-size: 0.95rem;
}
.card-header .btn-link:hover {
background-color: var(--surface-3);
}
.card-header .btn-link .api-path {
word-break: break-all;
}
.card-header .btn-link::after {
content: "\25BC";
font-size: 0.8em;
margin-left: auto;
transition: transform 0.2s ease;
}
/* Down arrow */
.card-header .btn-link[aria-expanded="true"]::after {
transform: rotate(-180deg);
}
/* Up arrow */
.card-body {
padding: 18px;
background-color: var(--surface-2);
border-top: 1px solid #e0e0e0;
}
.input-group-text {
background-color: var(--surface-3);
border-right: 0;
font-weight: 500;
font-size: 0.85rem;
}
input[type="text"],
input[type="number"],
input[type="file"],
select.form-control {
border-radius: var(--radius-sm);
font-size: 0.9rem;
}
input[type="text"]:focus,
input[type="number"]:focus,
input[type="file"]:focus,
select.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 2px var(--primary-lighter);
}
.url-input {
font-family: monospace;
font-size: 0.9rem;
}
.btn-sm {
padding: .25rem .5rem;
font-size: .8rem;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.btn-primary:hover {
background-color: var(--primary-dark);
border-color: var(--primary-dark);
}
.btn-secondary {
color: var(--primary-color);
border-color: var(--primary-color);
background-color: transparent;
}
.btn-secondary:hover {
background-color: var(--primary-lighter);
color: var(--primary-dark);
border-color: var(--primary-dark);
}
span.method {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 60px;
height: 22px;
border-radius: var(--radius-sm);
color: white;
font-weight: 500;
font-size: 0.75rem;
text-transform: uppercase;
padding: 0 8px;
}
span.GET {
background-color: #0d6efd;
}
span.POST {
background-color: #198754;
}
span.PUT {
background-color: #ffc107;
color: var(--text-primary);
}
span.DELETE {
background-color: #dc3545;
}
.nav-tabs .nav-link {
font-size: 0.9rem;
color: var(--text-secondary);
border-bottom-width: 2px;
}
.nav-tabs .nav-link.active {
color: var(--primary-color);
border-color: var(--primary-color) var(--primary-color) var(--surface-1);
font-weight: 500;
}
.tab-content {
padding: 15px;
background-color: var(--surface-1);
border: 1px solid var(--border-color);
border-top: 0;
border-radius: 0 0 var(--radius-md) var(--radius-md);
}
.response-meta {
font-size: 0.85rem;
color: var(--text-secondary);
margin-bottom: 10px;
}
.response-meta span {
margin-right: 15px;
}
.response-meta .status-success {
color: var(--success-color);
font-weight: bold;
}
.response-meta .status-error {
color: var(--error-color);
font-weight: bold;
}
.code-container {
border-radius: var(--radius-sm);
overflow: auto;
margin-top: 8px;
padding: 10px;
color: #f0f0f0;
max-height: 400px;
}
.code-container pre {
margin: 0;
font-size: 0.85rem;
}
.assertion-result {
margin-bottom: 5px;
padding: 8px;
border-radius: var(--radius-sm);
font-size: 0.85rem;
}
.assertion-pass {
background-color: #d1e7dd;
color: #0f5132;
border-left: 3px solid var(--success-color);
}
.assertion-fail {
background-color: #f8d7da;
color: #842029;
border-left: 3px solid var(--error-color);
}
.toast-container {
position: fixed;
top: 20px;
right: 20px;
z-index: 1050;
}
.toast-message {
background-color: var(--primary-color);
color: white;
padding: 10px 15px;
border-radius: var(--radius-sm);
box-shadow: var(--shadow-1);
font-size: 0.9rem;
}
#api-search-input {
margin-bottom: 15px;
}
#api-history-container .list-group-item {
font-size: 0.85rem;
}
.action-buttons button {
margin-right: 10px;
}
.headers-table {
width: 100%;
font-size: 0.85rem;
}
.headers-table th,
.headers-table td {
padding: 5px;
border-bottom: 1px solid var(--surface-3);
text-align: left;
}
.headers-table th {
font-weight: 500;
background-color: var(--surface-2);
}
.payload-section,
.global-auth-section {
font-size: 0.9rem;
padding: 10px;
background-color: var(--surface-3);
border-radius: var(--radius-sm);
}
.payload-section strong,
.global-auth-section strong {
display: block;
margin-bottom: 8px;
}
.curl-output pre {
color: #f0f0f0;
background-color: #1e1e1e;
padding: 10px;
border-radius: var(--radius-sm);
white-space: pre-wrap;
word-break: break-all;
}
</style>
</head>
<body>
<header>
<div class="header-content">
<img src="{LOGO}" alt="{NAME} Logo" style="width: 36px; height: 36px;" />
<h1>{NAME} API Documentation</h1>
</div>
</header>
<div class="container-fluid">
<div class="project-info section">
<h2>Project Information</h2>
<p id="project-info-text">{INFO}</p>
<h2>Project Global Headers</h2>
<div id="keys" style="margin-top: 10px;"></div>
<div id="env-switcher-container" style="margin-top: 10px;"></div>
</div>
<div class="section">
<div class="section-title d-flex justify-content-between align-items-center">
<h2>API Endpoints</h2>
</div>
<input type="text" id="api-search-input" class="form-control"
placeholder="Search endpoints (e.g., GET /users)...">
<div class="accordion" id="api-endpoint-container"></div>
</div>
<div id="api-history-container" class="section"></div>
</div>
<footer>
<div class="container text-center py-3">
<small class="text-muted">© {CURRENT_YEAR} {NAME}. All rights reserved. Powered by Jetpath.</small>
</div>
</footer>
<div class="toast-container" id="toast-container"></div>
<script>
// src/assets/bundle.ts
var infodoc = document.getElementById("project-info-text");
if (infodoc) {
infodoc.innerHTML = markdown(infodoc.innerText);
}
var MAX_HISTORY_ITEMS = 15;
var requestHistory = JSON.parse(localStorage.getItem("jetpathApiHistory")) || [];
var apiDocumentationRawTemplate = `{ JETPATH }`;
var apiGlobalHeadersRaw = `{ JETPATHGH }`;
var currentYear = new Date().getFullYear();
if (document.querySelector("footer small")) {
document.querySelector("footer small").textContent = document.querySelector("footer small").textContent.replace("{CURRENT_YEAR}", String(currentYear));
}
var environments = {
"Default (Current Host)": typeof window !== "undefined" ? window.location.origin : ""
};
var currentBaseUrl = environments["Default (Current Host)"];
function Rhoda(l) {
const fg = new DocumentFragment;
for (let ch of l) {
if (Array.isArray(ch))
fg.appendChild(Rhoda(ch));
else {
if (typeof ch === "function") {
ch = ch();
if (typeof ch === "function")
ch = ch();
}
if (ch instanceof HTMLElement || ch instanceof DocumentFragment) {
fg.appendChild(ch);
continue;
}
if (typeof ch === "string")
fg.appendChild(document.createTextNode(ch));
}
}
return fg;
}
var makeElement = (element, ElementChildrenAndPropertyList) => {
const props = {};
let text = undefined;
if (ElementChildrenAndPropertyList.length !== 0) {
for (let i = 0;i < ElementChildrenAndPropertyList.length; i++) {
let ch = ElementChildrenAndPropertyList[i];
if (typeof ch === "function") {
ch = ch();
}
if (ch instanceof HTMLElement || ch instanceof DocumentFragment) {
element.appendChild(ch);
continue;
}
if (Array.isArray(ch)) {
element.appendChild(Rhoda(ch));
continue;
}
if (typeof ch === "string") {
text = ch;
continue;
}
if (typeof ch === "object" && ch !== null) {
Object.assign(props, ch);
continue;
}
}
} else
return element;
if (typeof props === "object" && element) {
for (const [prop, value] of Object.entries(props)) {
if (prop === "style" && typeof value === "object") {
Object.assign(element.style, value);
continue;
}
if (prop.startsWith("on") && typeof value === "function") {
element.addEventListener(prop.substring(2).toLowerCase(), value);
continue;
}
if (prop.includes("data-") || prop.includes("aria-")) {
element.setAttribute(prop, value);
continue;
}
element[prop] = value;
}
}
if (text !== undefined)
element.appendChild(document.createTextNode(text));
return element;
};
var cra = (tag) => (...Children_and_Properties) => makeElement(document.createElement(tag), Children_and_Properties);
function $if(condition, ...elements) {
if (condition)
return Rhoda(elements.flat());
return document.createDocumentFragment();
}
var button = cra("button");
var div = cra("div");
var h2 = cra("h2");
var h3 = cra("h3");
var h4 = cra("h4");
var h5 = cra("h5");
var input = cra("input");
var span = cra("span");
var strong = cra("strong");
var pre = cra("pre");
var option = cra("option");
var selectEl = cra("select");
var ul = cra("ul");
var li = cra("li");
var table = cra("table");
var tbody = cra("tbody");
var thead = cra("thead");
var tr = cra("tr");
var th = cra("th");
var td = cra("td");
var a = cra("a");
var label = cra("label");
var small = cra("small");
var p = cra("p");
var loading_svg = () => {
const l = document.createElement("span");
l.innerHTML = '<div class="spinner-border spinner-border-sm text-primary" role="status"><span class="sr-only">Loading...</span></div>';
return l;
};
function syntaxHighlight(json) {
if (typeof json === "string") {
try {
json = JSON.parse(json);
} catch (e) {
return `<pre>${escapeHtml(json)}</pre>`;
}
}
if (typeof prettyPrintJson === "undefined") {
return `<pre>${escapeHtml(JSON.stringify(json, null, 2))}</pre>`;
}
return prettyPrintJson.toHtml(json, {
indent: 2,
lineNumbers: false,
linkUrls: true,
linksNewTab: true,
quoteKeys: true,
trailingCommas: false
});
}
function escapeHtml(unsafe) {
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
}
function showToast(message) {
const toastContainer = document.getElementById("toast-container");
if (!toastContainer)
return;
const toast = div({ className: "toast-message" }, message);
toastContainer.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 3000);
}
function copyToClipboard(text, type) {
navigator.clipboard.writeText(text).then(() => showToast(`${type} copied to clipboard!`)).catch((err) => {
console.error("Failed to copy: ", err);
showToast(`Failed to copy ${type}.`);
});
}
function createEnvironmentSelector() {
const container = document.getElementById("env-switcher-container");
if (!container)
return;
const label2 = h3({ style: { marginRight: "8px", fontWeight: "500" } }, "Environment:");
const selector = selectEl({
id: "env-selector",
className: "form-control form-control-sm d-inline-block",
style: { width: "auto", minWidth: "200px" },
onchange: (e) => {
currentBaseUrl = e.target.value;
renderApiEndpoints();
}
});
Object.entries(environments).forEach(([name, url]) => {
selector.appendChild(option({ value: url }, name));
});
selector.value = currentBaseUrl;
container.appendChild(label2);
container.appendChild(selector);
}
function saveToHistory(method, url, status, payloadSummary, time) {
const timestamp = new Date().toISOString();
requestHistory.unshift({
method,
url,
status,
timestamp,
payloadSummary,
time
});
if (requestHistory.length > MAX_HISTORY_ITEMS)
requestHistory.pop();
localStorage.setItem("jetpathApiHistory", JSON.stringify(requestHistory));
renderHistory();
}
function renderHistory() {
let historyContainer = document.getElementById("api-history-container");
if (!historyContainer)
return;
historyContainer.innerHTML = "";
historyContainer.appendChild(h2({ className: "section-title" }, "Request History"));
if (requestHistory.length === 0) {
historyContainer.appendChild(span("No requests in history yet."));
return;
}
const list = ul({ className: "list-group" });
requestHistory.forEach((item) => {
const listItem = li({
className: "list-group-item list-group-item-action flex-column align-items-start",
style: { cursor: "pointer" },
title: "Click to re-populate (basic)",
onclick: () => {
tryRepopulateFromHistory(item);
}
}, div({ className: "d-flex w-100 justify-content-between" }, h5({ className: "mb-1" }, `${item.method} ${new URL(item.url).pathname}`), small(`${item.time ? item.time + "ms - " : ""}${new Date(item.timestamp).toLocaleTimeString()}`)), p({
className: "mb-1",
style: { fontSize: "0.8rem", wordBreak: "break-all" }
}, item.url), small({ className: `status-${item.status < 400 ? "success" : "error"}` }, `Status: ${item.status}`), item.payloadSummary ? small({
className: "d-block text-muted",
style: {
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis"
}
}, `Payload: ${item.payloadSummary}`) : "");
list.appendChild(listItem);
});
historyContainer.appendChild(list);
}
function tryRepopulateFromHistory(historyItem) {
const endpointCard = Array.from(document.querySelectorAll(".card .btn-link")).find((btn) => {
const cardUrlPath = btn.querySelector(".api-path")?.textContent;
try {
return cardUrlPath && cardUrlPath.trim() === new URL(historyItem.url).pathname;
} catch {
return false;
}
});
if (endpointCard) {
const cardId = endpointCard.closest(".card").id;
const collapseTargetId = endpointCard.getAttribute("data-target");
if (collapseTargetId) {
$(collapseTargetId).collapse("show");
setTimeout(() => {
const urlInput = document.getElementById(`url-${cardId}`);
if (urlInput)
urlInput.value = historyItem.url;
if (historyItem.payloadSummary && historyItem.payloadSummary.startsWith("{")) {
try {
const payloadObj = JSON.parse(historyItem.payloadSummary);
const payloadTabContent = document.getElementById(`request-tab-content-${cardId}`);
if (payloadTabContent) {
const bodyPack = payloadTabContent.querySelector(".body-pack");
if (bodyPack) {
Object.entries(payloadObj).forEach(([key, value]) => {
const inputEl = bodyPack.querySelector(`input[placeholder="Enter ${key}"]`);
if (inputEl) {
inputEl.value = typeof value === "object" ? JSON.stringify(value) : value;
}
});
}
}
} catch (e) {
console.warn("Could not parse payload from history for re-population", e);
}
}
endpointCard.scrollIntoView({ behavior: "smooth", block: "center" });
showToast("Form populated from history.");
}, 300);
}
} else {
showToast("Could not find matching API card to re-populate.");
}
}
function parsePayload(packer) {
if (!packer)
return;
const result = {};
let isSet = false;
const divs = Array.from(packer.children);
divs.forEach((div2) => {
const inputEl = div2.querySelector("input");
if (!inputEl)
return;
isSet = true;
const key = div2.querySelector("span")?.textContent?.replace(":", "");
if (!key)
return;
if (inputEl.type === "file") {
result[key] = inputEl.files[0];
return;
}
result[key.trim()] = inputEl.value;
});
if (!isSet)
return;
return result;
}
function getInputValue(inputEl) {
if (!inputEl)
return;
if (inputEl.type === "file") {
return inputEl.files && inputEl.files.length > 0 ? inputEl.files[0] : undefined;
}
if (inputEl.type === "number") {
return inputEl.value === "" ? undefined : Number(inputEl.value);
}
if (inputEl.type === "checkbox")
return inputEl.checked;
const val = inputEl.value;
if (val === "true")
return true;
if (val === "false")
return false;
return val;
}
function parsePayloadData(bodyPackElement) {
if (!bodyPackElement)
return {};
function processNode(node) {
if (node.matches(".form-group.data-input")) {
const inputEl = node.querySelector('input:not([type="button"]), select, textarea');
return getInputValue(inputEl);
} else if (node.dataset?.["obj"] === "true" && node.matches(".data-input")) {
const objectData = {};
Array.from(node.children).forEach((childNode) => {
if (childNode.matches(".form-group.data-input, .data-input")) {
const key = childNode.querySelector("span[data-key-name]")?.dataset?.["keyName"] || childNode.dataset?.["holder"];
if (key) {
objectData[key] = processNode(childNode);
}
}
});
return objectData;
} else if (node.dataset?.["arr"] === "true" && node.matches(".data-input")) {
const arrayData = [];
const arrayItemsContainer = node.querySelector(".array-cont");
if (arrayItemsContainer) {
Array.from(arrayItemsContainer.children).filter((item) => item.matches(".array-item")).forEach((itemElement) => {
const itemObject = {};
Array.from(itemElement.children).forEach((fieldNode) => {
if (fieldNode.matches(".form-group.data-input, .data-input")) {
const key = fieldNode.querySelector("span[data-key-name]")?.dataset?.["keyName"] || fieldNode.dataset?.["holder"];
if (key) {
itemObject[key] = processNode(fieldNode);
}
}
});
arrayData.push(itemObject);
});
} else {
const simpleArrayInput = node.querySelector('input[type="text"][placeholder*="comma-separated"]');
if (simpleArrayInput) {
const val = getInputValue(simpleArrayInput);
if (val && typeof val === "string" && val.trim() !== "") {
arrayData.push(...val.split(",").map((s) => s.trim()).filter((s) => s));
} else if (val !== undefined && val !== "" && val !== null) {
arrayData.push(val);
}
}
}
return arrayData;
}
return;
}
const assembledPayload = {};
Array.from(bodyPackElement.children).forEach((topLevelNode) => {
if (topLevelNode.matches(".form-group.data-input, .data-input")) {
const key = topLevelNode.querySelector("span[data-key-name]")?.dataset?.["keyName"] || topLevelNode.dataset?.["holder"];
if (key) {
assembledPayload[key] = processNode(topLevelNode);
}
}
});
return assembledPayload;
}
function parsePayloadStructure(apiSchema, cardId, isPayload = true) {
if (!apiSchema)
return;
const packer = div({ className: "body-pack" });
function createInputField(key, valueSchema, _type, _holder, currentPath = "") {
const fieldId = `${cardId}-field-${(currentPath + key).replace(/[^a-zA-Z0-9]/g, "_")}`;
if (typeof valueSchema === "object" && !Array.isArray(valueSchema) && valueSchema !== null) {
const nestedDiv = div({
className: "data-input",
"data-obj": "true",
"data-holder": key,
style: {
marginLeft: "15px",
borderLeft: "2px solid var(--surface-3)",
paddingLeft: "10px",
marginBottom: "10px"
}
}, h5({ style: { fontSize: "0.9em", color: "var(--text-secondary)" } }, key + ": {object}"));
Object.entries(valueSchema ?? {}).forEach(([nestedKey, nestedValue]) => {
nestedDiv.appendChild(createInputField(nestedKey, nestedValue, "obj", key, currentPath + key + "."));
});
return nestedDiv;
} else if (Array.isArray(valueSchema)) {
const arrayDiv = div({
className: "data-input",
"data-arr": "true",
"data-holder": key,
style: {
marginLeft: "15px",
borderLeft: "2px solid var(--surface-3)",
paddingLeft: "10px",
marginBottom: "10px"
}
}, h5({ style: { fontSize: "0.9em", color: "var(--text-secondary)" } }, key + ": [array]"));
if (valueSchema.length > 0 && typeof valueSchema[0] === "object" && valueSchema[0] !== null) {
const arrayContainer = div({ className: "array-cont" });
arrayDiv.appendChild(arrayContainer);
let pos = 0;
const addItem = () => {
const itemDiv = div({
className: "array-item",
"data-pos": pos,
style: {
border: "1px dashed var(--border-color)",
padding: "10px",
marginBottom: "5px"
}
});
Object.entries(valueSchema).forEach(([arrayKey, arrayValue]) => {
itemDiv.appendChild(createInputField(arrayKey, arrayValue, "arr-obj", key, `${currentPath}${key}[${pos}].`));
});
arrayContainer.appendChild(itemDiv);
pos += 1;
};
addItem();
arrayDiv.appendChild(button("+ Add Item", {
onclick: addItem,
className: "btn btn-sm btn-outline-secondary",
style: { fontSize: "0.8rem", marginTop: "5px" }
}));
} else {
arrayDiv.appendChild(span({ "data-key-name": key, style: { marginRight: "5px" } }, key + ": "));
arrayDiv.appendChild(input({
type: valueSchema[0].split(":")[0],
id: fieldId,
placeholder: "Enter " + key + " (comma-separated values)",
value: valueSchema[0].split(":")[1],
className: "form-control form-control-sm"
}));
}
return arrayDiv;
} else {
let [type, value] = typeof valueSchema === "string" ? valueSchema.split(":") : [valueSchema, null];
if (!isPayload)
value = valueSchema;
return div({
className: "form-group row data-input",
style: { marginBottom: "0.5rem" }
}, span({
htmlFor: fieldId,
className: "col-sm-4 col-form-label col-form-label-sm",
"data-key-name": key,
style: { fontWeight: "normal" }
}, key + ": "), div({ className: "col-sm-8" }, input({
type: /(file|number|string|date|datetime|time|email|url|tel|password|checkbox|radio|select|textarea|hidden|button|submit|reset|image|color|month|week|range)/.test(type) ? type : "text",
id: fieldId,
value: type !== "file" && value !== null ? value : null,
placeholder: "Enter " + key,
className: "form-control form-control-sm"
})));
}
}
if (apiSchema && typeof apiSchema === "object") {
Object.entries(apiSchema).forEach(([key, value]) => {
packer.appendChild(createInputField(key, value, "root", ""));
});
} else if (apiSchema) {
packer.appendChild(span("Payload schema is not a valid object. Raw: " + escapeHtml(String(apiSchema))));
} else {
packer.appendChild(span("No payload schema defined for this request."));
}
return packer;
}
function parseApiDocumentation(apiDocString) {
const requests = apiDocString.split("### break ###").map((request) => request.trim()).filter((a2) => a2 !== "");
return requests.map(parseRequest);
}
function parseRequest(requestString) {
const lines = requestString.split("\\n").map((line) => line.trim());
const requestLine = lines[0].split(" ");
const method = requestLine[0];
const url = requestLine[1];
const httpVersion = requestLine[2];
const headers = {};
for (let i = 1;i < lines.length; i++) {
if (lines[i] === "")
break;
const [key, value] = lines[i].split(":").map((part) => part.trim());
headers[key.toLowerCase()] = value;
}
const payloadIndex = lines.indexOf("") + 1;
let payload = payloadIndex !== 0 ? lines.slice(payloadIndex).join("\\n") : null;
let title = "";
let description = "";
if (payload?.includes("#")) {
if (payload.includes("#-JET-TITLE")) {
title = payload.slice(payload.indexOf("#-JET-TITLE") + 12, payload.lastIndexOf("#-JET-TITLE"));
}
if (payload.includes("#-JET-DESCRIPTION")) {
description = payload.slice(payload.indexOf("#-JET-DESCRIPTION") + 18, payload.lastIndexOf("#-JET-DESCRIPTION"));
}
payload = payload.split(`
`).filter((line) => !line.startsWith("#")).join(`
`);
}
return { method, url, httpVersion, headers, payload, title, description };
}
var groupByFirstFeature = (apis) => {
const results = {};
const out = [];
for (let a2 = 0;a2 < apis.length; a2++) {
const api = apis[a2];
try {
const top_path = new URL(api?.url).pathname.split("/")[1] || "/";
if (results[top_path])
results[top_path].push(api);
else
results[top_path] = [api];
results[top_path].sort((x, y) => x.url.localeCompare(y.url));
} catch (e) {
console.warn("Skipping API with invalid URL:", api);
continue;
}
}
for (const apilist in results) {
out.push({ title: apilist, isGroup: true });
out.push(...results[apilist]);
}
return out;
};
function showApiResponse(response, cardId, expectedStatusCode, expectedBodyContains, startTime) {
const endTime = performance.now();
const requestTime = (endTime - startTime).toFixed(0);
const responseSize = response.body ? new TextEncoder().encode(response.body).length : 0;
const responseBodyTab = document.getElementById(`response-body-tab-${cardId}`);
const responseHeadersTab = document.getElementById(`response-headers-tab-${cardId}`);
const testResultsTab = document.getElementById(`test-results-tab-${cardId}`);
const responseMetaInfo = document.getElementById(`response-meta-${cardId}`);
if (!responseBodyTab || !responseHeadersTab || !testResultsTab || !responseMetaInfo)
return;
responseMetaInfo.innerHTML = "";
responseMetaInfo.appendChild(span({
className: `status-${response.status < 400 && !response.error ? "success" : "error"}`
}, `Status: ${response.status || (response.error ? "Client Error" : "N/A")}`));
responseMetaInfo.appendChild(span(`Time: ${requestTime} ms`));
responseMetaInfo.appendChild(span(`Size: ${(responseSize / 1024).toFixed(2)} KB`));
responseBodyTab.innerHTML = "";
if (response.body) {
responseBodyTab.appendChild(button({
className: "btn btn-sm btn-outline-secondary float-right mb-2",
onclick: () => copyToClipboard(response.body, "Response Body")
}, "Copy Body"));
responseBodyTab.appendChild(div({
className: "code-container",
style: { overflowX: "auto" },
innerHTML: syntaxHighlight(response.body)
}));
} else {
responseBodyTab.appendChild(span(response.error || "No response body."));
}
responseHeadersTab.innerHTML = "";
if (response.headers && typeof response.headers.forEach === "function") {
const headersTable = table({ className: "table table-sm headers-table" });
const tHead = thead(tr(th("Header Name"), th("Header Value")));
const tBody = tbody();
response.headers.forEach((value, name) => {
tBody.appendChild(tr(td(name), td(value)));
});
headersTable.appendChild(tHead);
headersTable.appendChild(tBody);
responseHeadersTab.appendChild(headersTable);
} else {
responseHeadersTab.appendChild(span("No headers in response."));
}
testResultsTab.innerHTML = "";
let allAssertionsPassed = true;
if (expectedStatusCode) {
const statusCode = response.status || (response.error ? 0 : 404);
const statusPass = Number(expectedStatusCode) === statusCode;
if (!statusPass)
allAssertionsPassed = false;
testResultsTab.appendChild(div({
className: `assertion-result ${statusPass ? "assertion-pass" : "assertion-fail"}`
}, `Status Code: Expected ${expectedStatusCode}, Got ${statusCode}. (${statusPass ? "PASS" : "FAIL"})`));
}
if (expectedBodyContains && response.body) {
const bodyPass = response.body.includes(expectedBodyContains);
if (!bodyPass)
allAssertionsPassed = false;
testResultsTab.appendChild(div({
className: `assertion-result ${bodyPass ? "assertion-pass" : "assertion-fail"}`
}, `Body Contains "${expectedBodyContains}": ${bodyPass ? "Found (PASS)" : "Not Found (FAIL)"}`));
}
if (!expectedStatusCode && !expectedBodyContains) {
testResultsTab.appendChild(span("No assertions defined for this request."));
}
document.getElementById(`response-container-tabs-${cardId}`).style.display = "block";
const tabToActivate = expectedStatusCode || expectedBodyContains ? `test-results-nav-${cardId}` : `response-body-nav-${cardId}`;
$(`#${tabToActivate}`).tab("show");
}
function createApiCard(request, i) {
const payloadSchema = JSON.parse(request.payload?.includes("{") ? request.payload : "null");
const cardIdSuffix = request.method.toLowerCase() + request.url.replace(/[^a-zA-Z0-9]/g, "") + i;
const cardId = `card-${cardIdSuffix}`;
const collapseId = `collapse-${cardIdSuffix}`;
const requestTabs = ul({ className: "nav nav-tabs", role: "tablist" }, li({ className: "nav-item" }, a({
className: "nav-link active",
id: `request-nav-${cardId}`,
"data-toggle": "tab",
href: `#request-tab-content-${cardId}`,
role: "tab"
}, "Request")), li({ className: "nav-item" }, a({
className: "nav-link",
id: `auth-nav-${cardId}`,
"data-toggle": "tab",
href: `#auth-tab-content-${cardId}`,
role: "tab"
}, "Global Auth")), li({ className: "nav-item" }, a({
className: "nav-link",
id: `assertions-nav-${cardId}`,
"data-toggle": "tab",
href: `#assertions-tab-content-${cardId}`,
role: "tab"
}, "Assertions")), li({ className: "nav-item" }, a({
className: "nav-link",
id: `curl-nav-${cardId}`,
"data-toggle": "tab",
href: `#curl-tab-content-${cardId}`,
role: "tab"
}, "cURL")));
const requestTabsContent = div({ className: "tab-content" }, div({
className: "tab-pane fade show active",
id: `request-tab-content-${cardId}`,
role: "tabpanel"
}, div({ className: "form-group mt-3" }, label({ htmlFor: `url-${cardId}` }, "Request URL"), input({
className: "form-control url-input",
id: `url-${cardId}`,
value: request.url
})), div({ className: "form-group" }, label({ htmlFor: `content-type-dropdown-${cardId}` }, "Content-Type"), selectEl({
id: `content-type-dropdown-${cardId}`,
className: "form-control form-control-sm"
}, option({ value: "application/json" }, "JSON"), option({ value: "multipart/form-data" }, "Form Data"), option({ value: "application/x-www-form-urlencoded" }, "Form URL Encoded"))), $if(payloadSchema, () => div({ className: "payload-section" }, strong("Payload:"), parsePayloadStructure(payloadSchema, cardId)))), div({
className: "tab-pane fade",
id: `auth-tab-content-${cardId}`,
role: "tabpanel"
}, div({
className: "global-auth-section mt-3",
id: `global-auth-preview-${cardId}`
}, strong("Global Authentication Headers (Read-only):"))), div({
className: "tab-pane fade",
id: `assertions-tab-content-${cardId}`,
role: "tabpanel"
}, div({ className: "form-group mt-3" }, label({ htmlFor: `expected-status-${cardId}` }, "Expected Status Code"), input({
type: "number",
id: `expected-status-${cardId}`,
placeholder: "e.g., 200",
className: "form-control form-control-sm"
})), div({ className: "form-group" }, label({ htmlFor: `expected-body-${cardId}` }, "Response Body Contains (Text)"), input({
type: "text",
id: `expected-body-${cardId}`,
placeholder: 'e.g., "success": true',
className: "form-control form-control-sm"
}))), div({
className: "tab-pane fade",
id: `curl-tab-content-${cardId}`,
role: "tabpanel"
}, div({ className: "mt-3 curl-output", id: `curl-output-${cardId}` }, pre('Click "Generate cURL" after configuring request.'))));
const responseSectionTabs = ul({ className: "nav nav-tabs mt-3", role: "tablist" }, li({ className: "nav-item" }, a({
className: "nav-link active",
id: `response-body-nav-${cardId}`,
"data-toggle": "tab",
href: `#response-body-tab-${cardId}`,
role: "tab"
}, "Body")), li({ className: "nav-item" }, a({
className: "nav-link",
id: `response-headers-nav-${cardId}`,
"data-toggle": "tab",
href: `#response-headers-tab-${cardId}`,
role: "tab"
}, "Headers")), li({ className: "nav-item" }, a({
className: "nav-link",
id: `test-results-nav-${cardId}`,
"data-toggle": "tab",
href: `#test-results-tab-${cardId}`,
role: "tab"
}, "Test Results")));
const responseSectionTabsContent = div({ className: "tab-content" }, div({
className: "tab-pane fade show active",
id: `response-body-tab-${cardId}`,
role: "tabpanel",
style: { padding: "10px", flexDirection: "column" }
}), div({
className: "tab-pane fade",
id: `response-headers-tab-${cardId}`,
role: "tabpanel",
style: { padding: "10px" }
}), div({
className: "tab-pane fade",
id: `test-results-tab-${cardId}`,
role: "tabpanel",
style: { padding: "10px" }
}));
const clearForm = () => {
const requestTab = document.getElementById(`request-tab-content-${cardId}`);
if (requestTab) {
requestTab.querySelectorAll('input[type="text"], input[type="number"], input[type="file"], textarea').forEach((inp) => inp.value = "");
requestTab.querySelectorAll('input[type="checkbox"], input[type="radio"]').forEach((inp) => inp.checked = false);
}
document.getElementById(`expected-status-${cardId}`).value = "";
document.getElementById(`expected-body-${cardId}`).value = "";
document.getElementById(`response-meta-${cardId}`).innerHTML = "";
document.getElementById(`response-body-tab-${cardId}`).innerHTML = "";
document.getElementById(`response-headers-tab-${cardId}`).innerHTML = "";
document.getElementById(`test-results-tab-${cardId}`).innerHTML = "";
document.getElementById(`curl-output-${cardId}`).querySelector("pre").textContent = 'Click "Generate cURL" after configuring request.';
showToast("Form cleared.");
};
const generateCurlAction = () => {
const method = request.method.toUpperCase();
const url = document.getElementById(`url-${cardId}`)?.value?.trim();
const globalHeaders = parsePayload(document.getElementById("keys")) || {};
const specificHeaders = request.headers;
const allHeaders = { ...specificHeaders, ...globalHeaders };
let curlCommand = `curl --location --request ${method} '${url}' \\
`;
for (const key in allHeaders) {
if (Object.hasOwnProperty.call(allHeaders, key)) {
curlCommand += `--header '${key}: ${allHeaders[key]}' \\
`;
}
}
const payloadData = parsePayloadData(document.getElementById(`request-tab-content-${cardId}`).querySelector(".body-pack"));
const contentType = document.getElementById(`content-type-dropdown-${cardId}`)?.value || "application/json";
if (payloadData && Object.keys(payloadData).length > 0) {
curlCommand += `--header 'Content-Type: ${contentType}' \\
`;
if (contentType === "application/json") {
curlCommand += `--data-raw '${JSON.stringify(payloadData)}'`;
} else if (contentType === "multipart/form-data") {
for (const key in payloadData) {
curlCommand += `--form '${key}=${payloadData[key] instanceof File ? payloadData[key].name : JSON.stringify(payloadData[key])}' \\
`;
}
curlCommand = curlCommand.slice(0, -4);
} else if (contentType === "application/x-www-form-urlencoded") {
curlCommand += `--data-raw '${new URLSearchParams(payloadData).toString()}'`;
}
} else if (curlCommand.endsWith(" \\\n"))
curlCommand = curlCommand;
const curlOutputPre = document.getElementById(`curl-output-${cardId}`).querySelector("pre");
curlOutputPre.textContent = curlCommand;
let copyBtn = document.getElementById(`copy-curl-${cardId}`);
if (!copyBtn && curlOutputPre.parentNode) {
copyBtn = button({
id: `copy-curl-${cardId}`,
className: "btn btn-sm btn-outline-secondary mt-2",
onclick: () => copyToClipboard(curlCommand, "cURL command")
}, "Copy cURL");
curlOutputPre.parentNode.appendChild(copyBtn);
}
$(`#curl-nav-${cardId}`).tab("show");
};
const sendRequestAction = async () => {
const startTime = performance.now();
const currentUrl = document.getElementById(`url-${cardId}`)?.value?.trim();
const globalAuthHeaders = parsePayload(document.getElementById("keys")) || {};
const requestSpecificHeaders = request.headers;
const combinedHeaders = { ...requestSpecificHeaders, ...globalAuthHeaders };
const payloadData = parsePayloadData(document.getElementById(`request-tab-content-${cardId}`).querySelector(".body-pack"));
const expectedStatusCode = document.getElementById(`expected-status-${cardId}`)?.value;
const expectedBodyContent = document.getElementById(`expected-body-${cardId}`)?.value;
const contentType = document.getElementById(`content-type-dropdown-${cardId}`)?.value || "application/json";
const responseContainerTabs = document.getElementById(`response-container-tabs-${cardId}`);
responseContainerTabs.style.display = "block";
document.getElementById(`response-meta-${cardId}`).innerHTML = "";
document.getElementById(`response-body-tab-${cardId}`).innerHTML = "";
document.getElementById(`response-headers-tab-${cardId}`).innerHTML = "";
document.getElementById(`test-results-tab-${cardId}`).innerHTML = "";
document.getElementById(`response-body-tab-${cardId}`).appendChild(loading_svg());
const apiResponse = await testApi(request.method, currentUrl, combinedHeaders, payloadData, contentType);
showApiResponse(apiResponse, cardId, Number(expectedStatusCode), expectedBodyContent, startTime);
if (!apiResponse.error) {
let payloadSummary = "";
if (payloadData && Object.keys(payloadData).length > 0) {
payloadSummary = JSON.stringify(payloadData).substring(0, 70) + (JSON.stringify(payloadData).length > 70 ? "..." : "");
}
saveToHistory(request.method, currentUrl, apiResponse.status, payloadSummary, (performance.now() - startTime).toFixed(0));
}
};
return div({ className: "card", id: cardId }, div({ className: "card-header", id: `header-${cardId}` }, h5({ className: "mb-0" }, button({
className: "btn btn-link collapsed",
type: "button",
"data-toggle": "collapse",
"data-target": `#${collapseId}`,
"aria-expanded": "false",
"aria-controls": collapseId,
onclick: () => {
const authPreview = document.getElementById(`global-auth-preview-${cardId}`);
if (authPreview && authPreview.children.length <= 1) {
if (Object.keys(request.headers).length > 0) {
Object.entries(request.headers).forEach(([key, value]) => {
authPreview.appendChild(div({ style: { fontSize: "0.85rem" } }, strong(key + ": "), span(value)));
});
} else {
authPreview.appendChild(span({ style: { fontSize: "0.85rem" } }, "No global authentication headers configured or found."));
}
}
}
}, span(request.method, { className: "method " + request.method }), span({ className: "api-path" }, request.url ? new URL(request.url).pathname : "Invalid URL"), span({
className: "text-muted small ml-2",
style: { fontWeight: "normal" }
}, request.title || "")))), div({
id: collapseId,
className: "collapse",
"data-parent": "#api-endpoint-container"
}, div({ className: "card-body" }, requestTabs, $if(request.description || request.title, div({
className: "tab-content",
innerHTML: markdown((request.description || "#" + request.title)?.slice(1)?.replaceAll(`
#`, `
`))
})), requestTabsContent, div({ className: "action-buttons mt-3 mb-3" }, button("Send Request", {
className: "btn btn-primary",
onclick: sendRequestAction
}), button("Generate cURL", {
className: "btn btn-secondary",
onclick: generateCurlAction
}), button("Clear Form", {
className: "btn btn-outline-danger btn-sm",
onclick: clearForm
})), div({ id: `response-meta-${cardId}`, className: "response-meta" }), div({
id: `response-container-tabs-${cardId}`,
style: { display: "none" }
}, responseSectionTabs, responseSectionTabsContent))));
}
function renderApiEndpoints() {
const apiEndpointContainer = document.getElementById("api-endpoint-container");
const searchInput = document.getElementById("api-search-input");
if (!apiEndpointContainer || !searchInput)
return;
const searchTerm = searchInput.value.toLowerCase();
apiEndpointContainer.innerHTML = "";
const apiDocumentation = apiDocumentationRawTemplate.replaceAll("[--host--]", currentBaseUrl);
const parsedApis = parseApiDocumentation(apiDocumentation);
let visibleApis = parsedApis;
if (searchTerm) {
visibleApis = parsedApis.filter((api) => {
const fullText = `${api.method} ${api.url} ${api.title || api.description || ""}`.toLowerCase();
return fullText.includes(searchTerm);
});
}
const groupedApis = groupByFirstFeature(visibleApis);
if (groupedApis.length === 0 && searchTerm) {
apiEndpointContainer.appendChild(div({ className: "alert alert-warning" }, "No endpoints match your search."));
return;
} else if (groupedApis.length === 0) {
apiEndpointContainer.appendChild(div({ className: "alert alert-info" }, "No API endpoints defined or an error occurred parsing them."));
return;
}
groupedApis.forEach((item, i) => {
if (item.isGroup) {
const groupTitle = h4({
className: "mt-3 mb-2 text-muted px-2",
style: {
fontSize: "1rem",
borderBottom: "1px solid var(--surface-3)",
paddingBottom: "5px"
}
}, item.title.toUpperCase());
apiEndpointContainer.appendChild(groupTitle);
} else {
apiEndpointContainer.appendChild(createApiCard(item, i));
}
});
}
async function testApi(method, url, headers = {}, body, contentType = "application/json") {
if (contentType !== "multipart/form-data" && contentType !== "application/octet-stream")
headers["Content-Type"] = contentType;
else if (contentType === "multipart/form-data") {
delete headers["Content-Type"];
}
let response;
try {
const fetchOptions = {
method,
headers,
signal: AbortSignal.timeout(30000),
body
};
if (method !== "GET" && method !== "HEAD" && body !== undefined) {
if (contentType === "application/json") {
fetchOptions.body = JSON.stringify(body);
} else if (contentType === "multipart/form-data") {
const formData = new FormData;
if (body) {
for (const key in body)
formData.append(key, body[key]);
}
fetchOptions.body = formData;
} else if (contentType === "application/x-www-form-urlencoded") {
fetchOptions.body = new URLSearchParams(body).toString();
} else
fetchOptions.body = body;
} else {
delete fetchOptions.body;
}
response = await fetch(url, fetchOptions);
const responseBody = await response.text();
const responseHeaders = {};
for (const [key, value] of response.headers.entries()) {
responseHeaders[key] = value;
}
return {
status: response.status,
headers: response.headers,
body: responseBody
};
} catch (error) {
console.error("API Test Error:", error);
return {
error: error.message,
status: 0,
body: error.message,
headers: new Headers
};
}
}
document.addEventListener("DOMContentLoaded", () => {
createEnvironmentSelector();
try {
document.getElementById("keys")?.appendChild(parsePayloadStructure(JSON.parse(apiGlobalHeadersRaw), "global-headers", false));
} catch (e) {
console.error("Failed to parse global headers JSON:", apiGlobalHeadersRaw, e);
}
renderApiEndpoints();
renderHistory();
const searchInput = document.getElementById("api-search-input");
if (searchInput) {
searchInput.addEventListener("keyup", () => {
clearTimeout(searchInput.searchTimeout);
searchInput.searchTimeout = setTimeout(renderApiEndpoints, 300);
});
}
});
</script>
</body>
</html>