Avatar

@pomdtr

59 likes63 public vals
Joined June 14, 2023
I mainly enjoy tinkering with the val.town api: - VS Code integration: https://github.com/pomdtr/valtown-vscode - CLI: https://github.com/pomdtr/vt

Generate a sunbeam form from a val

sunbeam fetch 'https://pomdtr-valform.express.val.run/pomdtr.add' | sunbeam

Imgur

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import { extractArgs } from "https://esm.town/v/pomdtr/extractArgs";
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=41";
export async function valForm(req: express.Request, resp: express.Response) {
try {
const [author, name] = req.path.slice(1).split(".");
const { code } = await fetchJSON(
`https://api.val.town/v1/alias/${author}/${name}`,
);
const args = await extractArgs(code);
if (req.method == "POST") {
const payload = {
args: args.map((arg) =>
arg.type == "number"
? parseInt(req.body[arg.name])
: req.body[arg.name]
),
};
const res = await fetchJSON(
`https://api.val.town/v1/run/${author}.${name}`,
{
method: "POST",
body: JSON.stringify(payload),
},
);
resp.json(res);
return;
}
const inputs = [];
for (const arg of args) {
switch (arg.type) {
case "boolean":
inputs.push({ name: arg.name, title: arg.name, type: "checkbox" });
break;
case "number":
inputs.push({ name: arg.name, title: arg.name, type: "textfield" });
break;
case "string":
inputs.push({ name: arg.name, title: arg.name, type: "textfield" });
break;
default:
resp.status(500);
resp.send(`Unsupported arg type: ${arg.type}`);
return;
}
}
return resp.json({
type: "form",
title: `${author}.${name}`,
submitAction: {
type: "fetch",
request: {
url: `https://pomdtr-valForm.express.val.run/${author}.${name}`,
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: "{{inputs:json}}",
},
inputs,
},
});
}
catch (e) {
resp.status(500);
resp.send(`An error occured: ${e}`);
}
}

Augmented run api

This val is a wrapper on top of the val.town run api, improving it with additional features:

  • basic auth
  • content-type header in response based on url file extension

Usage

Custom Content-Type

The content-type will be inferred from the filename using the mime-types library.

If you use a .html extension, the response will be interpreted as text/html

~ $ curl -v 'https://pomdtr-run.web.val.run/pomdtr/helloWorld.html'
HTTP/1.1 200 OK
...
Content-Type: text/html; charset=utf-8
...

Hello, World!

If you switch the extension to .txt, the content-type header switch to text/raw.

~ $ curl -v 'https://pomdtr-run.web.val.run/pomdtr/helloWorld.txt'
HTTP/1.1 200 OK
...
Content-Type: text/plain; charset=utf-8
...

Hello, World!

Passing arguments

The request is proxyed to the run api, so you can pass args to your vals via query params or body. See the run api docs for more details.

~ $ curl -X POST -d '{"args": ["pomdtr"]}' 'https://pomdtr-run.web.val.run/pomdtr/helloWorld.html'
...
< content-type: text/html; charset=utf-8
...

Hello, pomdtr!

Basic Authentication

Just add your val town token as the username:

curl  'https://<val-token>@pomdtr-run.web.val.run/pomdtr/privateVal.txt'
Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { fetch } from "https://esm.town/v/std/fetch";
import { parseAuthorizationHeader } from "https://esm.town/v/pomdtr/parseAuthorizationHeader";
export async function run(req: Request) {
const url = new URL(req.url);
const [owner, filename] = url.pathname.slice(1).split("/");
const [name] = filename.split(".");
const mime = await import("npm:mime-types");
let contentType = await mime.lookup(filename);
if (!contentType) {
contentType = "application/json";
}
url.host = "api.val.town";
url.pathname = `v1/run/${owner}.${name}`;
const body = await req.text();
const headers = {};
const authorization = req.headers.get("Authorization");
if (authorization) {
const auth = parseAuthorizationHeader(
req.headers.get("Authorization"),
);
if (auth.type == "basic") {
headers["Authorization"] = `Bearer ${auth.username}`;
}
else {
headers["Authorization"] = req.headers.get("Authorization");
}
}
const resp = await fetch(url.toString(), {
method: req.method,
body: body || undefined,
headers,
});
if (resp.status != 200) {
return resp;
}
if (contentType == "application/json") {
return resp;
}
const res = await resp.json();
return new Response(typeof res == "string" ? res : JSON.stringify(res), {
headers: { "Content-Type": contentType },
});
}

Usage

![Button](https://pomdtr-badge.express.val.run/Custom%20Badge)

Button

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import { fetch } from "https://esm.town/v/std/fetch";
export async function badge(req, res) {
async function inlineUrls(cssString) {
const urlRegex = /url\(['"]?(.*?)['"]?\)/g;
let result = cssString;
for (const urlMatch of cssString.matchAll(urlRegex)) {
const originalUrl = urlMatch[1];
const response = await fetch(originalUrl);
const data = new Uint8Array(await response.arrayBuffer());
result = result.replace(
urlMatch[0],
`url("data:${response.headers.get("content-type")};base64,${
btoa(data.reduce((a, b) => a + String.fromCharCode(b), ""))
}")`,
);
}
return result;
}
const esc = (await import("npm:escape-html@1.0.3")).default;
const { init } = await import("npm:server-text-width@1.0.2");
const { getTextWidth } = init(
(await import("https://easrng.github.io/IBMPlexSansMetrics.js")).default,
);
const text = decodeURIComponent(req.path.split("/")[1]);
let fontCss = "";
try {
const r = await fetch(
"https://fonts.googleapis.com/css2?family=" +
encodeURIComponent("IBM Plex Sans:wght@600") +
"&text=" +
encodeURIComponent(text),
{ headers: { "user-agent": req.get("user-agent") } },
);
if (!r.ok)
throw new Error();
fontCss = await inlineUrls(await r.text());
}
catch (e) {}
res.set("content-type", "image/svg+xml");
const width = Math.ceil(getTextWidth(text) + 16);
res.end(
`<svg viewBox="0 0 ${width} 28" width="${width}" height="28" xmlns="http://www.w3.org/2000/svg"><defs><style>${fontCss}</style></defs><foreignObject width="100%" height="100%"><span xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'IBM Plex Sans';font-weight: 600;font-size: .875rem;line-height: 1.25rem;padding-top: .25rem;padding-bottom: .25rem;padding-left: .5rem;padding-right: .5rem;display: inline-block;background: rgb(59 130 246);color: #fff;border-radius: .25rem;text-align: center;">${
esc(text)
}</span></foreignObject></svg>`,
);
}
// Forked from @easrng.button
1
2
3
4
5
6
7
8
9
10
11
12
import { vanPlate } from "https://esm.town/v/pomdtr/vanPlate";
export const exampleTemplating = (() => {
const { html, tags } = vanPlate();
const { body, p, ul, li, a } = tags;
return html(
body(
p("👋Hello"),
ul(li("World"), li(a({ href: "https://vanjs.org/" }, "🍦VanJS"))),
),
);
})();
Output

Van Plate

Example of building html using the mini-van lib.

Access it at https://pomdtr-vanPlateExample.web.val.run

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
export async function vanPlateExample(req: Request) {
const { default: van } = await import(
"https://deno.land/x/minivan@0.3.9/src/van-plate.js"
);
const { a, body, li, p, ul } = van.tags;
return new Response(
van.html(
body(
p("Your user-agent is: ", req.headers.get("user-agent") ?? "Unknown"),
p("👋Hello"),
ul(li("🗺️World"), li(a({ href: "https://vanjs.org/" }, "🍦VanJS"))),
),
),
{
status: 200,
headers: { "content-type": "text/html; charset=utf-8" },
},
);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import { fetch } from "https://esm.town/v/std/fetch";
import { createSpaceApiKey } from "https://esm.town/v/pomdtr/createSpaceApiKey";
import { createSpaceDataKey } from "https://esm.town/v/pomdtr/createSpaceDataKey";
import { fetchSpaceApi } from "https://esm.town/v/pomdtr/fetchSpaceApi";
export async function fetchSpace(
accessToken: string,
store: {
apiKeys?: Record<string, string>;
dataKeys?: Record<string, string>;
},
url: string,
options?: RequestInit,
) {
const { urlParse } = await import("https://deno.land/x/url_parse/mod.ts");
const { hostname, pathname } = urlParse(url) as {
hostname: string;
pathname: string;
};
if (hostname.endsWith("deta.space")) {
return fetchSpaceApi(accessToken, url, options);
}
else if (hostname.endsWith("deta.sh")) {
const [, , projectID] = pathname.split("/");
if (!store.dataKeys[projectID]) {
store.dataKeys[projectID] = await createSpaceDataKey(
accessToken,
projectID,
"val.town",
);
}
const resp = await fetch(url, {
...options,
headers: {
"X-Api-Key": store.dataKeys[projectID],
...options?.headers,
},
});
return resp.json();
}
else { // hostname is either *.deta.app or a custom domain
if (!store.apiKeys[hostname]) {
store.apiKeys[hostname] = await createSpaceApiKey(
accessToken,
hostname,
"val.town",
);
}
const resp = await fetch(url, {
...options,
headers: {
"X-Space-App-Key": store.apiKeys[hostname],
...(options?.headers || {}),
},
});
return resp.json();
}
}

Fetch the source of a val

Usage

curl https://pomdtr-raw.web.val.run/<author>/<name>[@<version>].<extension>

To see the code of this val, use https://pomdtr-raw.web.val.run/pomdtr/raw.ts

Examples

Fetching the val code

$  curl https://pomdtr-raw.web.val.run/pomdtr/add.ts

Fetching private val

Pass an api token as an username

$ curl "https://<token>@pomdtr-raw.web.val.run/pomdtr/privateVal.ts"

Fetching the val README

$ curl https://pomdtr-raw.web.val.run/pomdtr/add.md

Fetching a specific version of a val

$ curl https://$VALTOWN_TOKEN@pomdtr-raw.web.val.run/pomdtr/raw@66.ts

You need to be authenticated to use this method.

Fetching the val metadata

$ curl https://pomdtr-raw.web.val.run/pomdtr/add.json

Running vals locally using Deno

Create a new val.ts file referencing the @pomdtr.add val

import { add } from "https://pomdtr-raw.web.val.run/pomdtr/add.ts";

console.log(add(1, 2));

then use deno run

$ deno run ./val.ts
3

If you val accept a request and return a response, you can pass it to Deno.Serve to run it locally!

import {raw} from "https://pomdtr-raw.web.val.run/pomdtr/raw.ts";

Deno.serve(raw);

If your val is private, you can set the DENO_AUTH_TOKENS env.

DENO_AUTH_TOKENS=<val-town-token>@pomdtr-raw.web.val.run
Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import { fetch } from "https://esm.town/v/std/fetch";
import { parseAuthorizationHeader } from "https://esm.town/v/pomdtr/parseAuthorizationHeader";
export async function raw(req: Request) {
const url = new URL(req.url);
const pattern = /^\/([^\/]+)\/([^@]+)(?:@([^\.]+))?\.([^\.]+)$/;
const match = url.pathname.match(pattern);
if (!match) {
return new Response("invalid request", { status: 400 });
}
const author = match[1];
const name = match[2];
const version = match[3] || "latest";
const extension = match[4];
const headers = {};
if (req.headers.get("Authorization")) {
const auth = parseAuthorizationHeader(
req.headers.get("Authorization"),
);
if (auth.type == "basic") {
headers["Authorization"] = `Bearer ${auth.username}`;
}
else {
headers["Authorization"] = req.headers.get("Authorization");
}
}
const resp = await fetch(`https://api.val.town/v1/alias/${author}/${name}`, {
headers,
});
if (!resp.ok) {
return resp;
}
let val = await resp.json();
if (version != "latest") {
const resp = await fetch(
`https://api.val.town/v1/vals/${val.id}/versions/${version}`,
{ headers },
);
if (!resp.ok) {
return resp;
}
val = await resp.json();
}
const { code, readme } = val;
if (extension == "ts") {
return new Response(code, {
headers: { "Content-Type": "text/typescript" },
});
}
if (extension == "json") {
return new Response(JSON.stringify(val, null, 2), {
headers: { "Content-Type": "text/json" },
});
}
if (extension == "md") {
return new Response(readme, {
headers: { "content-type": "text/markdown" },
});
}
return new Response("Not Found", { status: 404 });
}

Send yourself a Telegram message

Inspired by console.email, this val lets you send yourself a Telegram message via the valtown_bot from any of your vals

@me.telegram("hi to me on telegram!")

Installation

1. Start a conversation with valtown_bot

Click here: https://t.me/valtown_bot

2. Copy your Secret

3. Save in your secrets under telegramSecret

4. Fork this val & run it

Save your own private version of a telegram function to your account by forking this val: https://www.val.town/v/pomdtr.telegram

5. Send a test message!

await @me.telegram("hi to me on telegram!")

PS: the bot code is available here

Readme
1
2
3
4
5
6
7
8
9
10
import process from "node:process";
import { runVal } from "https://esm.town/v/std/runVal";
export async function telegram(message: string) {
return runVal(
"pomdtr.sendToTelegram",
process.env.telegramSecret,
message,
);
}

Usage

Favicon

<link rel="icon" href="https://pomdtr-svgEmoji.express.val.run/💩" />

Markdown

![favicon](https://pomdtr-svgEmoji.express.val.run/💩)

favicon

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
export function svgEmoji(req: express.Request, res: express.Response) {
const emoji = decodeURIComponent(req.path.replace("/", ""));
// People could (did) inject script tags here. So let's escape & and <
const cleanEmoji = emoji.replace(/&/g, "&amp;").replace(/</g, "&lt;");
res.set("Content-Type", "image/svg+xml");
res.set(
"Cache-Control",
`public, max-age=${60 * 60 * 24}, s-maxage=${60 * 60 * 24}`,
);
return res.send(
`<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 16 16'><text x='0' y='14'>${cleanEmoji}</text></svg>`,
);
}

Markdoc Playground

This readme is rendered using markdoc.

{% val author="pomdtr" name="markdocReactExample" %}

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/** @jsxImportSource npm:react */
import { api } from "https://esm.town/v/pomdtr/api";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";
import Markdoc from "npm:@markdoc/markdoc";
import React from "npm:react";
import { renderToString } from "npm:react-dom/server";
const tags = {
val: {
render: "Val",
attributes: {
name: {
type: String,
},
author: {
type: String,
},
},
},
};
function Val({ author, name }) {
return <iframe width="600" height="300" frameBorder="0" src={`https://www.val.town/embed/${author}/${name}`} />;
}
export async function markdocReactExample(req: Request) {
// extract the current val readme
const { author, name } = extractValInfo(import.meta.url);
const { readme } = await api(`/v1/alias/${author}/${name}`);
// render it using markdoc
const ast = Markdoc.parse(readme);
const content = Markdoc.transform(ast, { tags }); // register the val tag
const rendered = Markdoc.renderers.react(content, React, {
components: {
Val,
},
});
return new Response(renderToString(rendered), {
headers: {
"Content-Type": "text/html",
},
});
}