Search
btcPriceAlert
@richardkaplan
BTC Price Alert This val monitors the price of Bitcoin (BTC) and sends an email alert if the price fluctuates significantly. Specifically, it checks the current BTC price against the last recorded price and triggers an email notification if the change exceeds 20%. The email includes the new price, formatted as currency. Fork this val to get these notifications on your inbox.
Cron
import { email } from "https://esm.town/v/std/email?v=9";
import { currency } from "https://esm.town/v/stevekrouse/currency";
export async function btcPriceAlert() {
const lastBtcPrice: number = await blob.getJSON("lastBtcPrice");
let btcPrice = await currency("usd", "btc");
emailMeExchangeRate
@frankdilo
An interactive, runnable TypeScript val by frankdilo
Script
import { formatCurrency } from "https://esm.town/v/frankdilo/formatCurrency";
import { convertCurrency } from "https://esm.town/v/frankdilo/convertCurrency";
export async function emailMeExchangeRate() {
const rate = await convertCurrency("eur");
const formattedRate = formatCurrency(rate, 3, "EUR");

stripFences
@stevekrouse
Strip Fences Useful for stripping out code fences, ie from a reply from an llm where you want code and it replies with markdown code fences around that code.
Script
export default function stripFences(content: string) {
return content.replace(/```[\s\S]*?\n([\s\S]*?)\n```/g, "$1");

dataURL
@vladimyr
// SPDX-License-Identifier: 0BSD
Script
// SPDX-License-Identifier: 0BSD
import ky from "npm:ky";
export async function convertUrlToDataURL(url: string | URL): Promise<string> {
const blob = await ky.get(url).blob();
return convertBlobToDataURL(blob);
export function convertBlobToDataURL(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
jamz
@all
@jsxImportSource https://esm.sh/react
HTTP
/** @jsxImportSource https://esm.sh/react */
// Set to true to disable all editing functionality
const READ_ONLY = true;
created_at: string;
function AddSongForm({ onAdd }: { onAdd: (song: Omit<Song, "id" | "created_at">) => void }) {
const [url, setUrl] = React.useState("");
</form>
function SongCard(
{ song, onDelete, onEdit }: {
</div>
function App() {
const [songs, setSongs] = React.useState<Song[]>([]);
</div>
function client() {
createRoot(document.getElementById("root")!).render(<App />);
client();
export default async function server(req: Request): Promise<Response> {
const { sqlite } = await import("https://esm.town/v/stevekrouse/sqlite");
substrateBadgeMiddleware
@substrate
How to use: import wrapper from "https://esm.town/v/substrate/substrateBadgeMiddleware";
async function handler(req: Request): Promise<Response> {
const html = `
<h1>Hello, world</h1>
`;
return new Response(html, {
headers: {
"Content-Type": "text/html; charset=utf-8",
},
});
}
export default wrapper(handler, import.meta.url); reference: https://www.val.town/v/jxnblk/valTownBadge
Script
import wrapper from "https://esm.town/v/substrate/substrateBadgeMiddleware";
async function handler(req: Request): Promise<Response> {
const html = `
import valTownBadge from "https://esm.town/v/substrate/substrateBadge";
export default function badgeMiddleware(handler, url: string) {
return async function(req: Request): Promise<Response> {
if (req.method !== "GET") return await handler(req);

missingRosePony
@jakub_tel
BTC Price Alert This val monitors the price of Bitcoin (BTC) and sends an email alert if the price fluctuates significantly. Specifically, it checks the current BTC price against the last recorded price and triggers an email notification if the change exceeds 20%. The email includes the new price, formatted as currency. Fork this val to get these notifications on your inbox.
Cron
import { email } from "https://esm.town/v/std/email?v=9";
import { currency } from "https://esm.town/v/stevekrouse/currency";
export default async function btcPriceAlert() {
const lastBtcPrice: number = await blob.getJSON("lastBtcPrice");
let btcPrice = await currency("usd", "btc");
htmlBuilder
@xkonti
* Renders the provided tag into HTML string.
Script
* Renders the provided tag into HTML string.
export function renderTag(tag: TagDescriptor): string {
const content = renderTags(tag.content);
* Renders provided tags into a list of HTML strings.
export function renderTags(tags: Array<HtmlContent>): string[] {
return tags.map(element => {
return renderTag(element);
export function basicTag(tagName: string, content: HtmlContents | undefined = undefined): Tag {
const tag = new Tag(tagName);
return tag;
export function selfClosingTag(tagName: string): Tag {
return new Tag(tagName).makeSelfClose();
export function fragment(content: HtmlContents | undefined = undefined): Tag {
return basicTag("");

ab2str
@stevekrouse
Convert an ArrayBuffer into a string
from https://developer.chrome.com/blog/how-to-convert-arraybuffer-to-and-from-string/
Script
Convert an ArrayBuffer into a string
from https://developer.chrome.com/blog/how-to-convert-arraybuffer-to-and-from-string/
export function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint8Array(buf));
satisfactoryBlushCatshark
@maxm
An interactive, runnable TypeScript val by maxm
Script
import { Buffer } from "node:buffer";
import { base58 } from "npm:@scure/base";
export function convertUUIDToToken(uuid: string): string {
return "vt_" + base58.encode(Buffer.from(uuid.replace(/-/g, ""), "hex"));
export function generateAndConvertUUIDs() {
const uuidBuffer = Buffer.alloc(16);
let tokenLength = 0;
weatherForecastDashboard
@bhavana
@jsxImportSource https://esm.sh/react@18.2.0
HTTP
import { createRoot } from "https://esm.sh/react-dom@18.2.0/client";
function WeatherDashboard() {
const [weather, setWeather] = useState(null);
useEffect(() => {
function handleClickOutside(event) {
if (suggestionRef.current && !suggestionRef.current.contains(event.target)) {
return () => document.removeEventListener('mousedown', handleClickOutside);
async function fetchLocationSuggestions(query) {
if (query.length < 2) {
fetchLocationSuggestions(value);
async function selectLocation(selectedLocation) {
setLocationInput(selectedLocation.name);
await fetchWeatherData(selectedLocation);
async function fetchWeatherData(coords) {
try {
setError('Failed to fetch weather data');
async function handleLocationSubmit(e) {
e.preventDefault();
</div>
function client() {
createRoot(document.getElementById("root")).render(<WeatherDashboard />);
if (typeof document !== "undefined") { client(); }
export default async function server(request: Request): Promise<Response> {
return new Response(`
svgMapOfCountry
@jdan
An interactive, runnable TypeScript val by jdan
Script
import { countryData } from "https://esm.town/v/jdan/countryData";
import { blob } from "https://esm.town/v/std/blob?v=10";
export async function svgMapOfCountry(country: string) {
const mapUrl = (await countryData())
.find((value) => value.countryLabel === country)
// Start SVG string without width and height, only viewBox
let svgString = `<svg viewBox="${viewBox}" xmlns="http://www.w3.org/2000/svg">\n`;
// Function to convert a ring to a path data string
const ringToPathD = (ring) => {
let d = `M ${ring[0][0]} ${90 - ring[0][1]} `;

convertRGBToHex
@rodrigotello
An interactive, runnable TypeScript val by rodrigotello
Script
export function convertRGBToHex(rgb) {
return ((rgb * 255) | (1 << 8)).toString(16).slice(1);
expiringBlob
@postpostscript
expiringBlob: create-and-forget blobs using UUIDv7 How it Works This tool uses UUIDv7s in blob keys with UNIX timestamps included to keep track of when they expire.
Since UUIDv7s are sortable and the blob list endpoint returns results alphabetically, cleanup (checking the blob list and deleting any expired keys) is efficient since any expired keys will be at the front of the results! Each ID is prefixed with a prefix (default: expireBlob: ) and can be suffixed with a label (parameter order depends on the method).
Example blob keys: expiringBlob:01944432-4837-7a99-94e6-e0e162554e21
^Prefix ^UUIDv7
expiringBlob:01944432-4837-7a99-94e6-e0e162554e21:aaaa
^Prefix ^UUIDv7 ^Label Methods createKey : generate a key you can use with @std/blob methods set or setJSON : generate a blob key and save content to it in one step cleanup : delete expired blobs. You can run this manually or periodically with a cron val . If using a custom prefix, make sure to pass it to this method so that the correct blob list is searched. By default, createKey , set , and setJSON will create keys that expire in an hour, but you can override this by passing the expireAt: Date | number parameter. For convenience, expireIn is provided with methods to create future dates for each of the following: expireIn.hours(2) expireIn.minutes(5) expireIn.seconds(45) expireIn.ms(500) To request data once you've saved it, use @std/blob's get and getJSON as usual with the key provided by set or setJSON ! Full Example import { cleanup, createKey, expireIn, set, setJSON } from "https://esm.town/v/postpostscript/expiringBlob";
import { blob } from "https://esm.town/v/std/blob";
const aaaa = createKey("aaaa"); // equivalent of createKey("aaaa", expireIn.hours(1))
await blob.setJSON(aaaa, {
x: 1,
});
console.log(aaaa, await blob.getJSON(aaaa));
// expiringBlob:01944432-4837-7a99-94e6-e0e162554e21:aaaa { x: 1 }
console.log(await set("bbbb", "test", expireIn.seconds(1)));
// expiringBlob:019443fb-6106-77f2-ab0c-5142b099c81a:bbbb
console.log(await setJSON("cccc", { x: 1 }, expireIn.seconds(1)));
// expiringBlob:019443fb-6327-733c-9a0b-c8b9ee9faef0:cccc
await cleanup();
// [no logs]
await new Promise((resolve) => setTimeout(resolve, 1000));
await cleanup();
// deleting expired blob: expiringBlob:019443fb-6106-77f2-ab0c-5142b099c81a:bbbb
// deleting expired blob: expiringBlob:019443fb-6327-733c-9a0b-c8b9ee9faef0:cccc Forkable Cron Example Create a cron val with the following code to periodically delete expired blobs: import { cleanup } from "https://esm.town/v/postpostscript/expiringBlob";
export default () => cleanup();
Script
export const PREFIX = "expiringBlob:";
export async function cleanup(prefix = PREFIX, $blob: BlobInterface = blob) {
let deletedAnyOverall = false;
return new Date(new Date().getTime() + value);
export function createKey(label?: string, expireAt: Date | number = expireIn.hours(1), prefix = PREFIX) {
if (label && (label.length - prefix.length) >= 36) {
return id;
export async function set(
label: string | undefined,
return id;
export async function setJSON(
label: string | undefined,
type BlobInterface = Pick<typeof blob, "set" | "setJSON" | "delete" | "list">;
async function* applicableBlobKeys(prefix = PREFIX, $blob: BlobInterface = blob): AsyncGenerator<string> {
for (const el of await $blob.list(prefix)) {

githubNormalizedJsonString
@stevekrouse
An interactive, runnable TypeScript val by stevekrouse
Script
export function githubNormalizedJsonString(payload: object) {
// GitHub sends its JSON with an indentation of 2 spaces and a line break at the end
const payloadString = JSON.stringify(payload, null, 2) + "\n";