Avatar

neverstew

Twice called wise. 🧐 Building a better world at https://equalcare.coop.
118 public vals
Joined January 12, 2023

This Val URL

Returns the URL of the val that calls this function.

See https://www.val.town/v/neverstew.thisValUrlExample

1
2
3
4
5
6
import { parentReference } from "https://esm.town/v/stevekrouse/parentReference?v=3";
export let thisValUrl = () => {
let { userHandle, valName } = parentReference();
return `https://val.town/v/${userHandle}.${valName}`;
};

View source code

Just like a right click + inspect on desktop, except available on mobile too! Just write the website after the /

e.g. https://neverstew-viewSource.web.val.run/bbc.co.uk

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
import { prettifyHtml } from "https://esm.town/v/neverstew/prettifyHtml";
import { fetchText } from "https://esm.town/v/stevekrouse/fetchText?v=6";
import { escape } from "npm:html-sloppy-escaper";
export default async function viewSource(req: Request) {
const pathname = new URL(req.url).pathname;
const html = await fetchText(pathname === "/" ? "example.com" : pathname).then(prettifyHtml);
const body = `<html>
<head>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/code-mirror-web-component@0.0.15/dist/code-mirror.js"
></script>
<script type="module">
document.getElementById("editor").addEventListener("code-change", (e) => {
console.log(e.detail.code);
});
</script>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body style="margin: 0px; height: 100vh;">
<code-mirror id="editor" theme="dracula" language="html" code="${escape(html)}"></code-mirror>
</body>
</html>
`;
return new Response(body, {
headers: {
"Content-Type": "text/html",
},
});
}

Generate QR codes

Generate a QR code for any link instantly!

Example

https://neverstew-generateQR.web.val.run?url=https://neverstew.com

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
export async function generateQR(req: Request) {
let url: URL;
try {
url = new URL(new URL(req.url).searchParams.get("url"));
}
catch {
return new Response(
"<html><body>Not a valid URL - " + new URL(req.url).search +
"</body></html>",
{
headers: {
"Content-Type": "text/html",
},
},
);
}
const { qrcode } = await import("https://deno.land/x/qrcode/mod.ts");
const base64Image = await qrcode(url);
return new Response(
`<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<img src=${base64Image} style="margin: 0 auto; max-width: 600px" width="100%" />
<p style="font-size: 0.85rem">QR Code generated using <a href="https://val.town">val.town</a></p>
<p style="font-size: 0.85rem">Built with &#128151; by <a href="https://twitter.com/neverstew">Neverstew</a></p>
</body>
</html>`,
{ headers: { "Content-Type": "text/html" } },
);
}
// Forked from @ramkarthik.GenerateQR
// Forked from @galligan.generateQR

This val allows you to send me an email at a certain datetime. Fun!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { emailServiceCreate } from "https://esm.town/v/neverstew/emailServiceCreate";
export let scheduleEmail = async (req: express.Request, res: express.Response) => {
const z = await import("npm:zod");
const params = z.object({
sendAt: z.string().datetime(),
content: z.string(),
});
const sendAt = req.query.sendAt;
const content = req.query.content;
const parsedParams = params.safeParse({ sendAt, content });
if (parsedParams.success) {
await emailServiceCreate(parsedParams.data);
res.send(`Scheduled for ${sendAt}`);
}
else {
res.send(JSON.stringify(parsedParams.error.format()));
}
};

List things in a blob directory

Blobs can be organised using "directories" e.g.

/animals
  all-animals.txt
  /dogs
    sausage.txt
  /cats
    tom.txt

is really only three files: /animals/all-animals.txt, /animals/dogs/sausage.txt and /animals/cats/tom.txt because directories don't really exist, we're just making longer filenames.

When you want to list things only "in a directory" and none of the child "directories", you can use this val.

Create valimport { blobDirList } from "https://esm.town/v/neverstew/blobDirList"; console.log(await blobDirList("/animals")); // returns only "/animals/all-animals.txt"
1
2
3
4
5
6
7
8
9
10
import { dirname } from "https://deno.land/std@0.223.0/path/mod.ts";
import { blob } from "https://esm.town/v/std/blob?v=12";
type BlobMetadata = Awaited<ReturnType<typeof blob.list>>[0];
export const inDir = (directory: string) => (blobMeta: BlobMetadata) => blobMeta => dirname(blobMeta.key) === directory;
export async function blobDirList(directory: string) {
const list = await blob.list(directory);
return list.filter(inDir(directory));
}
1
2
3
4
import prettierPluginHtml from "https://unpkg.com/prettier@3.2.5/plugins/html.mjs";
import * as prettier from "npm:prettier/standalone.mjs";
export const prettifyHtml = (html: string) => prettier.format(html, { parser: "html", plugins: [prettierPluginHtml] });
1
2
3
export function sleep(milliseconds = 3000) {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}

inTheBackground

With the addition of the "early return" feature of web handlers, you can now process short background tasks in vals. This can be really useful for occasions where an immediate response is required, with a subsequent update a few seconds later

e.g. a Discord bot that calls ChatGPT needs to respond within a few seconds, which can be too fast for the AI to generate a response. We can instead reply immediately and then update that message later, inTheBackground

Simply wrap something in inTheBackground and it will do just that! In this example, we log something a few seconds later than the web response is sent back.

1
2
3
4
5
6
7
8
9
10
import { inTheBackground } from "https://esm.town/v/neverstew/inTheBackground";
import { sleep } from "https://esm.town/v/neverstew/sleep";
export default async function(req: Request): Promise<Response> {
inTheBackground(async () => {
await sleep(3000);
console.info(new Date());
});
return Response.json({ time: new Date() });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { email } from "https://esm.town/v/std/email?v=9";
import { PDFDocument } from "npm:pdf-lib";
export let sendPDF = (async () => {
// PDF Creation
const pdfDoc = await PDFDocument.create();
const page = pdfDoc.addPage();
page.drawText("You can create PDFs!");
const content = await pdfDoc.saveAsBase64();
return email({
text: "here's a pdf",
attachments: [
{
content,
filename: "test.pdf",
type: "application/pdf",
disposition: "attachment",
},
],
});
})();
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 { set } from "https://esm.town/v/std/set?v=11";
import { email } from "https://esm.town/v/std/email?v=9";
let { submittedEmailAddresses } = await import("https://esm.town/v/neverstew/submittedEmailAddresses");
export const renderFormAndSaveData = async (req: Request) => {
// A visit from a web browser? Serve a HTML page with a form
if (req.method === "GET") {
return new Response(
`
<!DOCTYPE html>
<html>
<head>
<title>Email Form</title>
</head>
<body>
<!-- Change the action here to THIS val's Express endpoint! -->
<form action="https://neverstew-renderFormAndSaveData.web.val.run" method="post">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<br>
<input type="submit" value="Submit">
</form>
</body>
</html>
`,
{ headers: { "Content-Type": "text/html" } },
);
}
// Otherwise, someone has submitted a form so let's handle that
if (submittedEmailAddresses === undefined) {
submittedEmailAddresses = [];
}
const formData = await req.formData();
const emailAddress = formData.get("email") as string;
if (submittedEmailAddresses.includes(emailAddress)) {
return new Response("you're already signed up!");
}
await email({
text: `${emailAddress} just signed up!`,
subject: "new sign up",
});
submittedEmailAddresses.push(emailAddress);
await set(
"submittedEmailAddresses",
submittedEmailAddresses,
);
return new Response("thanks! you're signed up!");
};