TownieCreditBalance.tsx1 match
9const fetchBalance = async () => {
10try {
11const response = await fetch("/api/credit-balance");
12if (response.ok) {
13const data = await response.json();
Towniecredit-additions.ts1 match
2import { formatNumber, formatPrice, formatDate } from "../utils/formatters.ts";
3import { PaginationResult, renderPaginationControls } from "../utils/pagination.ts";
4import { CreditAddition } from "../api/credit-additions.ts";
56interface CreditAdditionsSummary {
vtEditorFilesAGENTS.md10 matches
13- Generate code in TypeScript or TSX
14- Add appropriate TypeScript types and interfaces for all data structures
15- Prefer official SDKs or libraries than writing API calls directly
16- Ask the user to supply API or library documentation if you are at all unsure about it
17- **Never bake in secrets into the code** - always use environment variables
18- Include comments explaining complex logic (avoid commenting obvious operations)
23### 1. HTTP Trigger
2425- Create web APIs and endpoints
26- Handle HTTP requests and responses
27- Example structure:
173However, it's *extremely importing* to note that `parseProject` and other Standard Library utilities ONLY RUN ON THE SERVER.
174If you need access to this data on the client, run it in the server and pass it to the client by splicing it into the HTML page
175or by making an API request for it.
176177## Val Town Platform Specifics
181- **AI Image:** To inline generate an AI image use: `<img src="https://maxm-imggenurl.web.val.run/the-description-of-your-image" />`
182- **Storage:** DO NOT use the Deno KV module for storage
183- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
184- **Weather Data:** Use open-meteo for weather data (doesn't require API keys) unless otherwise specified
185- **View Source:** Add a view source link by importing & using `import.meta.url.replace("ems.sh", "val.town)"` (or passing this data to the client) and include `target="_top"` attribute
186- **Error Debugging:** Add `<script src="https://esm.town/v/std/catch"></script>` to HTML to capture client-side errors
187- **Error Handling:** Only use try...catch when there's a clear local resolution; Avoid catches that merely log or return 500s. Let errors bubble up with full context
188- **Environment Variables:** Use `Deno.env.get('keyname')` when you need to, but generally prefer APIs that don't require keys
189- **Imports:** Use `https://esm.sh` for npm and Deno dependencies to ensure compatibility on server and browser
190- **Storage Strategy:** Only use backend storage if explicitly required; prefer simple static client-side sites
224### Backend (Hono) Best Practices
225226- Hono is the recommended API framework
227- Main entry point should be `backend/index.ts`
228- **Static asset serving:** Use the utility functions to read and serve project files:
248});
249```
250- Create RESTful API routes for CRUD operations
251- Always include this snippet at the top-level Hono app to re-throwing errors to see full stack traces:
252```ts
285- For files in the project, use `readFile` helpers
2862875. **API Design:**
288- `fetch` handler is the entry point for HTTP vals
289- Run the Hono app with `export default app.fetch // This is the entry point for HTTP vals`
9// Simple test endpoint
10app.get("/test", async (c) => {
11return c.json({ message: "API routes working", timestamp: new Date().toISOString() });
12});
13120const { cleanupStaleViewingSessions } = await import("../../controllers/viewing.controller.ts");
121
122console.log("🔧 Manual cleanup triggered via API");
123const result = await cleanupStaleViewingSessions();
124
1617const updateStatus = async (viewing: boolean, tabVisible: boolean) => {
18// Only make API calls if user is authorized
19if (!isAuthorized) {
20console.log("User not authorized for viewing analytics on this page");
19const now = Date.now();
20if (!cached.accessToken || now >= cached.expiresAt) {
21const tokenResp = await fetch("https://accounts.spotify.com/api/token", {
22method: "POST",
23headers: {
46let playlists: any[] = [];
47let nextUrl: string | null =
48"https://api.spotify.com/v1/me/playlists?limit=50";
4950while (nextUrl) {
1# API Routes
23JSON API endpoints for frontend/backend communication.
45## Separation of Concerns
67API routes handle:
8- JSON request/response formatting
9- HTTP status code management
11- Error response standardization
1213API routes delegate business logic to controllers and return clean JSON responses.
1415## Endpoints
1617### GET /api/health
1819**Purpose**: System health check endpoint
32**HTTP Status**: Always 200
3334### POST /api/viewing
3536**Purpose**: Update page viewing status in blob storage for real-time tracking with immediate Notion sync
41pageId: string, // Notion page UUID
42viewing: boolean, // true when actively viewing, false when leaving
43tabVisible: boolean // Page Visibility API state
44}
45```
92```
9394### GET /api/viewing/:id
9596**Purpose**: Get current viewing status for a page
125**Error Responses**: Same pattern as other endpoints
126127### GET /api/viewing/:id/stats
128129**Purpose**: Get viewing analytics/statistics for a page
148```
149150### GET /api/demo/:id/properties
151152**Purpose**: Get Notion page properties (filtered for UI consumption)
198{
199error: "Failed to fetch page data",
200details: "Notion API error message"
201}
202```
203204### GET /api/demo/:id
205206**Purpose**: Get complete Notion page with content blocks
251## Error Handling Pattern
252253All API routes follow consistent error handling:
254255```typescript
269## Data Filtering
270271API routes return filtered data for UI consumption:
272- **Button properties removed**: Notion button properties are filtered out to prevent UI confusion
273- **Raw Notion format**: Other properties maintain their original Notion API structure
274- **Complete hierarchy**: Block content includes full nested structure with children
275278```bash
279# Get page properties only
280curl https://your-val.web.val.run/api/demo/abc123/properties
281282# Get complete page with content
283curl https://your-val.web.val.run/api/demo/abc123
284285# Health check
286curl https://your-val.web.val.run/api/health
287```
6768**Session Tracking:**
69- Uses `sendBeacon` API for reliable session ending during page unload
70- Falls back to synchronous fetch if sendBeacon fails
71- Handles both normal component unmount and browser tab closure
eventsCalendarserver.ts6 matches
1314import { Hono } from "npm:hono@4";
15import { SSEStreamingApi, stream, streamSSE } from "npm:hono@4/streaming";
16import { html, raw } from "npm:hono@4/html";
17import process from "node:process";
27return streamSSE(c, async (stream) => {
28// validate token
29const apiToken = c.req.query("token");
30if (apiToken !== process.env.API_TOKEN) {
31await fail(stream, "Unauthorized");
32return stream.close();
209});
210211function update(stream: SSEStreamingApi, payload: any) {
212return stream.writeSSE({
213event: "update",
220221function complete(
222stream: SSEStreamingApi,
223state: "success" | "error",
224) {
233234function fail(
235stream: SSEStreamingApi,
236message?: string,
237) {
eventsCalendarlogs.tsx1 match
20export const dashboard = async (req) => {
21const token = new URL(req.url).searchParams.get("token");
22if (token !== process.env.API_TOKEN) {
23return new Response("Token required to view logs", { status: 403 });
24}