Skip to content
This repository was archived by the owner on Sep 11, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/release-runtime.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ jobs:
with:
fetch-depth: 0
ref: "${{ github.ref_name }}"
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: ">=22"
- name: Install API Explorer dependencies
working-directory: runtime/explorer/content
run: npm ci
- name: Build API Explorer
working-directory: runtime/explorer/content
run: npm run build
- uses: actions/setup-go@v5
with:
go-version: 1.23
Expand Down
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
## UNRELEASED - Runtime

- feat: Reduce logger output during development [#576](https://github.com/hypermodeinc/modus/pull/576)
- chore: Trigger internal release pipeline at the end of the release-runtime workflow [#577](https://github.com/hypermodeinc/modus/pull/577)
- chore: Trigger internal release pipeline at the end of the release-runtime workflow [#577](https://github.com/hypermodeinc/modus/pull/577)
- feat: Add API explorer to runtime [#578](https://github.com/hypermodeinc/modus/pull/578)
- feat: Add API explorer component to runtime [#584](https://github.com/hypermodeinc/modus/pull/584)

## UNRELEASED - AssemblyScript SDK 0.13.5

Expand Down Expand Up @@ -32,7 +33,7 @@

- Automatically generate and push releases info to R2 bucket on every release [#526](https://github.com/hypermodeinc/modus/pull/526)
- Consistent help + print enum options + validate SDK prereq [#542](https://github.com/hypermodeinc/modus/pull/542)
- Consistent padding in the help section
- Consistent padding in the help section
- `modus new`: Enum options need to print possible options
- Validate SDK prereq immediately after choosing SDK
- `modus sdk remove`: Use select prompt to allow selection
Expand Down
3 changes: 3 additions & 0 deletions runtime/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
modus_runtime
runtime

node_modules
dist
8 changes: 6 additions & 2 deletions runtime/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ tidy:
go mod tidy -v
go fmt ./...

.PHONY: build-explorer
build-explorer:
cd explorer/content && npm install && npm run build

.PHONY: build
build:
build: build-explorer
go build -o $(EXECUTABLE) -ldflags "-s -w -X github.com/hypermodeinc/modus/runtime/config.version=$(VERSION)" .

.PHONY: run
run:
run: build-explorer
@ARGS="$(filter-out $@,$(MAKECMDGOALS))" && \
MODUS_ENV=dev go run . $$ARGS

Expand Down
61 changes: 2 additions & 59 deletions runtime/explorer/content/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,65 +5,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Modus API Explorer</title>
</head>
<body>
<h1>Modus API Explorer</h1>
<p>
This is the Modus API Explorer. You can use this tool to explore the API
and test the endpoints.
</p>
<p>Note, this works, but will soon be replaced by something much nicer!</p>

<h2>Available Endpoints</h2>
<select id="endpoint"></select>

<h2>GraphQL Query</h2>
<textarea
id="query"
rows="10"
cols="50"
placeholder="Enter your GraphQL query here..."
></textarea>

<button id="submit">Submit Query</button>

<h2>Response</h2>
<textarea id="response" rows="10" cols="50" readonly></textarea>

<script>
async function populateEndpoints() {
const response = await fetch("/explorer/api/endpoints");
const endpoints = await response.json();
const endpointSelect = document.getElementById("endpoint");
endpoints.forEach((ep) => {
const option = document.createElement("option");
option.value = ep.path;
option.text = `${ep.name}: ${ep.path}`;
endpointSelect.appendChild(option);
});
}

populateEndpoints();

document.getElementById("submit").addEventListener("click", async () => {
const endpoint = document.getElementById("endpoint").value;
const query = document.getElementById("query").value;
const responseTextarea = document.getElementById("response");

try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ query }),
});

const result = await response.json();
responseTextarea.value = JSON.stringify(result, null, 2);
} catch (error) {
responseTextarea.value = `Error: ${error.message}`;
}
});
</script>
<body id="root">
<script type="module" src="./main.tsx"></script>
</body>
</html>
64 changes: 64 additions & 0 deletions runtime/explorer/content/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import { ApiExplorer } from "@hypermode/react-api-explorer";
import "@hypermode/react-api-explorer/styles.css";

const rootElement = document.getElementById("root");
if (!rootElement) {
throw new Error("Root element not found");
}
const root = createRoot(rootElement);

function App() {
const sampleTheme = {
background: "224 71.4% 4.1%",
foreground: "210 20% 98%",
card: "224 71.4% 4.1%",
"card-foreground": "210 20% 98%",
popover: "224 71.4% 4.1%",
"popover-foreground": "210 20% 98%",
primary: "263.4 70% 50.4%",
"primary-foreground": "210 20% 98%",
secondary: "215 27.9% 16.9%",
"secondary-foreground": "210 20% 98%",
muted: "215 27.9% 16.9%",
"muted-foreground": "217.9 10.6% 64.9%",
accent: "215 27.9% 16.9%",
"accent-foreground": "210 20% 98%",
destructive: "0 62.8% 30.6%",
"destructive-foreground": "210 20% 98%",
border: "215 27.9% 16.9%",
input: "215 27.9% 16.9%",
ring: "263.4 70% 50.4%",
};
const [endpoints, setEndpoints] = useState<string[]>([
"http://localhost:8686/graphql",
]);

useEffect(() => {
// Fetch endpoints when component mounts
const fetchEndpoints = async () => {
try {
const response = await fetch("/explorer/api/endpoints");
const data = await response.json();

const origin = window.location.origin;
const ep = data.map((endpoint: { path: string }) => {
return endpoint.path.startsWith("/")
? `${origin}${endpoint.path}`
: endpoint.path;
});

setEndpoints(ep);
} catch (error) {
console.error("Failed to fetch endpoints:", error);
}
};

fetchEndpoints();
}, []);

return <ApiExplorer endpoints={endpoints} theme={sampleTheme} />;
}

root.render(<App />);
Loading
Loading