Magic-8-Jev
September 18, 2026 · View on GitHub
A minimal, tactile Magic 8 Ball with a little attitude. Ask a question; Jev chooses the most fitting of twenty witty replies. The result appears in the ball immediately, with measured click-to-answer latency.
Demo: https://willprout.github.io/magic-8-ball/
How it works
- GitHub Pages serves a tiny Vite/TypeScript frontend. The dimensional ball is CSS: no WebGL engine, model downloads, or render loop.
- A Cloudflare Worker sends one
choicequestion tohttps://api.typesafe.ai/v1/systemone, withjev-latestand all 20 faces incriteria. - The Worker returns Jev's selected face. There is no random fallback, answer cache, fake timer, forced animation delay, or automatic retry.
- The displayed time measures the complete click-to-DOM-update journey, including the network and backend. The status tooltip also shows the backend-to-Jev HTTP round trip; it is not pure model inference time.
- Fonts are self-hosted. Answer text is measured and wrapped within the triangular face; longer replies use smaller type. Layouts are precomputed after fonts load, without delaying requests. The question field grows as text wraps, up to ten lines before scrolling. Enter adds a line; Ctrl/Command+Enter submits. Screen-reader announcements, reduced motion, and mobile layouts are supported.
The live TypeSafe API reference and Choice guide define the integration. The twenty custom replies and exact Jev instructions live in worker/answers.ts.
Local development
Use Node 22.18+ and Python 3.
npm ci
npm run key
npm run dev:api
In a second terminal:
npm run dev
Open http://127.0.0.1:5173/magic-8-ball/. Vite forwards /api/ask to the local Worker on port 8787. The key prompt does not echo; it writes .dev.vars with permissions 0600. That file is Git-ignored. The helper refuses to overwrite an existing key file.
npm test
npm run build
Tests cover response membership, exactly one Jev call, CORS, input limits, rate limits, failure handling, redirects, timeouts, and cancellation. Test calls are mocked and never consume Jev usage.
Triangle layout tests check all twenty replies. During local development, /magic-8-ball/qa/answers.html renders every face with the production typography for visual and browser geometry checks. This fixture makes no API calls and is excluded from the production build.
Deploy
- Sign in with
npx wrangler login. - Run
npm run deploy:apito create or update the Worker. - Run
node scripts/publish-jev-key.mjsto upload the local key directly to the Worker's encryptedTYPESAFE_API_KEYsecret. It travels through stdin, never a command argument. - Set the repository variable
VITE_API_URLto the public Worker URL. It is an endpoint URL, not a secret. - Set GitHub Pages' source to GitHub Actions, then push
main. The Pages workflow runs tests, builds, and publishesdist/only.
Never put the Jev key in a VITE_* variable, a public config file, a GitHub Pages artifact, or browser storage. Worker changes deploy separately from the Pages workflow via npm run deploy:api.
Traffic and cost controls
The frontend is static and CDN-hosted. The Worker validates requests before making a Jev call, bounds questions to 2,000 characters (roughly 500 tokens of ordinary English), accepts browser origins only from https://willprout.github.io in production, and aborts upstream requests after 8 seconds. The character cap is an approximation, not an exact tokenizer limit; instructions and answer choices also count toward billable input tokens. It does not log questions or keys, and it does not persist user questions.
Native Cloudflare rate-limit bindings allow 15 requests per IP per minute and 300 requests per Cloudflare location per minute. Requests above those limits receive a retry message. Both bindings must exist in production; otherwise the Worker fails closed. Local development explicitly opts into localhost support with LOCAL_DEV=true.
These are approximate, per-location abuse limits, not a global spending cap or authentication. Origin headers can be forged outside a browser. Every admitted request can consume TypeSafe usage. For a large launch, set any available account-level budget/rate limits in TypeSafe, monitor usage, and adjust the demo limits deliberately. The Worker can be disabled by removing its TYPESAFE_API_KEY secret. Never enable LOCAL_DEV in production.
Cloudflare's Workers pricing and rate-limit semantics are the source of truth. Free-tier exhaustion should mean temporary unavailability rather than silently changing to a paid plan; no paid subscription is configured by this project.
Fonts
DM Sans and Source Serif 4 are distributed under the SIL Open Font License; their licenses are included in public/fonts/. Both are self-hosted; the Source Serif 4 heading uses true italic at weight 400 and optical size 48.