TypeScript 55.4%
Python 43.2%
SQL 1.2%
1"use client";23import { clientApiBase } from "./api";45const KEY = "ws_owner";6const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-";78/** Anonymous owner token for watchlists/alerts (phase 1, no accounts). Generated once per browser. */9export function ownerToken(): string {10 if (typeof window === "undefined") return "";11 let t = window.localStorage.getItem(KEY);12 if (!t || !/^[A-Za-z0-9_-]{16,80}$/.test(t)) {13 const bytes = new Uint8Array(32);14 window.crypto.getRandomValues(bytes);15 t = Array.from(bytes, (b) => ALPHABET[b % ALPHABET.length]).join("");16 window.localStorage.setItem(KEY, t);17 }18 return t;19}2021export async function ownerFetch<T>(path: string, init: RequestInit = {}): Promise<T> {22 const res = await fetch(`${clientApiBase()}${path}`, {23 ...init,24 headers: { "content-type": "application/json", accept: "application/json", "x-websensor-owner": ownerToken(), ...(init.headers ?? {}) },25 });26 if (!res.ok) {27 const body = (await res.json().catch(() => ({}))) as { error?: string };28 throw new Error(body.error ?? `HTTP ${res.status}`);29 }30 return (await res.json()) as T;31}3233export async function publicFetch<T>(path: string): Promise<T> {34 const res = await fetch(`${clientApiBase()}${path}`, { headers: { accept: "application/json" } });35 if (!res.ok) throw new Error(`HTTP ${res.status}`);36 return (await res.json()) as T;37}38