Search
keyvhqBlob
@vladimyr
// SPDX-License-Identifier: 0BSD
Script
key?: string;
export const blobStore = createStore();
export function createStore<T>(options?: KeyvBlobOptions): Store<T> {
const key = options?.key ?? "keyv";
return new KeyvLow<T>({
extractHeaderComments
@vladimyr
// SPDX-License-Identifier: 0BSD
Script
// SPDX-License-Identifier: 0BSD
export function extractHeaderComments(code: string) {
const comments: string[] = [];
let currChar;
btcPriceAlert
@ezomode
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");
convertEmailToBlogDraft
@georgemandis
Convert Emails to Blog Drafts This val will receive inbound emails and create a new Markdown file in a specified GitHub repository. Create drafts for your static site (or, for the bold, publish directly!) by simply sending an email! Notes This script assumes you're using some kind of static site generator that can read from a GitHub repository. There are many to choose from in this space. I'm partial to 11ty Gmail is doing some heavy-lifting for me here in terms of kindly converting my HTML email to Markdown behind the scenes. It's not a guarantee all email providers do this. If yours does not, you may have to investigate an HTML-to-Markdown conversion step. We'll use the GitHub API via Octokit to create the file. You'll need to create a token and store it an an environmental variable in your Val Town settings. Enjoy! — George https://george.mand.is/2024/09/using-email-and-val-town-to-draft-blog-posts/
Email
import { Octokit } from "https://esm.sh/@octokit/core";
import { email } from "https://esm.town/v/std/email";
export default async function(emailData: Email) {
// Extract email content
const htmlContent = emailData.text;
cisaKEVToRSS
@hrbrmstr
CISA KEV To RSS This val.town HTTP endpoint reads the JSON from CISA's Known Exploited Vulnerabilities Catalog and converts it to an RSS feed.
HTTP
const RSS_FEED_URL = "https://hrbrmstr-cisakevtorss.web.val.run"; // Update this to your actual RSS feed URL
function escapeXML(str: string): string {
return str.replace(/&/g, "&")
.replace(/'/g, "'");
function removeInvalidXMLChars(str: string): string {
return str
.replace(/\uFFFD/g, ""); // Remove Unicode Replacement Character
function generateRSS(data: any): string {
const { title, catalogVersion, dateReleased, vulnerabilities } = data;
return rss;
export async function handler(req: Request): Promise<Response> {
try {
getFileIndexPosition
@andreterron
An interactive, runnable TypeScript val by andreterron
Script
export async function getFileIndexPosition({ line, col }, textContents) {
const lines = textContents.split("\n");
let position = 0;
cancelledStripeSubscriber
@charmaine
Cancelled Stripe Subscription Handler This val processes cancelled Stripe subscribers to Val Town Pro. It sends our team
a Discord notification. It takes a couple of steps if you'd like to set up something similar for your own Stripe account. Setup Fork this HTTP val Create a new webhook in Stripe Add your val's HTTP endpoint URL into the Stripe webhook Select customer.subscription.cancelled as the only event to listen to (more on this below) Add your stripe_sk_customer_readonly to your Val Town Env Variables Add your webhook's signing secret as STRIPE_WEBHOOK_SECRET to you Val Town Env Variables How the code is structured Verifies the signature to make sure it's really from Stripe Sends off the Discord message
HTTP
const stripe = new Stripe(Deno.env.get("stripe_sk_customer_readonly") as string, {
apiVersion: "2020-08-27",
function getStripeCustomer(customerId: string) {
console.log(customerId);
return stripe.customers.retrieve(customerId);
jwkToDidKey
@vladimyr
// SPDX-License-Identifier: 0BSD
Script
x: string;
[propName: string]: unknown;
export function jwkToMultibase(jwk: JWK) {
if (jwk.kty === "OKP") {
const bytes = base64url.baseDecode(jwk.x);
return bytesToMultibase(bytes, jwk.crv);
throw new TypeError("error: unsupported key type");
export function jwkToDidKey(jwk: JWK) {
const keyMultibase = jwkToMultibase(jwk);
return `did:key:${keyMultibase}`;
resetFavoriteSongSearches
@dthyresson
Reset Favorite Song Searches Cron to clear and reset favoritre Spotify song searches periodically.
Cron
{ artist: "Primal Scream", title: "Higher than the Sun" },
] as Track[];
export default async function(interval: Interval) {
await sqlite.execute(`drop table if exists favorite_song_searches;`);
await sqlite.execute(`
tidbytSkipped
@andreterron
An interactive, runnable TypeScript val by andreterron
Script
import { tidbytCircle } from "https://esm.town/v/andreterron/tidbytCircle";
export async function tidbytSkipped({ size = 7, bg = 0xffffff80, line = 3 }: {
size?: number;
bg?: number;
valTownBadgeExample
@jxnblk
Example of how to add a Val Town badge to your val Demo: https://jxnblk-valtownbadgeexample.web.val.run/ Option 1 Wrap your HTTP response handler with https://www.val.town/v/jxnblk/valTownBadgeMiddleware [^1] import wrapper from "https://esm.town/v/jxnblk/valTownBadgeMiddleware";
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); Option 2 Get the HTML string for the badge using https://www.val.town/v/jxnblk/valTownBadge Add the HTML to your response's HTML string wherever you like import valTownBadge from "https://esm.town/v/jxnblk/valTownBadge";
export default async function(req: Request): Promise<Response> {
const badge = valTownBadge(import.meta.url);
const html = `
<h1>Hello, world</h1>
${badge}
`;
return new Response(html, {
headers: {
"Content-Type": "text/html; charset=utf-8",
},
});
} Manual options You can also edit the snippet below to manually add the badge in HTML <a href="https://www.val.town/v/jxnblk/valTownBadgeExample" target="_blank" style="text-decoration:none;color:inherit">
<img src="https://jxnblk-valtownbadgesvg.web.val.run/" width="160" height="160">
</a> Or markdown: [![View source on Val Town](https://jxnblk-valtownbadgesvg.web.val.run/)](https://www.val.town/v/jxnblk/valTownBadgeExample) [^1]: Middleware is not recommended when using client-side hydration in apps like React or when returning a full, valid HTML response
HTTP
import wrapper from "https://esm.town/v/jxnblk/valTownBadgeMiddleware";
async function handler(req: Request): Promise<Response> {
const html = `
import valTownBadge from "https://esm.town/v/jxnblk/valTownBadge";
export default async function(req: Request): Promise<Response> {
const badge = valTownBadge(import.meta.url);
import { render } from "npm:preact-render-to-string";
function handler(req: Request): Promise<Response> {
const body = render(
interruptibleChain
@postpostscript
interruptibleChain: simple interface for pausing and resuming execution chains ⚠️ Moved to GitHub ⚠️ import { $chain } from "https://esm.town/v/postpostscript/interruptibleChain";
const multiply = $chain()
.then(() => 1)
.then((x) => x + 3)
.then(async (x) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
return x * 2;
});
const handle = multiply.init();
console.log(await handle.untilDone());
// { done: true, value: 8, handle: InterruptibleChainHandle { ... } }
const handle2 = multiply.init({
index: 2,
value: 4,
});
await handle2.advance();
console.log(handle2.done, handle2.state);
// true { index: 3, value: 8 }
/**** Interrupts ****/
const handle3 = $chain().then(() => {
throw new Error("failed");
}).init();
await handle3.advance();
console.log(handle3.done, handle3.interrupt);
// false Interrupt { reason: Error: failed, chain: InterruptibleChain { index: 0 } }
console.log(await handle3.untilDone());
// { done: false, interrupt: Interrupt { ... }, handle: InterruptibleChainHandle { ... } }
const handle4 = $chain().then((_, interrupt) => {
return interrupt("call me later");
}).init();
await handle4.advance()
console.log(handle4.done, handle4.interrupt);
// false Interrupt { reason: "call me later", chain: InterruptibleChain { index: 0 } }
Script
import type { MaybePromise } from "https://esm.town/v/postpostscript/typeUtils";
export function $chain<T, P>(method: InterruptibleChainMethod<T, P>): InterruptibleChain<T, P>;
export function $chain(): InterruptibleChain<void, void>;
export function $chain<T, P>(
method?: InterruptibleChainMethod<T, P>,
type SubchainState = InterruptibleChainState | undefined;
export function subchainMethod<T, P = any>(chain: InterruptibleChain<T>): InterruptibleChainMethod<T, P> {
return async (previous, interrupt, intermediateState: SubchainState) => {
status.handle.state,
export function join<Chains extends InterruptibleChain<any>[]>(
...chains: Chains
: Return;
export function append<Chains extends InterruptibleChain<any>[]>(
...chains: Chains
timeZoneCompareTool
@paulkinlan
// This timezone comparison tool will use the Luxon library for date and time manipulation.
HTTP
"Australia/Sydney",
// Helper function to group timezones by country
function groupTimezonesByCountry(timezones) {
return timezones.reduce((acc, zone) => {
"Pacific/Auckland",
function TimezoneSelect({ onSelect }) {
const [selectedZone, setSelectedZone] = useState("");
</div>
function App({ initialTime }: { initialTime: string }) {
const [currentTime, setCurrentTime] = useState(DateTime.now());
</div>
function TimezoneRow({ zone, currentTime }: { zone: string; currentTime: DateTime }) {
const localTime = currentTime.setZone(zone || 'UTC');
window.addEventListener('load', client);
async function server(request: Request): Promise<Response> {
try {
expiringUUID
@postpostscript
An interactive, runnable TypeScript val by postpostscript
Script
import { v7, version } from "npm:uuid";
export function create(expireAt: Date | number) {
return v7({
msecs: new Date(expireAt).getTime(),
export function getTimestamp(uuid: string): number {
if (version(uuid) !== 7) {
return parseInt(tsHex, 16);
export function* getExpired(ids: Iterable<string>, presorted = false, logErrors = true): Generator<string> {
const now = new Date().getTime();
console.error(`id ${id} is invalid:`, e);
export async function* getExpiredAsync(
sortedIDs: AsyncIterable<string>,
libarmor
@vladimyr
// SPDX-License-Identifier: 0BSD
Script
// SPDX-License-Identifier: 0BSD
export function createPacketReader({
packetStartMarker,
packetStartMarker += "\n";
return function readPacket(input: string) {
input = input.trim();
return input.substring(contentOffset, contentOffset + contentLength);
export function createSignedPacketReader({
packetStartMarker,
signatureStartMarker += "\n";
return function readSignedPacket(input: string) {
input = input.trim();