2const API_URL = "https://greasegum--47620ec654ae11f088f8f69ea79377d9.web.val.run";
34async function testAPI() {
5console.log("Testing Bird Facts API...");
6
57// --- DATABASE MIGRATION & INITIALIZATION ---
5859async function initializeDatabase() {
60if (Deno.env.get(DB_INIT_KEY) === "true") return;
61if (Deno.env.get("DB_INITIALIZING") === "true") {
87}
8889// --- UTILITY & HELPER FUNCTIONS ---
9091async function hashToken(token: string) {
92const data = new TextEncoder().encode(token);
93const hash = await crypto.subtle.digest("SHA-256", data);
95}
9697function extractJson(str: string): any {
98const match = str.match(/\{.*\}/s);
99if (!match) throw new Error("AI response did not contain a valid JSON object.");
106}
107108function jsonResponse(data: any, status = 200) {
109const headers = { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" };
110return new Response(JSON.stringify(data), { status, headers });
111}
112113function optionsResponse() {
114return new Response(null, {
115status: 204,
122}
123124async function verifyAuth(req: Request): Promise<string | null> {
125const authHeader = req.headers.get("Authorization");
126if (!authHeader || !authHeader.startsWith("Bearer ")) return null;
132133// --- HTML & FRONTEND (SPA) ---
134function generateHtml(sourceUrl: string) {
135// This is a complete, functional SPA.
136return `<!DOCTYPE html>
137<html lang="en">
156</main>
157<script>
158(function() {
159const apiUrl = '${sourceUrl}api';
160const app = document.getElementById('app-container');
163164// --- API CLIENT ---
165async function resilientFetch(path, options = {}) {
166options.headers = { ...options.headers, 'Content-Type': 'application/json' };
167if (state.token) options.headers['Authorization'] = \`Bearer \${state.token}\`;
183184// --- UI RENDERING ---
185function renderNav() {
186if (state.token) {
187nav.innerHTML = \`<a href="#/feed">Feed</a> | <a href="#/my-habits">My Habits</a> | <a href="#" id="logout-btn">Logout</a>\`;
191}
192
193function renderLoginView() {
194app.innerHTML = \`<div id="login-view" class="view active"><h2>Login</h2>...</div>\`;
195// Add form and event listener for login
196}
197// Add render functions for all other views (register, feed, habits, etc.)
198199// --- ROUTING ---
200function router() {
201const path = window.location.hash.slice(1) || '/';
202renderNav();
232// --- MAIN HTTP VAL HANDLER ---
233234export default async function handleRequest(req: Request): Promise<Response> {
235await initializeDatabase();
236const url = new URL(req.url);
71/**
72* Ensures the database is initialized and migrated to the latest schema version.
73* This function is designed to be idempotent and safe for concurrent execution.
74* It uses a simple locking mechanism via Deno.env to prevent race conditions
75* during the first-ever initialization in a serverless environment.
76*/
77async function initializeDatabase() {
78if (Deno.env.get(DB_INIT_KEY) === "true") {
79return;
127// --- HTML & FRONTEND ---
128129function generateHtml(sourceUrl: string, affirmations: string[]) {
130return `<!DOCTYPE html>
131<html lang="en">
339340<script>
341(function() {
342const API_URL = '${sourceUrl}';
343const affirmations = ${JSON.stringify(affirmations)};
359let affirmationInterval;
360361function showLoading(visible) {
362if (visible) {
363let currentAffirmation = 0;
386* @param {number} backoff - The initial backoff in milliseconds.
387*/
388async function resilientFetch(url, options, retries = 3, backoff = 300) {
389for (let i = 0; i < retries; i++) {
390try {
403}
404405function renderResponse(entryText, guidance) {
406$$.initialView.classList.add("hidden");
407$$.responseArea.classList.remove("hidden");
465* @throws {Error} If no valid JSON object is found.
466*/
467function extractJson(str) {
468const match = str.match(/\{.*\}/s);
469if (!match) {
543544// Main request handler for serving the HTML application.
545export default async function(req: Request) {
546const url = new URL(req.url);
547
feedburnermain.tsx5 matches
1async function handler(req) {
2var _a;
3const origin = req.headers.get("Origin");
94}
95}
96function processURLsInDoc(doc, targetURL, attrs) {
97const targetHostname = targetURL.hostname;
98attrs.forEach((attr) => {
140"https://",
141];
142function processURL(url, targetURL) {
143if (!SUPPORTED_PROTOCOLS.some((prefix) => url.startsWith(prefix))) {
144return url;
158}
159}
160function processURLsInContent(content, targetURL) {
161const urlCache = /* @__PURE__ */ new Map();
162const processURLWithCache = (url) => {
171});
172}
173function setCORSHeaders(responseHeaders, origin) {
174responseHeaders.set("Access-Control-Allow-Origin", origin ?? "*");
175responseHeaders.set("access-control-allow-methods", "GET,POST,PUT,PATCH,TRACE,DELETE,HEAD,OPTIONS");
myNewWebsiteApp.tsx1 match
2import { useState } from "https://esm.sh/react@18.2.0";
34export function App() {
5const [clicked, setClicked] = useState(0);
6return (
stevensDemotestDailyBrief.ts1 match
4import { DateTime } from "https://esm.sh/luxon@3.4.4";
56export async function testDailyBrief() {
7try {
8const testChatId = Deno.env.get("TEST_TELEGRAM_CHAT_ID");
2// Run this script manually to create the database table
34export default async function setupTelegramChatDb() {
5try {
6// Import SQLite module
stevensDemosendDailyBrief.ts6 matches
13} from "../memoryUtils.ts";
1415async function generateBriefingContent(anthropic, memories, today, isSunday) {
16try {
17const weekdaysHelp = generateWeekDays(today);
96}
9798export async function sendDailyBriefing(chatId?: string, today?: DateTime) {
99// Get API keys from environment
100const apiKey = Deno.env.get("ANTHROPIC_API_KEY");
135const lastSunday = today.startOf("week").minus({ days: 1 });
136137// Fetch relevant memories using the utility function
138const memories = await getRelevantMemories();
139216}
217218function generateWeekDays(today) {
219let output = [];
220239// console.log(weekDays);
240241// Export a function that calls sendDailyBriefing with no parameters
242// This maintains backward compatibility with existing cron jobs
243export default async function (overrideToday?: DateTime) {
244return await sendDailyBriefing(undefined, overrideToday);
245}
stevensDemoREADME.md2 matches
16In a normal server environment, you would likely use a middleware [like this one](https://hono.dev/docs/getting-started/nodejs#serve-static-files) to serve static files. Some frameworks or deployment platforms automatically make any content inside a `public/` folder public.
1718However in Val Town you need to handle this yourself, and it can be suprisingly difficult to read and serve files in a Val Town Project. This template uses helper functions from [stevekrouse/utils/serve-public](https://www.val.town/x/stevekrouse/utils/branch/main/code/serve-public/README.md), which handle reading project files in a way that will work across branches and forks, automatically transpiles typescript to javascript, and assigns content-types based on the file's extension.
1920### `index.html`
26## CRUD API Routes
2728This app has two CRUD API routes: for reading and inserting into the messages table. They both speak JSON, which is standard. They import their functions from `/backend/database/queries.ts`. These routes are called from the React app to refresh and update data.
2930## Errors
stevensDemoREADME.md2 matches
45* `migrations.ts` - code to set up the database tables the app needs
6* `queries.ts` - functions to run queries against those tables, which are imported and used in the main Hono server in `/backend/index.ts`
78## Migrations
18The queries file is where running the migrations happen in this app. It'd also be reasonable for that to happen in index.ts, or as is said above, for that line to be commented out, and only run when actual changes are made to your database schema.
1920The queries file exports functions to get and write data. It relies on shared types and data imported from the `/shared` directory.