Back to APIs list

Github API examples & templates

Use these vals as a playground to view and fork Github API examples and templates on Val Town. Run any example below or find templates that can be used as a pre-built solution.

"Code on Val Town" Ribbon HTML Element

Ribbon element used by @andreterron/codeOnValTown

Usage

  • ribbonElement({ handle: "andre", name: "foo" }) - define which val to link to;
  • ribbonElement() - infer the val from the call stack.

Example: @andreterron/openable_element

import { ribbonElement } from "https://esm.town/v/andreterron/codeOnVT_ribbonElement";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
    ${ribbonElement()}
  `);
};
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
import { rootValRef } from "https://esm.town/v/andreterron/rootValRef";
import { ValRef } from "https://esm.town/v/andreterron/ValRef";
export function ribbonElement(val?: ValRef) {
const valRef = val?.handle && val?.name ? val : rootValRef();
if (!valRef) {
console.error("Failed to infer val. Please set the val parameter to the desired `{ handle, name }`");
return "";
}
const valSlug = `${valRef.handle}/${valRef.name}`;
return `
<div id="code-on-vt-host">
<template shadowrootmode="open">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css"
/>
<style>
@media (max-width: 768px) {
.github-fork-ribbon {
display: none !important;
}
}
</style>
<a
href="https://www.val.town/v/${valSlug}"
target="_blank"
class="github-fork-ribbon"
data-ribbon="Code on Val Town"
title="Code on Val Town"
>
Code on Val Town
</a>
</template>
</div>`;
}

Code on Val Town

306790319-d5b13560-7b3a-458a-98c9-1696ef40d972.png

Tell your website visitors about your val! This val is a fetch handler middleware. It inserts a "Code on Val Town" ribbon that links to the val itself. Your website visitors will be able to view your code, fork the val, and suggest changes with pull requests!

Usage

There are 5 different ways to add the "Code on Val Town" ribbon!

1. Use the element string

import { ribbonElement } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
    ${ribbonElement()}
  `);
};

2. Wrap your HTML string

import { htmlString } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return html(htmlString(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
  `));
};

3. Pipe your HTML stream through our injector

// TODO

4. Wrap your response

import { modifyResponse } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default async (req: Request): Promise<Response> => {
  return modifyResponse(html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
  `));
};

5. Wrap your fetch handler

Check out @andreterron/openable for an example!

import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown";
import { html } from "https://esm.town/v/stevekrouse/html?v=5";

export default modifyFetchHandler(async (req: Request): Promise<Response> => {
  return html(`
    <h2>Hello world!</h2>
    <style>* { font-family: sans-serif }</style>
  `);
});
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
import { inferRequestVal } from "https://esm.town/v/andreterron/inferRequestVal";
import { InjectHTMLElementStream } from "https://esm.town/v/andreterron/InjectHTMLElementStream";
import { rootRef } from "https://esm.town/v/andreterron/rootRef";
export function ribbonElement(val?: ValRef) {
const valRef = val?.handle && val?.name ? val : rootValRef();
if (!valRef) {
console.error("Failed to infer val. Please set the val parameter to the desired `{ handle, name }`");
return "";
}
const valSlug = `${valRef.handle}/${valRef.name}`;
return `
<div id="code-on-vt-host">
<template shadowrootmode="open">
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/github-fork-ribbon-css/0.2.3/gh-fork-ribbon.min.css"
/>
<a
href="https://www.val.town/v/${valSlug}"
target="_blank"
class="github-fork-ribbon"
data-ribbon="Code on Val Town"
title="Code on Val Town"
>
Code on Val Town
</a>
</template>
</div>`;
}
/**
* @param bodyText HTML string that will be used to inject the element.
* @param options.val Define which val should open. Defaults to the root reference.
*/
export function htmlString(
bodyText: string,
options: {
val?: {
handle: string;
name: string;
};
} = {},
) {
const val = options.val?.handle && options.val?.name ? options.val : rootValRef();
if (!val) {
console.error("Failed to infer val. Please set the options.val parameter to the desired `{ handle, name }`");

This is a deno/valtown port in progress of https://github.com/tarasglek/scrape2md

License: MIT

Handy script to scrape various data sources into markdown. Intended to feed llms in https://chatcraft.org

Usage: https://taras-scrape2md.web.val.run/$YOUR_URL

TODO

https://chatcraft.org/api/share/tarasglek/IDYChVAilfePgVZb_T5pH POST from browser

Metadata for use with https://github.com/tarasglek/valtown2js:

{
  "typeCheck": false,
  "mappings": {
    "https://esm.sh/linkedom": {
      "name": "linkedom",
      "version": "^0.16.8"
    }
  },
  "package": {
    "name": "scrape2md",
    "version": "1.0.0",
    "devDependencies": {
      "@types/turndown": "^5.0.4"
    }
  }
}
Readme
Fork
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
import { marked } from "https://deno.land/x/marked/mod.ts";
import { DOMParser } from "https://esm.sh/linkedom";
import { isProbablyReaderable, Readability } from "npm:@mozilla/readability@^0.5.0";
import TurndownService from "npm:turndown@^7.1.2";
import { getSubtitles } from "npm:youtube-captions-scraper@^2.0.1";
function getYoutubeVideoID(url: URL): string | null {
const regExp = /(?:youtube\.com\/(?:[^/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?/\s]{11})/i;
const match = url.href.match(regExp);
return match ? match[1] : null;
}
function response(message: string, contentType = "text/markdown"): Response {
const headers = new Headers();
headers.set("Access-Control-Allow-Origin", "*"); // Allow all origins
headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
headers.set("Access-Control-Max-Age", "86400"); // 24 hours
headers.set("Content-Type", contentType);
return new Response(message, {
status: 200,
headers: headers,
});
}
function err(msg: string): Response {
// Create the error message as a JSON string
const errorMessage = JSON.stringify({
error: {
message: msg,
code: 400,
},
});
// Use the response function to create and return the Response object
// with the error message and the "application/json" content type
return response(errorMessage, "application/json");
}
export default async function(req: Request): Promise<Response> {
const myurl = new URL(req.url);
const pathname = myurl.pathname.substring(1) + myurl.search;
if (!pathname.startsWith("http")) {
return new Response(null, {
status: 301,
headers: { "Location": "https://www.val.town/v/taras/scrape2md" },
});
}
const url = new URL(pathname);

Markdown to html (with github styling)

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
import { micromark } from "npm:micromark";
import { frontmatter, frontmatterHtml } from "npm:micromark-extension-frontmatter";
import { gfm as gfmMarkdown, gfmHtml } from "npm:micromark-extension-gfm";
export async function gfm(markdown: string, options?: { title?: string; favicon?: string }) {
const body = await micromark(markdown, {
extensions: [gfmMarkdown(), frontmatter()],
htmlExtensions: [gfmHtml(), frontmatterHtml()],
});
return `<html>
<head>
<title>${options?.title || "Article"}</title>
<link href="https://fav.farm/${options?.favicon || "📝"}" rel="icon" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.4.0/github-markdown.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/themes/prism.css" rel="stylesheet" />
<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}
</style>
</head>
<body class="markdown-body">
${body}
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
</body>
<html/>`;
}

Submit a PR from Val Town

This val provides a (very) thin wrapper around the GH rest API methods for creating a pull request. It handles the creation of a Octokit client for you.

Usage

import { submitPR } from "https://esm.town/v/nbbaier/submitPR";

await submitPR(Deno.env.get("GH_REPO_TOKEN"), {
  owner: "nbbaier",
  repo: "test-ground",
  head: "branch_2",
  base: "main",
  title: "trying another PR",
  body: "cool stuff, take a look",
});

Parameters

The function takes two parameters: your gh access token and an object that's identical to the object submitted to the gh API. See GH's documentation for more info!

Readme
1
2
3
4
5
6
import { Octokit } from "npm:@octokit/rest";
type PR = Parameters<typeof Octokit["rest"]["pulls"]["create"]>;
export async function submitPR(ghToken: string, ...pr: PR) {
return new Octokit({ auth: ghToken }).rest.pulls.create(pr);
}

githubRestClient

Does what it says! Defaults to using GITHUB_TOKEN env var if none is passed to the function.

Readme
1
2
3
4
5
import { Octokit } from "npm:@octokit/rest";
export async function githubRestClient(ghToken: string | undefined) {
return new Octokit({ auth: ghToken || Deno.env.get("GITHUB_TOKEN") }).rest;
}

valToGH

A utility function for programmatically updating a GitHub repository with code retrieved from a Val.

  • Fetches code from a specified Val
  • Commits the fetched code to a specified GitHub repository.
  • Supports TypeScript files (.ts and .tsx).

Note: This function currently only commits to a branch named main. I will be adding functionality for committing to arbitrary branches in the future! (or PR for that!)

Usage

Below is an example snippet demonstrating how to use the valToGH function. This function updates a GitHub repository with the code from a Val repository.

import { valToGH } from 'https://esm.town/v/nbbaier/valToGH';

const repo = "yourGitHubUser/yourRepo";
const val = "valUser/valName";
const ghToken = Deno.eng.get("yourGitHubToken");

const result = await valToGH(repo, val, ghToken);
 
console.log(result);

Parameters:

  • repo: The GitHub repository in the format {user}/{repo}.
  • val: The Val repository in the format {user}/{val}.
  • ghToken: Your GitHub token for authentication (must have write permission to the target repo)
  • prefix (optional): Folder path to prefix to the committed filename. Leave out trailing slash.
  • branch (optional): Branch to commit to. Defaults to main
  • message (optional): The message for the GitHub commit. Defaults to "Created programmatically from val: https://www.val.town/v/${val}"
  • tsx (optional): A boolean indicating whether to treat the file as .tsx (true) or .ts (false).
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
import { fetchJSON } from "https://esm.town/v/stevekrouse/fetchJSON?v=42";
import { Octokit } from "npm:@octokit/rest";
async function getLatestCommit(ghUser: string, ghRepo: string, branch: string, client: Octokit)
{
const { data: refData } = await client.rest.git.getRef({
owner: ghUser,
repo: ghRepo,
ref: `heads/${branch}`,
});
return refData.object.sha;
}
async function createNewTree(
ghUser: string,
ghRepo: string,
path: string,
content: string,
commitSHA: string,
client: Octokit,
) {
const {
data: { sha: currentTreeSHA },
} = await client.rest.git.createTree({
owner: ghUser,
repo: ghRepo,
tree: [
{
path,
content,
mode: "100644",
type: "commit",
},
],
base_tree: commitSHA,
parents: [commitSHA],
});
return currentTreeSHA;
}
async function createNewCommit(
ghUser: string,
ghRepo: string,
commitSHA: string,
currentTreeSHA: string,
message: string,
client: Octokit,
) {
const {
Fork
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
import { fetch } from "https://esm.town/v/std/fetch";
const gist =
"https://gist.githubusercontent.com/rvorias/2af68a5974bdbebba5925bbdba52c07d/raw/4320ade50bcccd020127db24607ca4fac71ff0f6/data.json";
export const queryParams = async (req: Request) => {
const data = await fetch(gist);
const jsonData = await data.json();
const searchParams = new URL(req.url).searchParams;
const params = Object.fromEntries(searchParams.entries());
const id = parseInt(params["id"]) - 1;
const code = jsonData["data"][id];
let ans;
if (code) {
ans = {
"background": jsonData["backgrounds"][code[0]].toLowerCase(),
"armour": jsonData["armours"][code[1]].toLowerCase(),
"jewelry": jsonData["jewelrys"][code[2]].toLowerCase(),
"mask": jsonData["masks"][code[3]].toLowerCase(),
"weapon": jsonData["weapons"][code[4]].toLowerCase(),
};
}
return Response.json(ans);
};
Fork
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
/** @jsxImportSource npm:hono/jsx */
import { Hono } from "npm:hono";
import { FC } from "npm:hono/jsx";
const app = new Hono();
const StyleTag: FC = ({ css }) => <style dangerouslySetInnerHTML={{ __html: css }}>{}</style>;
const css = `body {
font-family: system-ui, sans-serif;
}
button {
border-radius: .5em;
padding: .25em;
font-size: 1.5em;
line-height: 1;
}`;
app.get("/", c => {
return c.html(
<html>
<head>
<meta charset="utf-8" />
<script
type="module"
src="https://unpkg.com/@github/task-lists-element@latest"
>
</script>
<title>task-lists-element demo</title>
</head>
<body>
<task-lists sortable>
<ul class="contains-task-list">
<li class="task-list-item">
<label>
<input type="checkbox" class="task-list-item-checkbox" />
Hubot
</label>
</li>
<li class="task-list-item">
<label>
<input type="checkbox" class="task-list-item-checkbox" />
Bender
</label>
</li>
</ul>
<ul>

SQLite Explorer

View and interact with your Val Town SQLite data. It's based off Steve's excellent SQLite Admin val, adding the ability to run SQLite queries directly in the interface. This new version has a revised UI and that's heavily inspired by LibSQL Studio by invisal. This is now more an SPA, with tables, queries and results showing up on the same page.

image.webp

Install

Install the latest stable version (v37) by forking this val that imports & exports it in your account:

Install v37

Authentication

SQLite Explorer basic authentication with your Val Town API Token as the password (leave the username field blank).

Todos / Plans

  • fix wonky sidebar separator height problem
  • improve error handling
  • improve table formatting
  • make result tables scrollable
  • add codemirror
  • add loading indication to the run button (initial version shipped)
  • add ability to favorite queries
  • add saving of last query run for a table (started)
  • add visible output for non-query statements
  • add schema viewing
  • add export to SQL, CSV, and JSON (CSV and JSON helper functions written in this val)
  • add refresh to table list sidebar after CREATE/DROP/ALTER statements
  • add automatic execution of initial select query on double click
  • add listener for cmd+enter to submit query
  • add views to the sidebar
Readme
Fork
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
/** @jsxImportSource https://esm.sh/hono@latest/jsx **/
import { resetStyle } from "https://esm.town/v/nbbaier/resetStyle";
import {
EditorSection,
MockTable,
Separator,
Table,
TablesList,
} from "https://esm.town/v/nbbaier/sqliteStudioComponents";
import { RunSVG, TableSVG } from "https://esm.town/v/nbbaier/sqliteStudioSVGs";
import { sqliteStyle } from "https://esm.town/v/nbbaier/sqliteStyle";
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth";
import { sqlite } from "https://esm.town/v/std/sqlite";
import { Hono } from "npm:hono";
import type { FC } from "npm:hono/jsx";
import { jsxRenderer } from "npm:hono/jsx-renderer";
const HTML: FC = ({ children }) => {
return (
<html>
<head>
<title>SQLite Explorer</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@300..700&family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap"
rel="stylesheet"
/>
<style
dangerouslySetInnerHTML={{ __html: resetStyle }}
/>
<style
dangerouslySetInnerHTML={{ __html: sqliteStyle }}
/>
<script src="https://unpkg.com/htmx.org@1.9.9/dist/htmx.min.js"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<script type="module" src="https://esm.town/v/nbbaier/resizeScript" />
<script type="module" src="https://esm.town/v/nbbaier/tableSelectScript" />
</head>
<body _="
on keydown[event.metaKey and key is 'Enter'] log 'command + enter' then send submitForm to #sql-editor
">
<div class="root-container">
<header>
<h1>sqlite explorer</h1>
</header>
{children}
</div>
</body>
</html>

dark greetings cryptoadventurers. This val will print the contents of a given Ethereum wallet's Synthetic Loot, which is procedurally generated from your wallet address. To look at your sLoot in a browser with some fun pixel art, check out timshel's Synthetic Loot Viewer

to use this endpoint, pass ?address=0x... e.g. https://jamiedubs-syntheticloot.web.val.run/?account=0xf296178d553c8ec21a2fbd2c5dda8ca9ac905a00

the default response type is JSON. You can also get a simple list of the loot bag contents using ?format=text. e.g. https://jamiedubs-syntheticloot.web.val.run/?account=0xf296178d553c8ec21a2fbd2c5dda8ca9ac905a00&format=text

Readme
Fork
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
import { fetch } from "https://esm.town/v/std/fetch";
import process from "node:process";
import { DOMParser } from "https://deno.land/x/deno_dom/deno-dom-wasm.ts";
// I <3 ether.actor, very convenient for fetching on-chain data
// it's a little slow - Alchemy or another RPC/API provider would be faster
const url = `https://ether.actor/0x869ad3dfb0f9acb9094ba85228008981be6dbdde/tokenURI`;
// example data:
// const account = "0xf296178d553c8ec21a2fbd2c5dda8ca9ac905a00";
// const svg = `<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: white; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="black" /><text x="10" y="20"
async function fetchAndParseSvgFromJson(account: string) {
try {
const response = await fetch(`${url}/${account}`);
const dataUri = await response.text();
// Decode the JSON from the base64 encoded dataURI
const base64Json = dataUri.split(",")[1];
const decodedJsonString = atob(base64Json);
const json = JSON.parse(decodedJsonString);
console.log({ json });
// Extract the SVG dataURI from the JSON's `image` field
const svgDataUri = json.image;
const base64Svg = svgDataUri.split(",")[1];
const decodedSvg = atob(base64Svg);
console.log(decodedSvg);
return decodedSvg; // or manipulate as needed
} catch (error) {
console.error("Error fetching or decoding SVG from JSON:", error);
}
}
type SvgTextElement = {
content: string;
x: string;
y: string;
class: string;
};
function parseElementsFromSvg(svgString: string): SvgTextElement[] {
const parser = new DOMParser();
// deno-dom only supports HTML
// https://deno.land/x/deno_dom@v0.1.45
// const doc = parser.parseFromString(svgString, "image/svg+xml");
const doc = parser.parseFromString(svgString, "text/html");
const elements = doc.querySelectorAll("text");
1
2
3
4
5
import toml from "https://ejm.sh/raw.githubusercontent.com/denoland/deno/3b6b75bb46840a897a310dfd3fcbbd05618f3c5b/core/Cargo.toml" assert {
type: "json",
};
console.log(toml);
1
2
3
4
5
import toml from "https://ejm.sh/raw.githubusercontent.com/denoland/deno/3b6b75bb46840a897a310dfd3fcbbd05618f3c5b/core/Cargo.toml" assert {
type: "json",
};
console.log(toml);
Fork
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
import { z } from "npm:zod";
const paramsSchema = z.object({
slug: z.coerce.string(),
estimatedProb: z.coerce.number().min(0).max(1),
bankroll: z.coerce.number().min(0),
deferenceFactor: z.coerce.number().min(0).max(1).optional().default(0.5),
});
// Copied from https://github.com/Will-Howard/manifolio/blob/master/manifolio-ui/lib/calculate.ts
function calculateNaiveKellyBet({
marketProb,
estimatedProb,
deferenceFactor,
bankroll,
}: { marketProb: number; estimatedProb: number; deferenceFactor: number; bankroll: number }): {
amount: number;
outcome: "YES" | "NO";
} {
const outcome = estimatedProb > marketProb ? "YES" : "NO";
const marketWinProb = outcome === "YES" ? marketProb : 1 - marketProb;
// kellyFraction * ((p - p_market) / (1 - p_market))
const fraction = deferenceFactor
* (Math.abs(estimatedProb - marketProb) / (1 - marketWinProb));
// clamp fraction between 0 and 1
const clampedFraction = Math.min(Math.max(fraction, 0), 1);
const amount = bankroll * clampedFraction;
return {
amount,
outcome,
};
}
async function getMarket(slug: string) {
const res = await fetch(`https://api.manifold.markets/v0/slug/${slug}`);
if (!res.ok) {
const body = await res.text();
throw new Error(body ?? "Error fetching market");
}
return res.json();
}
export default async function(req: Request): Promise<Response> {
const searchParams = new URL(req.url).searchParams;
const params = paramsSchema.parse(Object.fromEntries(searchParams.entries())) as z.infer<typeof paramsSchema>;
const market = await getMarket(params.slug);

This val is supposed to be used with the val.town extension. See the extension readme for installation instructions.

Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { Base64 } from "npm:js-base64@3.7.5";
import { BrowserContext } from "https://esm.town/v/pomdtr/browser";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
export default async function(ctx: BrowserContext<{ author: string; name: string }>) {
const { author, name } = ctx.params;
const title = `@${author}/${name}`;
const resp = await fetch(`https://api.val.town/v1/alias/${author}/${name}`, {
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
});
if (!resp.ok) {
throw new Error(await resp.text());
}
const { code } = await resp.json();
const hash = `#language=tsx&title=${title}&code=${Base64.encodeURI(code)}`;
return { type: "open", url: `https://ray.so${hash}` };
}
// #web