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
import { type AccessDeep, accessDeep } from "https://esm.town/v/postpostscript/accessDeep";
import { sha } from "https://esm.town/v/postpostscript/hash";
const moduleCache = new Map<string, any>();
const moduleSourceCache = new Map<string, string>();
export async function importModule<ModuleType>(moduleName: string, checkPrivacy = true) {
const url = moduleName[0] === "@"
? `https://esm.town/v/${moduleName.slice(1)}`
: moduleName;
const name = getValNameFromUrl(url).slice(1);
const res = await fetch(`https://api.val.town/v1/alias/${name}`, {
headers: {
authorization: `Bearer ${Deno.env.get("valtown")}`,
},
});
if (!(res.ok && (await res.json()).privacy !== "private")) {
throw new Error(`module ${name} is private or does not exist`);
}
if (moduleCache.has(url)) {
return moduleCache.get(url);
}
const module = import(url) as Promise<ModuleType>;
moduleCache.set(url, module);
return module;
}
export function moduleSource(moduleName: string) {
const url = moduleName[0] === "@"
? `https://esm.town/v/${moduleName.slice(1)}`
: moduleName;
if (moduleSourceCache.has(url)) {
return moduleSourceCache.get(url);
}
return fetch(url).then((res) => res.text()).then(text => {
moduleSourceCache.set(url, text);
return text;
});
}
export async function moduleSourceHash(moduleName: string) {
return sha(await moduleSource(moduleName));
}
export async function call<ModuleType, const TMethod extends string | string[]>(
moduleName: string,
methodName: TMethod,
args: any[] = [],
): Promise<
AccessDeep<ModuleType, TMethod> extends (...args: any[]) => infer T ? T
: never
> {
const module = await importModule<ModuleType>(moduleName);
const method = accessDeep(module, methodName);
if (!(method instanceof Function)) {
const key = methodName instanceof Array
? methodName.join(".")
: methodName;
throw new Error(`\`call\` value \`${key}\` is not callable`);
}
return method(...args);
}
export function getValNameFromUrl(url: string) {
// https://esm.town/v/postpostscript/meta?v=11
// @^^^^^^^^^^^^^^^^^^^
return "@" + url.split("?")[0].split("/").slice(4, 6).join("/");
}
export function getValEmailFromUrl(url: string) {
// e.g. postpostscript.callbackEmail@valtown.email
const name = url.split("?")[0].split("/").slice(4, 6).join(".");
return `${name}@valtown.email`;
}
export function getValEndpointFromUrl(url: string) {
// https://esm.town/v/postpostscript/meta?v=11
// ^^^^^^^^^^^^^^^^^^^
return `https://${url.split("?")[0].split("/").slice(4, 6).join("-").toLowerCase()}.web.val.run`;
}
export function getValEndpointFromName(url: string) {
const [handle, name] = url.split(/[^\w]/g).filter(Boolean);
return `https://${[handle, name].join("-").toLowerCase()}.web.val.run`;
}
export function getValUrlFromName(name: string, domain = "esm.town") {
const _name = name.replace("@", "");
return `https://${domain}/v/${_name}`;
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
March 2, 2024