Test Explorer

3c584b1ccdd2115d031f49d69f2ac68ea33c450bbde9fdd462036dc2cbf8e907.png

Click on the play button next to list items to run them.

Usage

  1. Fork this val

  2. Create new tests in any of vals (and export them) (see @pomdtr/example_test)

Create valimport { assertEquals } from "https://deno.land/std@0.216.0/assert/mod.ts"; import { Test } from "https://esm.town/v/<account>/test_explorer"; export const exampleTestSuccess = new Test(() => { assertEquals(1 + 1, 2); }); export const exampleTestFailure = new Test(() => { assertEquals(1 + 1, 3); });
  1. Go to https://<account>-test_explorer.web.val.run to run your test
    • click on the val name to go to the val the tests are originating from
    • click on the test name to run it

ℹ️ You probably want to protect your test explorer behind an authentication middleware like @pomdtr/basicAuth

Discovery mechanism

In order to define a test, the user need to import the Test class from https://val.town/v/<account>/Test. So we can use the api to search for vals containing the https://val.town/v/<account>/Test string to locate the vals containing tests.

Next, we need to extract the tests from the val exports. We use exported instanceof Test to filter them (at some point we will probably use static analysis for this).

TODO

  • persist test results in sqlite
  • Improve styling (help welcome ❤️)
  • View logs in a modal
  • Batch http requests
1
2
3
4
5
import { router } from "https://esm.town/v/pomdtr/test_explorer_router";
export { Test } from "https://esm.town/v/pomdtr/test";
export default router.fetch;

SQLite Admin

This is a lightweight SQLite Admin interface to view and debug your SQLite data.

Screenshot 2023-12-08 at 13.35.04.gif

It's currently super limited (no pagination, editing data, data-type specific viewers), and is just a couple dozens lines of code over a couple different vals. Forks encouraged! Just comment on the val if you add any features that you want to share.

To use it on your own Val Town SQLite database, fork it to your account.

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

1
2
3
4
5
6
7
8
9
10
11
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth";
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { html } from "https://esm.town/v/stevekrouse/html";
import { sqlite_admin_table } from "https://esm.town/v/stevekrouse/sqlite_admin_table";
import { sqlite_admin_tables } from "https://esm.town/v/stevekrouse/sqlite_admin_tables";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await sqlite_admin_tables()));
app.get("/:table", async (c) => c.html(await sqlite_admin_table(c.req.param("table"))));
export default basicAuth(app.fetch);

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

b7321ca2cd80899250589b9aa08bc3cae9c7cea276282561194e7fc537259b46.png

Use this button to install the val:

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

TODO

  • /new - render a page to write a new blob key and value
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
  • /delete/:blob - delete a blob and render success
  • add upload/download buttons
  • Use modals for create/upload/edit/view/delete page (htmx ?)
  • handle non-textual blobs properly
  • use codemirror instead of a textarea for editing text blobs
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono@4.0.8/jsx **/
import view_route from "https://esm.town/v/pomdtr/blob_admin_blob";
import create_route from "https://esm.town/v/pomdtr/blob_admin_create";
import delete_route from "https://esm.town/v/pomdtr/blob_admin_delete";
import edit_route from "https://esm.town/v/pomdtr/blob_admin_edit";
import upload_route from "https://esm.town/v/pomdtr/blob_admin_upload";
import { passwordAuth } from "https://esm.town/v/pomdtr/password_auth?v=74";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { Hono } from "npm:hono@4.0.8";
import { jsxRenderer } from "npm:hono@4.0.8/jsx-renderer";
const app = new Hono();
app.use(
jsxRenderer(({ children }) => {
return (
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/code-mirror-web-component@0.0.8/dist/code-mirror.js"
>
</script>
<title>Blob Admin</title>
</head>
<body>
<main class="container">
{children}
</main>
</body>
</html>
);
}),
);
app.get("/", async (c) => {
let blobs = await blob.list();
return c.render(
<div class="overflow-auto">
<h1>Blob Admin</h1>
<section
style={{
display: "flex",
gap: "0.5em",
}}
>
<a href="/create">New Blob</a>
<a href="/upload">Upload Blob</a>
</section>
<section>
<table>
<thead>
<tr>
<th>Name</th>
<th>Size (kb)</th>
<th>Last Modified</th>
<th
style={{
textAlign: "center",
}}
>
Edit
</th>
<th
style={{
textAlign: "center",
}}
>
Delete
</th>
<th
style={{
textAlign: "center",
}}
>
Download
</th>
</tr>
</thead>
{blobs.map(b => (
<tr>
<td>
<a href={`/view/${encodeURIComponent(b.key)}`}>
{b.key}
</a>
</td>
<td>{b.size / 1000}</td>
<td>{new Date(b.lastModified).toLocaleString()}</td>
<td
style={{
textAlign: "center",
}}
>
<a href={`/edit/${encodeURIComponent(b.key)}`}>✍️</a>

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 (v81) by forking this val:

Install Stable Release (v81)

Authentication

Login to your SQLite Explorer with password authentication with your Val Town API Token as the password.

Todos / Plans

  • improve error handling
  • improve table formatting
  • sticky table headers
  • 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 refresh to table list sidebar after CREATE/DROP/ALTER statements
  • add automatic execution of initial select query on double click
  • add views to the sidebar
  • add triggers to sidebar
  • add upload from SQL, CSV and JSON
  • add ability to connect to a non-val town Turso database
  • fix wonky sidebar separator height problem (thanks to @stevekrouse)
  • make result tables scrollable
  • add export to CSV, and JSON (CSV and JSON helper functions written in this val. Thanks to @pomdtr for merging the initial version!)
  • add listener for cmd+enter to submit query
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono@latest/jsx **/
import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50";
import { iframeHandler } from "https://esm.town/v/nbbaier/iframeHandler";
import { resetStyle } from "https://esm.town/v/nbbaier/resetStyle";
import { sqlToCSV, sqlToJSON } from "https://esm.town/v/nbbaier/sqliteExportHelpers";
import {
EditorSection,
MockTable,
Separator,
Table,
TablesList,
} from "https://esm.town/v/nbbaier/sqliteStudioComponents";
import { sqliteStyle } from "https://esm.town/v/nbbaier/sqliteStyle";
import { passwordAuth } from "https://esm.town/v/pomdtr/password_auth?v=70";
import { ResultSet, sqlite } from "https://esm.town/v/std/sqlite";
import { reloadOnSaveFetchMiddleware } from "https://esm.town/v/stevekrouse/reloadOnSave";
import { Hono } from "npm:hono";
import type { FC } from "npm:hono/jsx";
import { jsxRenderer } from "npm:hono/jsx-renderer";
import papa from "npm:papaparse";
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" />
<script
type="module"
src="https://raw.esm.sh/code-mirror-web-component@0.0.20/dist/code-mirror.js"
>
</script>
</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>
<script type="module" src="https://esm.town/v/nbbaier/downloadScript" />
<script type="module" src="https://esm.town/v/nbbaier/enableButtonsScript" />
<script type="module" src="https://esm.town/v/nbbaier/getCodeScript" />
</body>
</html>
);
};
const app = new Hono();
app.use(
"*",
jsxRenderer(
({ children }) => {
return <HTML children={children} />;
},
{ docType: false },
),
);
app.get("/", async (c) => {
let data = await sqlite.batch(
[
`SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%';`,
`SELECT name FROM sqlite_schema WHERE type ='view' AND name NOT LIKE 'sqlite_%';`,
],
);
let views = data[1].rows.map(view => {
return { type: "view", name: view[0] };
});
let tables = data[0].rows.map(view => {
return { type: "table", name: view[0] };
});
return c.render(
<main class="sidebar-layout">
<div class="sidebar">
<TablesList tables={[...tables, ...views]}></TablesList>

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

b7321ca2cd80899250589b9aa08bc3cae9c7cea276282561194e7fc537259b46.png

Use this button to install the val:

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

TODO

  • /new - render a page to write a new blob key and value
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
  • /delete/:blob - delete a blob and render success
  • add upload/download buttons
  • Use modals for create/upload/edit/view/delete page (htmx ?)
  • handle non-textual blobs properly
  • use codemirror instead of a textarea for editing text blobs
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono@4.0.8/jsx **/
import { modifyFetchHandler } from "https://esm.town/v/andreterron/codeOnValTown?v=50";
import view_route from "https://esm.town/v/pomdtr/blob_admin_blob";
import create_route from "https://esm.town/v/pomdtr/blob_admin_create";
import delete_route from "https://esm.town/v/pomdtr/blob_admin_delete";
import edit_route from "https://esm.town/v/pomdtr/blob_admin_edit";
import upload_route from "https://esm.town/v/pomdtr/blob_admin_upload";
import { passwordAuth } from "https://esm.town/v/pomdtr/password_auth?v=74";
import { blob } from "https://esm.town/v/std/blob?v=11";
import { Hono } from "npm:hono@4.0.8";
import { jsxRenderer } from "npm:hono@4.0.8/jsx-renderer";
const app = new Hono();
app.use(
jsxRenderer(({ children }) => {
return (
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"
/>
<script
type="module"
src="https://cdn.jsdelivr.net/npm/code-mirror-web-component@0.0.8/dist/code-mirror.js"
>
</script>
<title>Blob Admin</title>
</head>
<body>
<main class="container">
{children}
</main>
</body>
</html>
);
}),
);
app.get("/", async (c) => {
let blobs = await blob.list();
return c.render(
<div class="overflow-auto">
<h1>Blob Admin</h1>
<section
style={{
display: "flex",
gap: "0.5em",
}}
>
<a href="/create">New Blob</a>
<a href="/upload">Upload Blob</a>
</section>
<section>
<table>
<thead>
<tr>
<th>Name</th>
<th>Size (kb)</th>
<th>Last Modified</th>
<th
style={{
textAlign: "center",
}}
>
Edit
</th>
<th
style={{
textAlign: "center",
}}
>
Delete
</th>
<th
style={{
textAlign: "center",
}}
>
Download
</th>
</tr>
</thead>
{blobs.map(b => (
<tr>
<td>
<a href={`/view/${encodeURIComponent(b.key)}`}>
{b.key}
</a>
</td>
<td>{b.size / 1000}</td>
<td>{new Date(b.lastModified).toLocaleString()}</td>
<td
style={{
textAlign: "center",
}}
>

Static Vals

Serve static content from val.town

Usage

First, fork this val to get your own http endpoint.

Then create a val that uses a string as it's default export, or a single string export. The val must be either public or unlisted.

export default `<static content>`

You can then fetch the exported string from outside val.town using:

curl 'https://<owner>-static.web.val.run/<val>.<extension>'

The Content-Type will be dynamically set depending on the provided extension.

Example

https://pomdtr-static.web.val.run/val_town_readme_style.css

Val Link

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
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { gfm } from "https://esm.town/v/pomdtr/gfm";
import { readme } from "https://esm.town/v/pomdtr/readme";
import { Hono } from "npm:hono";
import { HTTPException } from "npm:hono/http-exception";
import mime from "npm:mime";
const app = new Hono();
const { author, name } = extractValInfo(import.meta.url);
async function extractStaticContent(code: string) {
const blob = new Blob([code], { type: "text/javascript" });
const url = URL.createObjectURL(blob);
const exports = await import(url);
if (exports.default) {
return exports.default;
}
const values = Object.values(exports);
if (values.length == 1) {
return values[0];
}
return "";
}
app.get("/", async c => {
const html = await gfm(await readme(author, name));
return c.html(html);
});
app.get("/:filename", async c => {
const { filename } = c.req.param();
const [name, ext] = filename.split(".");
// we check that we are not exposing private vals
const resp = await fetch(`https://api.val.town/v1/alias/${author}/${name}`, {
headers: {
authorization: `Bearer ${Deno.env.get("valtown")}`,
},
});
if (!resp.ok) {
return resp;
}
const { code, privacy } = await resp.json();
if (privacy == "private") {
throw new HTTPException(404);
}
const content = await extractStaticContent(code);
if (typeof content !== "string") {
throw new HTTPException(500, { message: `val @${author}/${name} is not a static val: ${content}` });
}
return new Response(content, {
headers: {
"Content-Type": mime.getType(ext),
},
});
});
export default app.fetch;
1
2
3
4
5
6
7
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth";
function handler(req: Request) {
return new Response("You are authenticated!");
}
export default basicAuth(handler);

SQLite Admin

This is a lightweight SQLite Admin interface to view and debug your SQLite data.

Screenshot 2023-12-08 at 13.35.04.gif

It's currently super limited (no pagination, editing data, data-type specific viewers), and is just a couple dozens lines of code over a couple different vals. Forks encouraged! Just comment on the val if you add any features that you want to share.

To use it on your own Val Town SQLite database, fork it to your account.

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

1
2
3
4
5
6
7
8
9
10
11
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth";
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { html } from "https://esm.town/v/stevekrouse/html";
import { sqlite_admin_table } from "https://esm.town/v/stevekrouse/sqlite_admin_table";
import { sqlite_admin_tables } from "https://esm.town/v/stevekrouse/sqlite_admin_tables";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await sqlite_admin_tables()));
app.get("/:table", async (c) => c.html(await sqlite_admin_table(c.req.param("table"))));
export default basicAuth(app.fetch);

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 (v64) by forking this val that imports & exports it in your account:

Install v64

Authentication

Login to your SQLite Explorer with password authentication with your Val Town API Token as the password.

Todos / Plans

  • fix wonky sidebar separator height problem (thanks to @stevekrouse)
  • improve error handling
  • improve table formatting
  • make result tables scrollable
  • sticky table headers
  • 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 CSV, and JSON (CSV and JSON helper functions written in this val. Thanks to @pomdtr for merging the initial version!)
  • 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
  • add triggers to sidebar
  • add upload from SQL, CSV and JSON
  • add ability to connect to a non-val town Turso database
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono@latest/jsx **/
import { resetStyle } from "https://esm.town/v/nbbaier/resetStyle";
import { sqlToCSV, sqlToJSON } from "https://esm.town/v/nbbaier/sqliteExportHelpers";
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 { passwordAuth } from "https://esm.town/v/pomdtr/password_auth?v=70";
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>
<script type="module" src="https://esm.town/v/nbbaier/downloadScript" />
<script type="module" src="https://esm.town/v/nbbaier/enableButtonsScript" />
</body>
</html>
);
};
const app = new Hono();
app.use(
"*",
jsxRenderer(
({ children }) => {
return <HTML children={children} />;
},
{ docType: false },
),
);
app.get("/", async (c) => {
let data = await sqlite.execute(`
SELECT
name
FROM
sqlite_schema
WHERE
type ='table' AND
name NOT LIKE 'sqlite_%';`);
let tables = data.rows;
return c.render(
<main class="sidebar-layout">
<div class="sidebar">
<TablesList tables={tables}></TablesList>
</div>
<Separator direction="horizontal"></Separator>
<div class="not-sidebar">
<EditorSection />
<Separator direction="vertical" isResizer={true}></Separator>
<div class="bottom-container">
<div id="results-table" class="bottom">
</div>
</div>
</div>
</main>,
);
});
app.post("/query", async (c) => {
const query = (await c.req.parseBody()).query as string;
try {

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 CSV, and JSON (CSV and JSON helper functions written in this val)
  • add export to sqlite
  • 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
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/hono@latest/jsx **/
import { resetStyle } from "https://esm.town/v/nbbaier/resetStyle";
import { sqlToCSV, sqlToJSON } from "https://esm.town/v/nbbaier/sqliteExportHelpers?v=4";
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>
);
};
const app = new Hono();
app.use(
"*",
jsxRenderer(
({ children }) => {
return <HTML children={children} />;
},
{ docType: false },
),
);
app.get("/", async (c) => {
let data = await sqlite.execute(`
SELECT
name
FROM
sqlite_schema
WHERE
type ='table' AND
name NOT LIKE 'sqlite_%';`);
let tables = data.rows;
return c.render(
<main class="sidebar-layout">
<div class="sidebar">
<TablesList tables={tables}></TablesList>
</div>
<Separator direction="horizontal"></Separator>
<div class="not-sidebar">
<EditorSection />
<Separator direction="vertical" isResizer={true}></Separator>
<div class="bottom-container">
<div id="results-table" class="bottom">
</div>
</div>
</div>
</main>,
);
});
app.post("/query", async (c) => {
const query = (await c.req.parseBody()).query as string;
try {
console.log(`attempting:`, query);
const rs = await sqlite.execute(query);

SQLite Admin

This is a lightweight SQLite Admin interface to view and debug your SQLite data.

Screenshot 2023-12-08 at 13.35.04.gif

It's currently super limited (no pagination, editing data, data-type specific viewers), and is just a couple dozens lines of code over a couple different vals. Forks encouraged! Just comment on the val if you add any features that you want to share.

To use it on your own Val Town SQLite database, fork it to your account.

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

1
2
3
4
5
6
7
8
9
10
11
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth?v=38";
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { html } from "https://esm.town/v/stevekrouse/html";
import { sqlite_admin_table } from "https://esm.town/v/stevekrouse/sqlite_admin_table";
import { sqlite_admin_tables } from "https://esm.town/v/stevekrouse/sqlite_admin_tables";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await sqlite_admin_tables()));
app.get("/:table", async (c) => c.html(await sqlite_admin_table(c.req.param("table"))));
export default basicAuth(app.fetch);

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

Screenshot 2023-12-13 at 12.51.53.png

To use it on your own Val Town Blob Storage, fork it to your account.

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

TODO

  • /new - render a page to write a new blob key and value
    • or new blob by file upload
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
    • json validation when the existing content is json
      • checkbox to disable that
  • /delete/:blob - delete a blob and render success
1
2
3
4
5
6
7
8
9
10
11
12
13
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth?v=38";
import { blob_admin_blob } from "https://esm.town/v/stevekrouse/blob_admin_blob";
import { blob_admin_home } from "https://esm.town/v/stevekrouse/blob_admin_home";
import { html } from "https://esm.town/v/stevekrouse/html";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await blob_admin_home()));
app.get("/:blob", async (c) => c.html(await blob_admin_blob(c.req.param("blob"))));
app.get("/new", (c) => c.html("TODO: New Blob"));
app.get("/edit/:blob", async (c) => c.html(`TODO: Edit ${c.req.param("blob")}`));
app.get("/delete/:blob", async (c) => c.html(`TODO: Delete ${c.req.param("blob")}`));
export default basicAuth(app.fetch);

SQLite Admin

This is a lightweight SQLite Admin interface to view and debug your SQLite data.

Screenshot 2023-12-08 at 13.35.04.gif

It's currently super limited (no pagination, editing data, data-type specific viewers), and is just a couple dozens lines of code over a couple different vals. Forks encouraged! Just comment on the val if you add any features that you want to share.

To use it on your own Val Town SQLite database, fork it to your account.

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

1
2
3
4
5
6
7
8
9
10
11
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth?v=38";
import { sqlite } from "https://esm.town/v/std/sqlite?v=4";
import { html } from "https://esm.town/v/stevekrouse/html";
import { sqlite_admin_table } from "https://esm.town/v/stevekrouse/sqlite_admin_table";
import { sqlite_admin_tables } from "https://esm.town/v/stevekrouse/sqlite_admin_tables";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await sqlite_admin_tables()));
app.get("/:table", async (c) => c.html(await sqlite_admin_table(c.req.param("table"))));
export default basicAuth(app.fetch);

Blob Admin

This is a lightweight Blob Admin interface to view and debug your Blob data.

Screenshot 2023-12-13 at 12.51.53.png

To use it on your own Val Town Blob Storage, fork it to your account.

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

TODO

  • /new - render a page to write a new blob key and value
    • or new blob by file upload
  • /edit/:blob - render a page to edit a blob (prefilled with the existing content)
    • json validation when the existing content is json
      • checkbox to disable that
  • /delete/:blob - delete a blob and render success
1
2
3
4
5
6
7
8
9
10
11
12
13
import { basicAuth } from "https://esm.town/v/pomdtr/basicAuth";
import { blob_admin_blob } from "https://esm.town/v/stevekrouse/blob_admin_blob";
import { blob_admin_home } from "https://esm.town/v/stevekrouse/blob_admin_home";
import { html } from "https://esm.town/v/stevekrouse/html";
import { Hono } from "npm:hono@3.9.2";
const app = new Hono();
app.get("/", async (c) => c.html(await blob_admin_home()));
app.get("/new", (c) => c.html("TODO: New Blob"));
app.get("/:blob", async (c) => c.html(await blob_admin_blob(c.req.param("blob"))));
app.get("/edit/:blob", async (c) => c.html(`TODO: Edit ${c.req.param("blob")}`));
app.get("/delete/:blob", async (c) => c.html(`TODO: Delete ${c.req.param("blob")}`));
export default basicAuth(app.fetch);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { isAuthenticated } from "https://esm.town/v/pomdtr/basicAuth";
export default async function(req: Request): Promise<Response> {
if (!await isAuthenticated(req)) {
return new Response("Unauthorized", {
status: 401,
headers: {
"WWW-Authenticate": "Basic",
},
});
}
return new Response("Well played!");
}