Avatar

postpostscript

he/him
57 public vals
Joined February 22, 2024

@postpostscript/MyFooter: my footer component which shares random vals I've liked!

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
import { rootValRef } from "https://esm.town/v/andreterron/rootValRef";
import { extractValInfo } from "https://esm.town/v/pomdtr/extractValInfo";
import { html, htmlResponse } from "https://esm.town/v/postpostscript/html";
import { recommends } from "https://esm.town/v/postpostscript/recommends";
import { valTownLogoAuto } from "https://esm.town/v/postpostscript/valTownLogo";
const USERNAME = extractValInfo(import.meta.url).author;
export async function MyFooter(logo = valTownLogoAuto) {
const recommendation = rootValRef().handle === USERNAME
? html`<span class="recommends">${await recommends()}</span>`
: "";
return html`
<footer>
Made by
<a href="https://val.town/u/${USERNAME}" target="_blank">@${USERNAME}</a>
for
<a href="https://val.town/" target="_blank">
${logo}
</a>
${recommendation}
</footer>
<style>
body {
min-height: calc(100vh - 1.5rem);
display: flex;
flex-direction: column;
}
footer {
margin-top: auto !important;
}
</style>
`;
}
export default async function(req) {
const { Layout } = await import("https://esm.town/v/postpostscript/Layout");
return htmlResponse`${Layout`
<h1>Footer Demo</h1>
<p>
Fork this if you want to share random vals from your likes in your projects!
</p>
${await MyFooter()}
`}`;
}

moduleHighlightValueLink: Link to a Val With a Value or Method's Code Highlighted

Examples:

Create valimport { moduleHighlightValueLink, getRedirectUrl } from "https://esm.town/v/postpostscript/moduleHighlightValueLink"; console.log(await moduleHighlightValueLink("@std/email", "email")) // https://val.town/v/std/email#L6-42 console.log(await moduleHighlightValueLink("@postpostscript/moduleHighlightValueLink", "moduleHighlightValueLink")) // https://val.town/v/postpostscript/moduleHighlightValueLink#L6-20 // get URL you can imbed in an iframe console.log(getRedirectUrl("@postpostscript/moduleHighlightValueLink", "getRedirectUrl", true)); // https://postpostscript-modulehighlightvaluelink.web.val.run/?embed=1&module=%40postpostscript%2FmoduleHighlightValueLink&name=getRedirectUrl

Iframe example:

Create valimport { htmlResponse } from "https://esm.town/v/postpostscript/html"; import { getRedirectUrl } from "https://esm.town/v/postpostscript/moduleHighlightValueLink"; export default async function(req: Request): Promise<Response> { return htmlResponse` <iframe src="${getRedirectUrl(import.meta.url, "default", true)}" width="100%" height="100%"> </iframe> `; }
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
import * as queryString from "https://deno.land/x/querystring@v1.0.2/mod.js";
import { parse, print } from "https://deno.land/x/swc@0.2.1/mod.ts";
import { findASTValueDefinition, findSpanLocation, getSourceAST } from "https://esm.town/v/postpostscript/ast";
import {
getValEndpointFromUrl,
getValNameFromUrl,
getValUrlFromName,
moduleSource,
} from "https://esm.town/v/postpostscript/meta";
export async function moduleHighlightValueLink(module: string, name: string, log = false) {
const { source, node: ast } = await getSourceAST(await moduleSource(module));
const node = findASTValueDefinition(ast, name);
const range = node && findSpanLocation(source, ast, node.span);
const locationText = range && (
range.start === range.end
? `L${range.start}`
: `L${range.start}-${range.end}`
);
if (log) {
console.log({
module,
name,
astSpan: ast.span,
node,
range,
locationText,
});
}
const url = module.charAt(0) === "@"
? getValUrlFromName(module, "val.town")
: getValUrlFromName(getValNameFromUrl(module), "val.town");
return url + (locationText ? `#${locationText}` : "");
}
export function getRedirectUrl(module: string, name: string, embed = false) {
const qs = new URLSearchParams();
if (embed) {
qs.set("embed", "1");
}
qs.set("module", module);
qs.set("name", name);
return getValEndpointFromUrl(import.meta.url) + "/?" + qs.toString();
}
export default async function(req: Request) {
const qs = queryString.parse(req.url.split("?")[1] || "");
const module = qs.module?.trim();
const name = qs.name?.trim();
const embed = qs.embed?.trim();
if (!(module && name)) {
return Response.json({
error: "qs params `module` and `name` must be set",
});
}
const url = await moduleHighlightValueLink(module, name, true);
return Response.redirect(
embed
? url.replace("val.town/v/", "val.town/embed/")
: url,
);
}
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
import { Statement } from "https://esm.town/v/postpostscript/sqliteBuilder";
// this is apparently a dubious solution (https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468375328),
// but this seems to work: https://github.com/microsoft/TypeScript/issues/13298#issuecomment-885980381
type UnionToIntersection<U> = (
U extends never ? never : (arg: U) => never
) extends (arg: infer I) => void ? I
: never;
type UnionToTuple<T> = UnionToIntersection<
T extends never ? never : (t: T) => T
> extends (_: never) => infer W ? [...UnionToTuple<Exclude<T, W>>, W]
: [];
// ---
type EntryTuple<T> = UnionToTuple<
{
[K in keyof T]: [K, T[K]];
}[keyof T]
>;
type KeyTuple<T> = EntryTuple<T> extends infer E ? {
[Index in keyof E]: E[Index] extends [infer K, any] ? K
: never;
}
: never;
type ValueTuple<T> = EntryTuple<T> extends infer E ? {
[Index in keyof E]: E[Index] extends [any, infer V] ? V
: never;
}
: never;
export function StatementTyped<T>(
strings: TemplateStringsArray,
// @ts-ignore
...replacements: ValueTuple<T>
) {
// @ts-ignore
return Statement(strings, ...replacements);
}

sqliteBlob: make sqlite queries against your blobs!*

  • does not include blob values
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
import { Statement, type StatementInstance } from "https://esm.town/v/postpostscript/sqliteBuilder";
import { createSqlite } from "https://esm.town/v/postpostscript/sqliteWasm";
import { blob as blobAPI } from "https://esm.town/v/std/blob";
export async function sqliteBlob(options: SqliteBlobOptions = {}) {
const schema = sqliteBlobSchema(options);
const sqlite = createSqlite();
sqlite.batch(await schema);
return sqlite;
}
export async function sqliteBlobSchema(
{ prefix = undefined, table = Statement`blobs`, blob = blobAPI }: SqliteBlobOptions = {},
) {
const blobs = await blob.list(prefix);
return [
Statement`
CREATE TABLE ${table} (
key TEXT PRIMARY KEY,
size INTEGER NOT NULL,
lastModified DATETIME NOT NULL
)
`,
...blobs.map(({ key, size, lastModified }) => {
return Statement`
INSERT INTO ${table} VALUES (
${key},
${size},
${new Date(lastModified).getTime() / 1000}
)
`;
}),
];
}
export type SqliteBlobOptions = {
prefix?: string;
table?: StatementInstance;
blob?: {
list: (...args: any[]) => any;
};
};

sqliteUniverse: make queries against multiple vals or endpoints at the same time!

Example: @postpostscript/sqliteUniverseExample

Todo

  • tests‼️
  • update to support following syntax: SELECT * FROM "@example/endpoint".someTable or SELECT * FROM "@example/endpoint:::someTable"
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
import { getValEndpointFromName } from "https://esm.town/v/postpostscript/meta";
import { getTableNames, replaceTableNames } from "https://esm.town/v/postpostscript/sqliteAST";
import { sqliteFromBlob } from "https://esm.town/v/postpostscript/sqliteBackup";
import { Statement, StatementInstance } from "https://esm.town/v/postpostscript/sqliteBuilder";
import { sqliteDump } from "https://esm.town/v/postpostscript/sqliteDump";
import { sqliteFromAPI } from "https://esm.town/v/postpostscript/sqliteFromAPI";
import type { SqliteInterface } from "https://esm.town/v/postpostscript/sqliteTypes";
import { createSqlite } from "https://esm.town/v/postpostscript/sqliteWasm";
import type { MaybePromise } from "https://esm.town/v/postpostscript/typeUtils";
import type { InStatement } from "https://esm.town/v/std/sqlite";
import type { ResultSet } from "npm:@libsql/client";
export const sqliteUniverse = {
execute,
batch,
};
export function sqliteUniverseWithOptions(options: SqliteUniverseOptions) {
return {
execute(statement: InStatement) {
return execute(statement, options);
},
batch(statements: InStatement[]) {
return batch(statements, options);
},
};
}
async function execute(
statement: InStatement,
options: SqliteUniverseOptions = {},
): Promise<Omit<ResultSet, "columnTypes" | "lastInsertRowid">> {
const [res] = await batch([statement], options);
return res;
}
async function batch(
statements: InStatement[],
options: SqliteUniverseOptions = {},
): Promise<Omit<ResultSet, "columnTypes" | "lastInsertRowid">[]> {
const endpointTableMap: EndpointTableMap = {};
const fullTableNames = statements.map(getTableNames).reduce((res, { tables }) => {
return new Set([...res, ...tables]);
}, new Set<string>());
fullTableNames.forEach(fullTable => {
const parts = fullTable.split("/");
const endpoint = parts.slice(0, -1).join("/");
const table = parts.slice(-1)[0];
endpointTableMap[endpoint] ??= new Set<string>();
endpointTableMap[endpoint].add(table);
});
const sqlite = await createSqliteFromEndpointTables(endpointTableMap, options);
const normalized = statements.map(statement => {
let { sql, args } = typeof statement === "string"
? new StatementInstance(statement)
: statement;
let match;
while (match = sql.match(/"([^"]+\/)sqlite_schema"/)) {
const [full, start] = match;
const index = sql.indexOf(full);
const numPreviousArgs = sql.slice(0, index).split("?").length - 1;
if (!args) {
args = [];
}
args.splice(numPreviousArgs, 0, `${start}%`);
sql = [
sql.slice(0, index),
`(SELECT * FROM sqlite_schema WHERE tbl_name LIKE ?)`,
sql.slice(index + full.length),
].join("");
}
return new StatementInstance(
sql,
args,
);
});
return sqlite.batch(normalized);
}
async function createSqliteFromEndpointTables(
endpointTableMap: EndpointTableMap,
{ interfaces = defaultInterfaces }: SqliteUniverseOptions = {},
) {
const schemas = Object.entries(endpointTableMap).map(async ([endpoint, tables]) => {
let sqlite = await interfaces.exact?.[endpoint];
if (sqlite instanceof Function) {
sqlite = await sqlite({ endpoint, tables });
}
for (let i = 0; !sqlite && i < interfaces.patterns?.length; i++) {
const [test, method] = interfaces.patterns[i];
const match = endpoint.match(test);
if (match) {
sqlite = await method({
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
import { sqliteBlob } from "https://esm.town/v/postpostscript/sqliteBlob";
import { Statement } from "https://esm.town/v/postpostscript/sqliteBuilder";
import {
defaultPatterns,
sqliteUniverse,
sqliteUniverseWithOptions,
} from "https://esm.town/v/postpostscript/sqliteUniverseBeta";
import { sqlite as sqlitePrivate } from "https://esm.town/v/std/sqlite";
await (async () => {
const start = performance.now();
const result = await Statement`
SELECT t1.*, t2.*
FROM "@postpostscript/sqlitePublic:::authIdExampleComments_comment" t1
JOIN "https://pps-sqlitepublic.web.val.run:::example" t2
LIMIT 1
`.execute({
sqlite: sqliteUniverse,
});
console.log(result);
console.log("took", performance.now() - start, "ms");
})();
await (async () => {
const start = performance.now();
// to query against your private data, use sqliteUniverseWithOptions with the @std/sqlite interface option passed
// use sqliteBlob to query your blob metadata!
const sqlite = sqliteUniverseWithOptions({
interfaces: {
exact: {
"@std/sqlite": sqlitePrivate,
"@std/blob": () => sqliteBlob(),
},
patterns: defaultPatterns,
fallback() {
return sqlitePrivate;
},
},
});
const result = await Statement`
SELECT t1.*, t4.*
FROM "@std/sqlite:::authIdExampleComments_comment" t1
JOIN "@std/blob:::blobs" t4
LIMIT 1
`.execute({ sqlite });
// Statement`
// DELETE FROM "@std/sqlite/use_tracking"
// `,
console.log(result);
console.log("took", performance.now() - start, "ms");
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { Statement } from "https://esm.town/v/postpostscript/sqliteBuilder";
import { sqliteDump } from "https://esm.town/v/postpostscript/sqliteDump";
import { sqliteServe } from "https://esm.town/v/postpostscript/sqliteServe";
import { createSqlite } from "https://esm.town/v/postpostscript/sqliteWasm";
export const {
execute,
batch,
sqlite: sqlitePublic,
handler,
} = sqliteServe(async () => {
const dump = await sqliteDump({
tables: ["authIdExampleComments_comment"],
subset: {
authIdExampleComments_comment:
Statement`SELECT * FROM authIdExampleComments_comment WHERE username = "postpostscript"`,
},
});
const sqlite = createSqlite();
sqlite.batch(dump);
return sqlite;
});
export default handler;
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
import { html } from "https://esm.town/v/postpostscript/html";
export async function randomLike() {
const { data } = await fetch("https://api.val.town/v1/me/likes", {
headers: {
Authorization: `Bearer ${Deno.env.get("valtown")}`,
},
}).then(res => res.json());
return data.sort(() => Math.random() > 0.5 ? 1 : -1)[0];
}
export async function randomLikeLink() {
try {
const { author: { username }, name } = await randomLike();
return html`
<a href="https://val.town/v/${username}/${name}" target="_blank">
@${username}/${name}
</a>
`;
} catch (e) {
console.log("randomLikeLink could not find like", e);
}
return html`Couldn't find one!`;
}
export async function recommends() {
return html`
Check out a random Val I've liked!
${await randomLikeLink()}
`;
}
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
import { html } from "https://esm.town/v/postpostscript/htmlComponentLibrary";
import svg2dataURL from "npm:mini-svg-data-uri";
export const logo = {
white: html(svg2dataURL(
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"600\" height=\"237\" fill=\"none\"><path d=\"M0 0h600v237H0z\"/><g fill=\"#fff\" clip-path=\"url(#a)\"><path d=\"M171.182 146.387c3.89 0 7.064-1.082 9.524-3.248 2.459-2.164 3.689-5.046 3.689-8.644v-1.1
)),
black: html(svg2dataURL(
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"600\" height=\"237\" fill=\"none\"><path d=\"M0 0h600v237H0z\"/><g fill=\"#000\" clip-path=\"url(#a)\"><path d=\"M171.182 146.387c3.89 0 7.064-1.082 9.524-3.248 2.459-2.164 3.689-5.046 3.689-8.644v-1.1
)),
};
export const mark = {
white: html(svg2dataURL(
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"400\" height=\"400\" fill=\"none\"><path d=\"M0 0h400v400H0z\"/><g fill=\"#fff\" clip-path=\"url(#a)\"><path d=\"M265.026 271.002c-7.196 0-13.032-2.235-17.508-6.709-4.48-4.472-6.716-10.452-6.716-17.93v
)),
black: html(svg2dataURL(
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"400\" height=\"400\" fill=\"none\"><path d=\"M0 0h400v400H0z\"/><g fill=\"#000\" clip-path=\"url(#a)\"><path d=\"M265.026 271.002c-7.196 0-13.032-2.235-17.508-6.709-4.48-4.472-6.716-10.452-6.716-17.93v
)),
};
export const valTownLogoWhite =
html`<img class="logo-white" style="height: 3.4em; margin: -1.2em auto -1.2em -0.75em;" alt="Val Town" src="${logo.white}">`;
export const valTownLogoBlack =
html`<img class="logo-black" style="height: 3.4em; margin: -1.2em auto -1.2em -0.75em;" alt="Val Town" src="${logo.black}">`;
export const valTownMarkWhite =
html`<img class="mark-white" style="height: 3.4em; margin: -1.2em auto -1.2em -0.75em;" alt="Val Town" src="${mark.white}">`;
export const valTownMarkBlack =
html`<img class="mark-black" style="height: 3.4em; margin: -1.2em auto -1.2em -0.75em;" alt="Val Town" src="${mark.black}">`;
export const valTownLogoAuto = html`
${valTownLogoWhite}
${valTownLogoBlack}
<style>
.logo-white {
display: none;
}
@media (prefers-color-scheme: dark) {
.logo-white {
display: inline-block;
}
.logo-black {
display: none;
}
}
</style>
`;
export const valTownMarkAuto = html`
${valTownMarkWhite}
${valTownMarkBlack}
<style>
.mark-white {
display: none;
}
@media (prefers-color-scheme: dark) {
.mark-white {
display: inline-block;
}
.mark-black {
display: none;
}
}
</style>
`;
// let me know if this is an issue :)

htmlAsync: async fork of @postpostscript/html supporting Promises as replacements

Examples

Create valimport { delay } from "https://deno.land/x/delay@v0.2.0/mod.ts"; import { html, htmlResponseAsync } from "https://esm.town/v/postpostscript/htmlAsync"; export default function(req: Request) { return htmlResponseAsync` ${(async () => { await delay(1000); return `<script>console.log("sanitized")</script>`; })()} ${(async () => { await delay(1000); return html`<script>console.log("unsanitized")</script>`; })()} `; }

Tests: @postpostscript/htmlAsyncTest

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
import { htmlEscape, RawHTML } from "https://esm.town/v/postpostscript/html";
import { hybridTaggedTemplateMethodAsync } from "https://esm.town/v/postpostscript/hybridTaggedTemplateMethod";
import type { MaybePromise } from "https://esm.town/v/postpostscript/typeUtils";
import { html as createHTMLResponse } from "https://esm.town/v/stevekrouse/html";
export { html, htmlEscape, htmlResponse, RawHTML, rawHtml } from "https://esm.town/v/postpostscript/html";
export const htmlAsync = hybridTaggedTemplateMethodAsync({
async transformReplacement(replacement: MaybePromise<unknown>) {
const _replacement = await replacement;
return _replacement instanceof Array
? (await Promise.all(_replacement.map(async (part) => htmlEscape(await part)))).join("")
: htmlEscape(_replacement);
},
async transformResult(result: Promise<string>) {
return new RawHTML(await result);
},
});
export const rawHtmlAsync = hybridTaggedTemplateMethodAsync({
async transformReplacement(replacement: MaybePromise<unknown>) {
const _replacement = await replacement;
return _replacement instanceof Array
? (await Promise.all(_replacement)).join("")
: _replacement;
},
async transformResult(result: Promise<string>) {
return new RawHTML(await result);
},
});
export const htmlResponseAsync = hybridTaggedTemplateMethodAsync({
async transformReplacement(replacement: MaybePromise<unknown>) {
const _replacement = await replacement;
return _replacement instanceof Array
? (await Promise.all(_replacement.map(async (part) => htmlEscape(await part)))).join("")
: htmlEscape(_replacement);
},
async transformResult(result: Promise<string>) {
return createHTMLResponse(await result);
},
});