feat: scaffold cloudflare shortlinks app

This commit is contained in:
Hermes Agent
2026-06-20 09:57:13 +10:00
commit 32913619f4
14 changed files with 3051 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
node_modules/
dist/
.wrangler/
.dev.vars
.env
.env.*
!.env.example
.DS_Store
npm-debug.log*
+31
View File
@@ -0,0 +1,31 @@
# Heygo
Heygo is an independent Cloudflare React + Workers scaffold for a future shortlinks app.
This repository lives at `/home/ai-bot/code/heygo` and is intentionally separate from the old Linux/Django Links repository. The old Links repository under `/home/ai-bot/code/links` is read-only reference material only and must not be modified by this project.
## Local development
```bash
npm install
npm run dev
npm run dev:worker
```
- `npm run dev` starts the Vite React SPA dev server.
- `npm run dev:worker` starts the Cloudflare Worker locally with Wrangler.
## Verification
```bash
npm run build
npm test
```
- `npm run build` type-checks the TypeScript project and builds the React SPA into `dist/client` for Workers Assets.
- `npm test` runs Vitest tests against the Worker API handler.
## Current API skeleton
- `GET /api/health` returns JSON health status from the Worker.
- Unknown `/api/*` routes return JSON `404` responses.
+12
View File
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Heygo</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
+2793
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "heygo",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"dev:worker": "wrangler dev",
"build": "tsc --noEmit && vite build",
"preview": "vite preview",
"test": "vitest run"
},
"dependencies": {
"react": "^19.2.7",
"react-dom": "^19.2.7"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20260619.1",
"@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.2",
"typescript": "^6.0.3",
"vite": "^8.0.16",
"vitest": "^4.1.9",
"wrangler": "^4.103.0"
}
}
+15
View File
@@ -0,0 +1,15 @@
export default function App() {
return (
<main className="app-shell">
<section className="hero">
<p className="eyebrow">Cloudflare Workers + React</p>
<h1>Heygo shortlinks</h1>
<p>
A fresh TypeScript scaffold for a React SPA backed by a Cloudflare
Worker API.
</p>
<a href="/api/health">Check the Worker API health endpoint</a>
</section>
</main>
);
}
+10
View File
@@ -0,0 +1,10 @@
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import './styles.css';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);
+47
View File
@@ -0,0 +1,47 @@
:root {
color: #172033;
background: #f6f8fb;
font-family:
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
}
body {
margin: 0;
}
.app-shell {
align-items: center;
display: flex;
min-height: 100vh;
justify-content: center;
padding: 2rem;
}
.hero {
background: white;
border: 1px solid #dde4ef;
border-radius: 24px;
box-shadow: 0 24px 80px rgb(23 32 51 / 12%);
max-width: 720px;
padding: 3rem;
}
.eyebrow {
color: #5b6ee1;
font-size: 0.85rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
h1 {
font-size: clamp(2.5rem, 8vw, 5rem);
line-height: 0.95;
margin: 0 0 1rem;
}
a {
color: #4354d8;
font-weight: 700;
}
+1
View File
@@ -0,0 +1 @@
/// <reference types="vite/client" />
+28
View File
@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest';
import worker from '../worker/index';
function fetchWorker(path: string) {
const request = new Request(`https://heygo.test${path}`);
return worker.fetch(request as unknown as Parameters<typeof worker.fetch>[0]);
}
describe('worker API scaffold', () => {
it('responds to the health endpoint', async () => {
const response = await fetchWorker('/api/health');
expect(response.status).toBe(200);
expect(response.headers.get('content-type')).toContain('application/json');
await expect(response.json()).resolves.toEqual({
ok: true,
service: 'heygo-worker',
});
});
it('returns 404 JSON for unknown API routes', async () => {
const response = await fetchWorker('/api/missing');
expect(response.status).toBe(404);
await expect(response.json()).resolves.toEqual({ error: 'Not found' });
});
});
+21
View File
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2022",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ES2022", "WebWorker"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"types": ["@cloudflare/workers-types", "vitest/globals"]
},
"include": ["src", "worker", "tests", "vite.config.ts"]
}
+13
View File
@@ -0,0 +1,13 @@
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
outDir: 'dist/client',
emptyOutDir: true,
},
test: {
environment: 'node',
},
});
+31
View File
@@ -0,0 +1,31 @@
export interface Env {}
const jsonHeaders = {
'content-type': 'application/json; charset=utf-8',
};
function json(body: unknown, init: ResponseInit = {}) {
return Response.json(body, {
...init,
headers: {
...jsonHeaders,
...init.headers,
},
});
}
export default {
async fetch(request): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === '/api/health') {
return json({ ok: true, service: 'heygo-worker' });
}
if (url.pathname.startsWith('/api/')) {
return json({ error: 'Not found' }, { status: 404 });
}
return json({ error: 'Not found' }, { status: 404 });
},
} satisfies ExportedHandler<Env>;
+13
View File
@@ -0,0 +1,13 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "heygo",
"main": "worker/index.ts",
"compatibility_date": "2026-06-20",
"assets": {
"directory": "./dist/client",
"not_found_handling": "single-page-application"
},
"observability": {
"enabled": true
}
}