Skip to content

Commit 7500ba7

Browse files
committed
first commit
0 parents  commit 7500ba7

File tree

6 files changed

+406
-0
lines changed

6 files changed

+406
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
dist/worker.js
3+
worker/script.js

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2021 Cloudflare Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"main": "./src/index.js",
3+
"dependencies": {
4+
"toucan-js": "^2.1.0"
5+
}
6+
}

src/constants.js

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// The allowed file extensions.
2+
export const ALLOWED_EXTENSIONS = {
3+
js: "JavaScript",
4+
mjs: "JavaScript",
5+
6+
ts: "TypeScript",
7+
wasm: "WebAssembly",
8+
map: "Source Maps",
9+
swf: "Flash",
10+
json: "JSON",
11+
12+
css: "Styling",
13+
scss: "Styling",
14+
15+
png: "Images",
16+
gif: "Images",
17+
jpg: "Images",
18+
jpeg: "Images",
19+
svg: "Images",
20+
webp: "Images",
21+
cur: "Images",
22+
23+
ttf: "Fonts",
24+
eot: "Fonts",
25+
woff: "Fonts",
26+
woff2: "Fonts",
27+
otf: "Fonts",
28+
29+
aac: "Audio",
30+
mp3: "Audio",
31+
ogg: "Audio",
32+
33+
lang: "Other",
34+
hpb: "Other",
35+
};

src/index.js

+216
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import { ALLOWED_EXTENSIONS } from "./constants.js";
2+
import Toucan from "toucan-js";
3+
4+
addEventListener("fetch", (event) => {
5+
const sentry = new Toucan({
6+
dsn: SENTRY_DSN,
7+
event,
8+
environment: ENV,
9+
});
10+
event.respondWith(handleRequest(event, sentry));
11+
});
12+
13+
async function handleRequest(event, sentry) {
14+
function respond(msg, status) {
15+
return new Response(msg, { status });
16+
}
17+
18+
function ok(msg) {
19+
return respond(msg, 200);
20+
}
21+
22+
function forbid(msg = "invalid request") {
23+
return respond(msg, 403);
24+
}
25+
26+
function not_found(msg = "metadata not found") {
27+
return respond(msg, 404);
28+
}
29+
30+
function err(msg = "something went wrong") {
31+
return respond(msg, 500);
32+
}
33+
34+
function cache_resp(resp, cache, cache_key, max_age) {
35+
resp.headers.append("Cache-Control", `max-age=${max_age}`);
36+
event.waitUntil(cache.put(cache_key, resp.clone()));
37+
return resp;
38+
}
39+
40+
// Gets all keys in a KV namespace.
41+
async function* get_all_keys(namespace, options) {
42+
do {
43+
var { keys, list_complete, cursor } = await namespace.list({
44+
...options,
45+
cursor,
46+
});
47+
for (const key of keys) yield key;
48+
} while (list_complete === false);
49+
}
50+
51+
try {
52+
const { request } = event;
53+
const url = new URL(request.url);
54+
const pathname = decodeURI(url.pathname);
55+
56+
if (pathname === "/favicon.ico") {
57+
return not_found("not found");
58+
}
59+
60+
if (pathname === "/extensions") {
61+
return ok(JSON.stringify(ALLOWED_EXTENSIONS));
62+
}
63+
64+
const cache = caches.default;
65+
const cache_key = new Request(url.toString(), request);
66+
67+
// Check cache for request.
68+
let response = await cache.match(cache_key);
69+
if (response !== undefined) {
70+
return response;
71+
}
72+
73+
if (pathname === "/packages") {
74+
// Endpoint: `/packages`.
75+
// Return list of package names.
76+
let package_names = [];
77+
for await (const { name } of get_all_keys(CDNJS_PACKAGES)) {
78+
package_names.push(name);
79+
}
80+
81+
// Cache response for an hour.
82+
// New packages are not added often.
83+
return cache_resp(
84+
ok(JSON.stringify(package_names)),
85+
cache,
86+
cache_key,
87+
"3600"
88+
);
89+
}
90+
91+
const packages_endpoint = new RegExp(
92+
"^/packages/(?<package_name>[^/]+)$"
93+
).exec(pathname);
94+
95+
if (packages_endpoint !== null) {
96+
// Endpoint: `/packages/<package name>`.
97+
// Fetch package metadata from KV.
98+
const { package_name } = packages_endpoint.groups;
99+
const package_json = await CDNJS_PACKAGES.get(package_name);
100+
if (package_json === null) {
101+
// Cache 404 package for an hour.
102+
// New packages are not added often.
103+
return cache_resp(not_found(), cache, cache_key, "3600");
104+
}
105+
106+
// Cache package metadata for an hour since the
107+
// autoupdater runs about once per hour.
108+
return cache_resp(ok(package_json), cache, cache_key, "3600");
109+
}
110+
111+
const pkg_sris_endpoint = new RegExp(
112+
"^/packages/(?<package_name>[^/]+)/sris(/(?<version_name>[^/]+))?$"
113+
).exec(pathname);
114+
115+
if (pkg_sris_endpoint !== null) {
116+
// Endpoints: `/packages/<package name>/sris`, `/packages/<package name>/sris/<version name>`
117+
// Fetch SRIs for a package.
118+
const { package_name, version_name } = pkg_sris_endpoint.groups;
119+
let sris = {};
120+
for await (const { name, metadata } of get_all_keys(CDNJS_SRIS, {
121+
prefix: `${package_name}/${
122+
version_name === undefined ? "" : version_name
123+
}`,
124+
})) {
125+
sris[name] = metadata.sri;
126+
}
127+
128+
// Cache SRIs for an hour since the
129+
// autoupdater runs about once per hour.
130+
return cache_resp(ok(JSON.stringify(sris)), cache, cache_key, "3600");
131+
}
132+
133+
const aggregated_metadata_endpoint = new RegExp(
134+
"^/packages/(?<package_name>[^/]+)/all$"
135+
).exec(pathname);
136+
137+
if (aggregated_metadata_endpoint !== null) {
138+
// Endpoint: `/packages/<package name>/all`.
139+
// Fetch aggregated metadata for a package.
140+
const { package_name } = aggregated_metadata_endpoint.groups;
141+
const aggregated_gzip = await CDNJS_AGGREGATED_METADATA.get(
142+
package_name,
143+
"arrayBuffer"
144+
);
145+
if (aggregated_gzip === null) {
146+
// Cache 404 package for an hour.
147+
// New packages are not added often.
148+
return cache_resp(not_found(), cache, cache_key, "3600");
149+
}
150+
151+
// Cache package metadata for a couple minutes.
152+
// The API will use this metadata to update itself often.
153+
let resp = ok(aggregated_gzip);
154+
resp.headers.set("Content-Encoding", "gzip");
155+
return cache_resp(resp, cache, cache_key, "300");
156+
}
157+
158+
const versions_endpoint = new RegExp(
159+
"^/packages/(?<package_name>[^/]+)/versions$"
160+
).exec(pathname);
161+
162+
if (versions_endpoint !== null) {
163+
// Endpoint: `/packages/<package name>/versions`.
164+
// Fetch all versions for a package.
165+
const { package_name } = versions_endpoint.groups;
166+
const version_prefix = `${package_name}/`;
167+
168+
let version_names = [];
169+
for await (const { name } of get_all_keys(CDNJS_VERSIONS, {
170+
prefix: version_prefix,
171+
})) {
172+
version_names.push(name.substring(version_prefix.length));
173+
}
174+
175+
// Cache versions for an hour since the
176+
// autoupdater runs about once per hour.
177+
return cache_resp(
178+
ok(JSON.stringify(version_names)),
179+
cache,
180+
cache_key,
181+
"3600"
182+
);
183+
}
184+
185+
const version_endpoint = new RegExp(
186+
"^/packages/(?<package_name>[^/]+)/versions/(?<version_name>[^/]+)$"
187+
).exec(pathname);
188+
189+
if (version_endpoint !== null) {
190+
// Endpoint: `/packages/<package name>/versions/<version name>`.
191+
const { package_name, version_name } = version_endpoint.groups;
192+
const version_key = `${package_name}/${version_name}`;
193+
const version_json = await CDNJS_VERSIONS.get(version_key);
194+
if (version_json === null) {
195+
// Cache 404 version for an hour since the
196+
// autoupdater runs about once per hour.
197+
return cache_resp(
198+
not_found(`version not found: ${version_key}`),
199+
cache,
200+
cache_key,
201+
"3600"
202+
);
203+
}
204+
205+
// Each version is immutable once in KV
206+
// so cache it for a year.
207+
return cache_resp(ok(version_json), cache, cache_key, "31536000");
208+
}
209+
210+
// Unknown request.
211+
return forbid();
212+
} catch (e) {
213+
sentry.captureException(e);
214+
return ENV === "production" ? err() : err(e.stack);
215+
}
216+
}

0 commit comments

Comments
 (0)