diff --git a/dev-bucket/README.md b/dev-bucket/README.md
new file mode 100644
index 0000000..28c997a
--- /dev/null
+++ b/dev-bucket/README.md
@@ -0,0 +1,6 @@
+# dev-bucket
+
+This is a very slimmed down recreation of the `dist-prod` bucket meant for testing the Release Worker.
+The files here have little to no content, they're here just to build the structure of the bucket itself so that the Release Worker can be used in development easier.
+
+For more information on the structure of the bucket, read [TODO]().
diff --git a/dev-bucket/metrics/index.html b/dev-bucket/metrics/index.html
new file mode 100644
index 0000000..7178822
--- /dev/null
+++ b/dev-bucket/metrics/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ metrics/index.html
+
+
\ No newline at end of file
diff --git a/dev-bucket/metrics/logs/nodejs.org-access.log.20241222.0000000000.csv b/dev-bucket/metrics/logs/nodejs.org-access.log.20241222.0000000000.csv
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-nightly/index.json b/dev-bucket/nodejs/chakracore-nightly/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/chakracore-nightly/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/SHASUMS256.txt b/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/docs/api/index.html b/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/docs/api/index.html
new file mode 100644
index 0000000..56de0b2
--- /dev/null
+++ b/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/docs/api/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/docs/api/index.html
+
+
diff --git a/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-arm64/.gitkeep b/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-arm64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-x64/.gitkeep b/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-x64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-x86/.gitkeep b/dev-bucket/nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-x86/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-rc/index.json b/dev-bucket/nodejs/chakracore-rc/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/chakracore-rc/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/SHASUMS256.txt b/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/win-arm64/.gitkeep b/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/win-arm64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/win-x64/.gitkeep b/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/win-x64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/win-x86/.gitkeep b/dev-bucket/nodejs/chakracore-rc/v10.0.0-rc.0/win-x86/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-release/index.json b/dev-bucket/nodejs/chakracore-release/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/chakracore-release/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/chakracore-release/v10.0.0/SHASUMS256.txt b/dev-bucket/nodejs/chakracore-release/v10.0.0/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-release/v10.0.0/docs/api/index.html b/dev-bucket/nodejs/chakracore-release/v10.0.0/docs/api/index.html
new file mode 100644
index 0000000..428d363
--- /dev/null
+++ b/dev-bucket/nodejs/chakracore-release/v10.0.0/docs/api/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ nodejs/chakracore-release/v10.0.0/docs/api/index.html
+
+
diff --git a/dev-bucket/nodejs/chakracore-release/v10.0.0/win-arm64/.gitkeep b/dev-bucket/nodejs/chakracore-release/v10.0.0/win-arm64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-release/v10.0.0/win-x64/.gitkeep b/dev-bucket/nodejs/chakracore-release/v10.0.0/win-x64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/chakracore-release/v10.0.0/win-x86/.gitkeep b/dev-bucket/nodejs/chakracore-release/v10.0.0/win-x86/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/docs/v0.0.1/index.html b/dev-bucket/nodejs/docs/v0.0.1/index.html
new file mode 100644
index 0000000..8b84318
--- /dev/null
+++ b/dev-bucket/nodejs/docs/v0.0.1/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ nodejs/docs/v0.0.1/index.html
+
+
\ No newline at end of file
diff --git a/dev-bucket/nodejs/nightly/index.json b/dev-bucket/nodejs/nightly/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/nightly/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/SHASUMS256.txt b/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/api/index.html b/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/api/index.html
new file mode 100644
index 0000000..61a2856
--- /dev/null
+++ b/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/api/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/api/index.html
+
+
diff --git a/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/apilinks.json b/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/apilinks.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/apilinks.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/win-arm64/.gitkeep b/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/win-arm64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/win-x64/.gitkeep b/dev-bucket/nodejs/nightly/v24.0.0-nightly20241219756077867b/win-x64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/rc/index.json b/dev-bucket/nodejs/rc/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/rc/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/rc/v23.0.0-rc.3/SHASUMS256.txt b/dev-bucket/nodejs/rc/v23.0.0-rc.3/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/rc/v23.0.0-rc.3/docs/api/index.html b/dev-bucket/nodejs/rc/v23.0.0-rc.3/docs/api/index.html
new file mode 100644
index 0000000..80f1072
--- /dev/null
+++ b/dev-bucket/nodejs/rc/v23.0.0-rc.3/docs/api/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ nodejs/rc/v23.0.0-rc.3/docs/api/index.html
+
+
diff --git a/dev-bucket/nodejs/rc/v23.0.0-rc.3/docs/apilinks.json b/dev-bucket/nodejs/rc/v23.0.0-rc.3/docs/apilinks.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/rc/v23.0.0-rc.3/docs/apilinks.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/rc/v23.0.0-rc.3/win-arm64/.gitkeep b/dev-bucket/nodejs/rc/v23.0.0-rc.3/win-arm64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/rc/v23.0.0-rc.3/win-x64/.gitkeep b/dev-bucket/nodejs/rc/v23.0.0-rc.3/win-x64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/release/index.json b/dev-bucket/nodejs/release/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/release/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/release/v20.0.0/SHASUMS256.txt b/dev-bucket/nodejs/release/v20.0.0/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/release/v20.0.0/docs/api/index.html b/dev-bucket/nodejs/release/v20.0.0/docs/api/index.html
new file mode 100644
index 0000000..87922d5
--- /dev/null
+++ b/dev-bucket/nodejs/release/v20.0.0/docs/api/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ nodejs/release/v20.0.0/docs/api/index.html
+
+
diff --git a/dev-bucket/nodejs/release/v20.0.0/docs/apilinks.json b/dev-bucket/nodejs/release/v20.0.0/docs/apilinks.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/release/v20.0.0/docs/apilinks.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/release/v20.0.0/win-arm64/.gitkeep b/dev-bucket/nodejs/release/v20.0.0/win-arm64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/release/v20.0.0/win-x64/.gitkeep b/dev-bucket/nodejs/release/v20.0.0/win-x64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/release/v20.0.0/win-x86/.gitkeep b/dev-bucket/nodejs/release/v20.0.0/win-x86/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/test/index.json b/dev-bucket/nodejs/test/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/test/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/test/v24.0.0-test6af5c4e2b40/SHASUMS256.txt b/dev-bucket/nodejs/test/v24.0.0-test6af5c4e2b40/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/test/v24.0.0-test6af5c4e2b40/win-arm64/.gitkeep b/dev-bucket/nodejs/test/v24.0.0-test6af5c4e2b40/win-arm64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/test/v24.0.0-test6af5c4e2b40/win-x64/.gitkeep b/dev-bucket/nodejs/test/v24.0.0-test6af5c4e2b40/win-x64/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/v8-canary/index.json b/dev-bucket/nodejs/v8-canary/index.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/v8-canary/index.json
@@ -0,0 +1 @@
+{}
diff --git a/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/SHASUMS256.txt b/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/SHASUMS256.txt
new file mode 100644
index 0000000..e69de29
diff --git a/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/api/index.html b/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/api/index.html
new file mode 100644
index 0000000..8a0b6b3
--- /dev/null
+++ b/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/api/index.html
@@ -0,0 +1,9 @@
+
+
+
+ Document
+
+
+ nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/api/index.html
+
+
diff --git a/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/apilinks.json b/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/apilinks.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/dev-bucket/nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/apilinks.json
@@ -0,0 +1 @@
+{}
diff --git a/package.json b/package.json
index 6108966..26f4d38 100644
--- a/package.json
+++ b/package.json
@@ -5,9 +5,11 @@
"type": "module",
"scripts": {
"start": "wrangler dev --remote",
+ "dev": "wrangler deploy --dry-run --outdir=dist && node --import=tsx scripts/dev-server.ts",
"format": "prettier --check --write \"**/*.{ts,js,mjs,json,md}\"",
"prettier": "prettier --check \"**/*.{ts,js,mjs,json,md}\"",
"lint": "eslint ./src",
+ "_test": "wrangler deploy --dry-run --outdir=dist && node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --import=tsx",
"test": "npm run test:unit && npm run test:e2e",
"test:unit": "node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --import=tsx ./tests/unit/index.test.ts",
"test:e2e": "wrangler deploy --dry-run --outdir=dist && node --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout --import=tsx ./tests/e2e/index.test.ts",
diff --git a/scripts/constants.mjs b/scripts/constants.mjs
index f1a7be9..859d9c9 100644
--- a/scripts/constants.mjs
+++ b/scripts/constants.mjs
@@ -9,3 +9,76 @@ export const PROD_BUCKET = process.env.PROD_BUCKET ?? 'dist-prod';
export const STAGING_BUCKET = process.env.STAGING_BUCKET ?? 'dist-staging';
export const R2_RETRY_COUNT = 3;
+
+/**
+ * @type {Record}
+ */
+export const DEV_BUCKET = {
+ 'metrics/index.html': 'metrics/index.html
',
+ 'metrics/logs/nodejs.org-access.log.20241222.0000000000.csv': '',
+
+ 'nodejs/chakracore-nightly/index.json': '{}',
+ 'nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/SHASUMS256.txt':
+ '',
+ 'nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/docs/api/index.html':
+ 'nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/docs/api/index.html
\n',
+ 'nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-arm64/.gitkeep':
+ '',
+ 'nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-x64/.gitkeep':
+ '',
+ 'nodejs/chakracore-nightly/v10.13.0-nightly2018112084bd6f3c82/win-x86/.gitkeep':
+ '',
+
+ 'nodejs/chakracore-rc/index.json': '{}',
+ 'nodejs/chakracore-rc/v10.0.0-rc.0/SHASUMS256.txt': '',
+ 'nodejs/chakracore-rc/v10.0.0-rc.0/win-arm64/.gitkeep': '',
+ 'nodejs/chakracore-rc/v10.0.0-rc.0/win-x64/.gitkeep': '',
+ 'nodejs/chakracore-rc/v10.0.0-rc.0/win-x86/.gitkeep': '',
+
+ 'nodejs/chakracore-release/index.json': '{}',
+ 'nodejs/chakracore-release/v10.0.0/SHASUMS256.txt': '',
+ 'nodejs/chakracore-release/v10.0.0/docs/api/index.html':
+ 'nodejs/chakracore-release/v10.0.0/docs/api/index.html
\n',
+ 'nodejs/chakracore-release/v10.0.0/win-arm64/.gitkeep': '',
+ 'nodejs/chakracore-release/v10.0.0/win-x64/.gitkeep': '',
+ 'nodejs/chakracore-release/v10.0.0/win-x86/.gitkeep': '',
+
+ 'nodejs/docs/v0.0.1/index.html': 'nodejs/docs/v0.0.1/index.html
',
+
+ 'nodejs/nightly/index.json': '{}',
+ 'nodejs/nightly/v24.0.0-nightly20241219756077867b/SHASUMS256.txt': '',
+ 'nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/api/index.html':
+ 'nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/api/index.html
\n',
+ 'nodejs/nightly/v24.0.0-nightly20241219756077867b/docs/apilinks.json': '{}',
+ 'nodejs/nightly/v24.0.0-nightly20241219756077867b/win-arm64/.gitkeep': '',
+ 'nodejs/nightly/v24.0.0-nightly20241219756077867b/win-x64/.gitkeep': '',
+
+ 'nodejs/rc/index.json': '{}',
+ 'nodejs/rc/v23.0.0-rc.3/SHASUMS256.txt': '',
+ 'nodejs/rc/v23.0.0-rc.3/docs/api/index.html':
+ 'nodejs/rc/v23.0.0-rc.3/docs/api/index.html
\n',
+ 'nodejs/rc/v23.0.0-rc.3/docs/apilinks.json': '{}',
+ 'nodejs/rc/v23.0.0-rc.3/win-arm64/.gitkeep': '',
+ 'nodejs/rc/v23.0.0-rc.3/win-x64/.gitkeep': '',
+
+ 'nodejs/release/index.json': '{}',
+ 'nodejs/release/v20.0.0/SHASUMS256.txt': '',
+ 'nodejs/release/v20.0.0/docs/api/index.html':
+ 'nodejs/release/v20.0.0/docs/api/index.html
\n',
+ 'nodejs/release/v20.0.0/docs/apilinks.json': '{}',
+ 'nodejs/release/v20.0.0/win-arm64/.gitkeep': '',
+ 'nodejs/release/v20.0.0/win-x64/.gitkeep': '',
+ 'nodejs/release/v20.0.0/win-x86/.gitkeep': '',
+
+ 'nodejs/test/index.json': '{}',
+ 'nodejs/test/v24.0.0-test6af5c4e2b40/SHASUMS256.txt': '',
+ 'nodejs/test/v24.0.0-test6af5c4e2b40/win-arm64/.gitkeep': '',
+ 'nodejs/test/v24.0.0-test6af5c4e2b40/win-x64/.gitkeep': '',
+
+ 'nodejs/v8-canary/index.json': '{}',
+ 'nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/SHASUMS256.txt': '',
+ 'nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/api/index.html':
+ 'nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/api/index.html
\n',
+ 'nodejs/v8-canary/v24.0.0-v8-canary202412221f947c1730/docs/apilinks.json':
+ '{}',
+};
diff --git a/scripts/dev-server.ts b/scripts/dev-server.ts
new file mode 100644
index 0000000..388bccc
--- /dev/null
+++ b/scripts/dev-server.ts
@@ -0,0 +1,40 @@
+// Starts the dev server with the R2 bucket populated with the contents of the
+// `dev-bucket` directory
+
+// TODO this is still annoying since autoreload doesn't work
+
+import { setupMiniflare } from '../src/e2e-tests/util';
+import type { Env } from '../src/env';
+
+const [, url] = await setupMiniflare(getEnvironment());
+
+console.log(`Listening on ${url}`);
+
+function getEnvironment() {
+ const environment: Partial = {};
+
+ const environmentVariables: Array = [
+ 'ENVIRONMENT',
+ 'LOG_ERRORS',
+ 'S3_ENDPOINT',
+ 'S3_ACCESS_KEY_ID',
+ 'S3_ACCESS_KEY_SECRET',
+ 'BUCKET_NAME',
+ 'SENTRY_DSN',
+ 'ORIGIN_HOST',
+ ];
+
+ for (const variable of environmentVariables) {
+ if (variable in process.env) {
+ // @ts-expect-error todo
+ environment[variable as keyof Env] = process.env[variable]!;
+ }
+ }
+
+ if (environment.LOG_ERRORS) {
+ // Parse string value to boolean
+ environment.LOG_ERRORS = JSON.parse(environment.LOG_ERRORS);
+ }
+
+ return environment;
+}
diff --git a/src/e2e-tests/README.md b/src/e2e-tests/README.md
new file mode 100644
index 0000000..de46d8b
--- /dev/null
+++ b/src/e2e-tests/README.md
@@ -0,0 +1,3 @@
+# e2e-tests
+
+TODO
diff --git a/src/e2e-tests/directory.test.ts b/src/e2e-tests/directory.test.ts
new file mode 100644
index 0000000..7b46aec
--- /dev/null
+++ b/src/e2e-tests/directory.test.ts
@@ -0,0 +1,12 @@
+// import { SELF, env } from 'cloudflare:test';
+// import { beforeAll, test, expect } from 'vitest';
+// import { populateR2Bucket } from './util';
+
+// beforeAll(async () => {
+// // @ts-expect-error
+// await populateR2Bucket(env.R2_BUCKET);
+// });
+
+// test('something', async () => {
+// await SELF.fetch('https://localhost/123');
+// });
diff --git a/src/e2e-tests/file.test.ts b/src/e2e-tests/file.test.ts
new file mode 100644
index 0000000..aff61cf
--- /dev/null
+++ b/src/e2e-tests/file.test.ts
@@ -0,0 +1,130 @@
+import assert from 'node:assert';
+import test, { after, before, describe } from 'node:test';
+import { type Miniflare, type DispatchFetch } from 'miniflare';
+import { setupMiniflare } from './util';
+import { CACHE_HEADERS } from '../constants/cache';
+
+describe('file tests', async () => {
+ let mf: Miniflare;
+ let url: URL;
+
+ before(async () => {
+ [mf, url] = await setupMiniflare();
+ });
+
+ const sendRequest: DispatchFetch = (path, init) => {
+ if (typeof path === 'string' && path.startsWith('/')) {
+ path = path.substring(1);
+ }
+
+ return mf.dispatchFetch(`${url}${path}`, init);
+ };
+
+ // TODO go through each file and make sure it works
+
+ // test('GET `/dist/index.json`', async () => {
+ // const res = await sendRequest('/dist/index.json')
+
+ // assert.strictEqual(res.status, 200)
+ // assert.strictEqual(res.headers.get('content-type'), 'application/json')
+ // assert.strictEqual(res.headers.get('cache-control'), CACHE_HEADERS.success)
+ // assert.ok(res.headers.has('etag'))
+ // assert.ok(res.headers.has('last-modified'))
+
+ // const body = await res.text();
+ // // assert.strictEqual(body, )
+ // })
+
+ test('returns 404 for missing file', async () => {
+ const res = await sendRequest('/dist/asd123.json');
+
+ assert.equal(res.status, 404);
+ assert.strictEqual(res.headers.get('cache-control'), CACHE_HEADERS.failure);
+
+ const body = await res.text();
+ assert.strictEqual(body, 'File not found');
+ });
+
+ test('if-modified-since', async () => {
+ let lastModified: string;
+
+ // Make first request to grab its last modified date
+ {
+ const res = await sendRequest('/dist/index.json');
+
+ assert.strictEqual(res.status, 200);
+ assert.ok(res.headers.has('last-modified'));
+
+ lastModified = res.headers.get('last-modified')!;
+ }
+
+ // Make sure it returns a 304 when if-modified-since >= the file's last
+ // modified
+ {
+ const res = await sendRequest('/dist/index.json', {
+ headers: {
+ 'if-modified-since': lastModified,
+ },
+ });
+
+ assert.equal(res.status, 304);
+ }
+
+ // Now let's send a request to before the file's last modified date.
+ // This should give us a 200 since the file has changed.
+ {
+ const res = await sendRequest('/dist/index.json', {
+ headers: {
+ 'if-modified-since': new Date(0).toUTCString(),
+ },
+ });
+
+ assert.equal(res.status, 200);
+ }
+ });
+
+ test('if-unmodified-since', async () => {
+ let lastModified: string;
+
+ // Send request to get the file's last modified date
+ {
+ const res = await sendRequest('/dist/index.json');
+
+ assert.equal(res.status, 200);
+ assert.ok(res.headers.has('last-modified'));
+
+ lastModified = res.headers.get('last-modified')!;
+ }
+
+ // Send request before the last modified. This should return a 412.
+ {
+ const res = await sendRequest('/dist/index.json', {
+ headers: {
+ 'if-unmodified-since': new Date(0).toUTCString(),
+ },
+ });
+
+ assert.equal(res.status, 412);
+ }
+
+ // Send request after the last modified. This should return a 200
+ {
+ const timestamp = new Date(lastModified);
+ timestamp.setMinutes(timestamp.getMinutes() + 1);
+
+ const res = await sendRequest('/dist/index.json', {
+ headers: {
+ 'if-unmodified-since': timestamp.toUTCString(),
+ },
+ });
+
+ assert.equal(res.status, 412);
+ }
+ });
+
+ test('if-match', async () => {});
+
+ after(async () => {
+ await mf.dispose();
+ });
+});
diff --git a/src/e2e-tests/tsconfig.json b/src/e2e-tests/tsconfig.json
new file mode 100644
index 0000000..8a3ef31
--- /dev/null
+++ b/src/e2e-tests/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "types": ["@cloudflare/workers-types", "@types/node"]
+ }
+}
diff --git a/src/e2e-tests/util.ts b/src/e2e-tests/util.ts
new file mode 100644
index 0000000..5588c2d
--- /dev/null
+++ b/src/e2e-tests/util.ts
@@ -0,0 +1,70 @@
+import { join } from 'node:path';
+import { readdir, readFile, stat } from 'node:fs/promises';
+import { Miniflare } from 'miniflare';
+import type { Env } from '../env';
+
+const DEV_BUCKET_PATH = join(import.meta.dirname, '..', '..', 'dev-bucket');
+
+/**
+ * Fills the provided R2 bucket with the contents of the dev bucket
+ */
+async function populateR2Bucket(bucket: R2Bucket): Promise {
+ const paths = await readdir(DEV_BUCKET_PATH, { recursive: true });
+
+ const promises: Array> = [];
+
+ for (const path of paths) {
+ const relativePath = join(DEV_BUCKET_PATH, path);
+
+ const statResult = await stat(relativePath);
+
+ if (!statResult.isFile()) {
+ continue;
+ }
+
+ promises.push(
+ new Promise((resolve, reject) => {
+ readFile(relativePath, 'utf8')
+ .then(contents => {
+ bucket
+ .put(path, contents, {
+ customMetadata: {
+ // This is added by rclone when copying the release assets to the
+ // bucket
+ mtime: `${Math.floor(Date.now() / 1000)}`,
+ },
+ })
+ .then(() => resolve())
+ .catch(reject);
+ })
+ .catch(reject);
+ })
+ );
+ }
+
+ await Promise.all(promises);
+}
+
+export async function setupMiniflare(
+ env?: Partial>
+): Promise<[Miniflare, URL]> {
+ const mf = new Miniflare({
+ scriptPath: join(import.meta.dirname, '..', '..', 'dist', 'worker.js'),
+ modules: true,
+ bindings: {
+ ENVIRONMENT: 'e2e-tests',
+ LOG_ERRORS: true,
+ ...env,
+ },
+ r2Buckets: ['R2_BUCKET'],
+ });
+
+ const url = await mf.ready;
+
+ const bucket = await mf.getR2Bucket('R2_BUCKET');
+
+ // @ts-expect-error misc type mismatch
+ await populateR2Bucket(bucket);
+
+ return [mf, url];
+}
diff --git a/src/env.ts b/src/env.ts
index c4eaff7..693d630 100644
--- a/src/env.ts
+++ b/src/env.ts
@@ -3,31 +3,40 @@ export interface Env {
* Environment the worker is running in
*/
ENVIRONMENT: 'dev' | 'staging' | 'prod' | 'e2e-tests';
+
+ LOG_ERRORS?: boolean;
+
/**
* R2 bucket we read from
*/
R2_BUCKET: R2Bucket;
+
/**
* Endpoint to hit when using the S3 api.
*/
S3_ENDPOINT: string;
+
/**
* Id of the api token used for the S3 api.
* The token needs >=Object Read only permissions
*/
S3_ACCESS_KEY_ID: string;
+
/**
* Secret of the api token used for the S3 api
*/
S3_ACCESS_KEY_SECRET: string;
+
/**
* Bucket name
*/
BUCKET_NAME: string;
+
/**
* Sentry DSN, used for error monitoring
* If missing, Sentry isn't used
*/
SENTRY_DSN?: string;
+
ORIGIN_HOST: string;
}
diff --git a/tests/unit/router/router.test.ts b/src/routes/router.test.ts
similarity index 58%
rename from tests/unit/router/router.test.ts
rename to src/routes/router.test.ts
index 0553b95..b89d428 100644
--- a/tests/unit/router/router.test.ts
+++ b/src/routes/router.test.ts
@@ -1,26 +1,27 @@
-import assert from 'node:assert';
-import { it } from 'node:test';
-import { Router } from '../../../src/routes/router';
-import { Middleware } from '../../../src/middleware/middleware';
+import { test } from 'node:test';
+import { strictEqual } from 'node:assert';
+import type { Middleware } from '../middleware/middleware';
+import { type Context } from '../context';
+import { Router } from './router';
-it('middleware chains properly', async () => {
- const callOrdered: string[] = [];
+test('middleware chains properly', async () => {
+ const callOrder: string[] = [];
const firstMiddleware: Middleware = {
handle: (_, _2, next) => {
- callOrdered.push('first');
+ callOrder.push('first');
return next();
},
};
const secondMiddleware: Middleware = {
handle: (_, _2, next) => {
- callOrdered.push('second');
+ callOrder.push('second');
return next();
},
};
const thirdMiddleware: Middleware = {
handle: () => {
- callOrdered.push('third');
+ callOrder.push('third');
return Promise.resolve(new Response('cool response'));
},
};
@@ -28,13 +29,15 @@ it('middleware chains properly', async () => {
const router = new Router();
router.get('/', [firstMiddleware, secondMiddleware, thirdMiddleware]);
- // @ts-expect-error context
- const response = await router.handle(new Request('http://localhost/'), {});
- assert.strictEqual(await response.text(), 'cool response');
- assert.deepStrictEqual(callOrdered, ['first', 'second', 'third']);
+ // @ts-expect-error don't need a complete context
+ const ctx: Context = {};
+
+ const response = await router.handle(new Request('http://localhost/'), ctx);
+ strictEqual(await response.text(), 'cool response');
+ strictEqual(callOrder, ['first', 'second', 'third']);
});
-it('errors in middleware get skipped & reported', async () => {
+test('errors in middleware get skipped & reported', async () => {
const errorToThrow = new Error('error from first middleware');
const firstMiddleware: Middleware = {
handle: () => {
@@ -42,7 +45,7 @@ it('errors in middleware get skipped & reported', async () => {
},
};
const secondMiddleware: Middleware = {
- handle: (_, _2, next) => {
+ handle: (_, _2, _3) => {
return Promise.resolve(new Response('response from second middleware'));
},
};
@@ -55,9 +58,9 @@ it('errors in middleware get skipped & reported', async () => {
// @ts-expect-error incorrect signature but it's fine
captureException(exception) {
// Make sure sentry gets the error
- assert.strictEqual(exception, errorToThrow);
+ strictEqual(exception, errorToThrow);
},
},
});
- assert.strictEqual(await response.text(), 'response from second middleware');
+ strictEqual(await response.text(), 'response from second middleware');
});
diff --git a/src/routes/router.ts b/src/routes/router.ts
index c9cf507..dc81879 100644
--- a/src/routes/router.ts
+++ b/src/routes/router.ts
@@ -66,7 +66,6 @@ function buildMiddlewareChain(middlewares: Middleware[]): MiddlewareChain {
};
// Link the middlewares in reverse order for simplicity sakes
- // @ts-expect-error TODO: update types so toReversed is recognized
for (const middleware of middlewares.toReversed()) {
const wrappedMiddleware = errorHandled(middleware);
@@ -89,7 +88,9 @@ async function callMiddlewareChain(
ctx: Context
): Promise {
// Parse url here so we don't have to do it multiple times later on
- const url = parseUrl(request);
+ // TODO why isn't `parse` recognized
+ const url = URL.parse(request);
+
if (url === undefined) {
return responses.badRequest();
}
@@ -113,6 +114,11 @@ function errorHandled(middleware: Middleware): Middleware {
if (ctx.sentry !== undefined) {
ctx.sentry.captureException(err);
}
+
+ if (ctx.env.LOG_ERRORS !== undefined && ctx.env.LOG_ERRORS) {
+ console.error(err);
+ }
+
return next();
}
},
diff --git a/src/utils/directoryListing.ts b/src/utils/directoryListing.ts
index ff45385..01fa1f6 100644
--- a/src/utils/directoryListing.ts
+++ b/src/utils/directoryListing.ts
@@ -52,12 +52,9 @@ type TableElement = {
function renderSubdirectory(name: string): TableElement {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#utf-16_characters_unicode_code_points_and_grapheme_clusters
- // @ts-expect-error isWellFormed not recognized
- // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const wellFormedName: string = name.isWellFormed()
? name
- : // @ts-expect-error toWellFormed not recognized
- name.toWellFormed();
+ : name.toWellFormed();
const href = encodeURIComponent(
wellFormedName.substring(0, wellFormedName.length - 1)
diff --git a/src/utils/memo.test.ts b/src/utils/memo.test.ts
new file mode 100644
index 0000000..bbdd93b
--- /dev/null
+++ b/src/utils/memo.test.ts
@@ -0,0 +1,19 @@
+import { test } from 'node:test';
+import { equal, strictEqual } from 'node:assert';
+import { once } from './memo';
+
+test('once()', () => {
+ let callCount = 0;
+ const getString = once(() => {
+ callCount++;
+ return 'asd123';
+ });
+
+ const str = getString();
+ strictEqual(str, 'asd123');
+ equal(callCount, 1);
+
+ const str2 = getString();
+ equal(str2, str);
+ equal(callCount, 1);
+});
diff --git a/src/utils/object.test.ts b/src/utils/object.test.ts
new file mode 100644
index 0000000..e74338f
--- /dev/null
+++ b/src/utils/object.test.ts
@@ -0,0 +1,30 @@
+import { describe, test } from 'node:test';
+import { strictEqual } from 'node:assert';
+import { toReadableBytes } from './object';
+
+describe('toReadableBytes', () => {
+ test('converts 10 bytes to `10 B`', () => {
+ const result = toReadableBytes(10);
+ strictEqual(result, '10 B');
+ });
+
+ test('converts 1 KiB to `1.0 KB`', () => {
+ const result = toReadableBytes(1024);
+ strictEqual(result, '1.0 KB');
+ });
+
+ test('converts 1 MiB to `1.0 MB`', () => {
+ const result = toReadableBytes(1024 * 1024);
+ strictEqual(result, '1.0 MB');
+ });
+
+ test('converts 1 GiB to `1.1 GB`', () => {
+ const result = toReadableBytes(1024 * 1024 * 1024);
+ strictEqual(result, '1.1 GB');
+ });
+
+ test('converts 1 TiB to `1.1 TB`', () => {
+ const result = toReadableBytes(1024 * 1024 * 1024 * 1024);
+ strictEqual(result, '1.1 TB');
+ });
+});
diff --git a/src/utils/path.test.ts b/src/utils/path.test.ts
new file mode 100644
index 0000000..a48fd1f
--- /dev/null
+++ b/src/utils/path.test.ts
@@ -0,0 +1,38 @@
+import { describe, test } from 'node:test';
+import { equal } from 'node:assert';
+import { isDirectoryPath } from './path';
+
+describe('isDirectoryPath', () => {
+ test('returns true for `/dist/`', () => {
+ equal(isDirectoryPath('/dist/'), true);
+ });
+
+ test('returns true for `/dist`', () => {
+ equal(isDirectoryPath('/dist'), true);
+ });
+
+ test('returns true for `/dist/latest-v20.x`', () => {
+ equal(isDirectoryPath('/dist/latest-v20.x'), true);
+ });
+
+ test('returns true for `/dist/v20.20.2`', () => {
+ equal(isDirectoryPath('/dist/v20.20.2'), true);
+ });
+
+ test('returns false for `/dist/index.json`', () => {
+ equal(isDirectoryPath('/dist/index.json'), false);
+ });
+
+ // https://github.com/nodejs/release-cloudflare-worker/issues/71
+ test('returns false for `/download/release/latest/win-x64/node_pdb.7z`', () => {
+ equal(
+ isDirectoryPath('/download/release/latest/win-x64/node_pdb.7z'),
+ false
+ );
+ });
+
+ // https://github.com/nodejs/release-cloudflare-worker/issues/99
+ test('returns true for `/docs/latest/api`', () => {
+ equal(isDirectoryPath('/docs/latest/api'), true);
+ });
+});
diff --git a/src/utils/request.test.ts b/src/utils/request.test.ts
new file mode 100644
index 0000000..8361344
--- /dev/null
+++ b/src/utils/request.test.ts
@@ -0,0 +1,59 @@
+import { describe, test } from 'node:test';
+import { notEqual, strictEqual } from 'node:assert';
+import { parseRangeHeader } from './request';
+
+// TODO parseConditionalHeaders invalid date tests
+
+describe('parseRangeHeader', () => {
+ test('`bytes=0-10`', () => {
+ const result = parseRangeHeader('bytes=0-10');
+
+ strictEqual(result, {
+ offset: 0,
+ length: 11,
+ });
+ });
+
+ test('`bytes=0-10, 15-20, 20-30`', () => {
+ const result = parseRangeHeader('bytes=0-10, 15-20, 20-30');
+ notEqual(result, undefined);
+
+ strictEqual(result, {
+ offset: 0,
+ length: 11,
+ });
+ });
+
+ test('`bytes=0-`', () => {
+ const result = parseRangeHeader('bytes=0-');
+ notEqual(result, undefined);
+
+ strictEqual(result, {
+ offset: 0,
+ });
+ });
+
+ test('`bytes=-10`', () => {
+ const result = parseRangeHeader('bytes=-10');
+
+ strictEqual(result, { suffix: 10 });
+ });
+
+ test('`bytes=-`', () => {
+ const result = parseRangeHeader('bytes=-');
+
+ strictEqual(result, undefined);
+ });
+
+ test('`some-other-unit=-`', () => {
+ const result = parseRangeHeader('some-other-unit=-');
+
+ strictEqual(result, undefined);
+ });
+
+ test('`bytes=10-0`', () => {
+ const result = parseRangeHeader('bytes=10-0');
+
+ strictEqual(result, undefined);
+ });
+});
diff --git a/src/utils/request.ts b/src/utils/request.ts
index f5293a8..7ce8046 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -30,12 +30,14 @@ export function parseUrl(request: Request): URL | undefined {
}
export function parseConditionalHeaders(headers: Headers): ConditionalHeaders {
- const ifModifiedSince = headers.has('if-modified-since')
+ let ifModifiedSince = headers.has('if-modified-since')
? new Date(headers.get('if-modified-since')!)
: undefined;
if (ifModifiedSince instanceof Date) {
ifModifiedSince.setSeconds(ifModifiedSince.getSeconds() + 1);
+ } else {
+ ifModifiedSince = undefined;
}
const ifMatch = headers.has('if-match')
@@ -46,9 +48,16 @@ export function parseConditionalHeaders(headers: Headers): ConditionalHeaders {
? headers.get('if-none-match')!.replaceAll('"', '')
: undefined;
- const ifUnmodifiedSince = headers.has('if-unmodified-since')
- ? new Date(headers.get('if-unmodified-since')!)
- : undefined;
+ let ifUnmodifiedSince: Date | undefined = undefined;
+
+ if (headers.has('if-unmodified-since')) {
+ ifUnmodifiedSince = new Date(headers.get('if-modified-since')!);
+
+ if (!(ifUnmodifiedSince instanceof Date)) {
+ // Date isn't valid
+ ifUnmodifiedSince = undefined;
+ }
+ }
const range = headers.has('range')
? parseRangeHeader(headers.get('range')!)
diff --git a/tests/README.md b/tests/README.md
deleted file mode 100644
index c34c68b..0000000
--- a/tests/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tests
-
-Tests use Node's builtin test runner. See [https://nodejs.org/api/test.html](https://nodejs.org/api/test.html) for documentation.
diff --git a/tests/tsconfig.json b/tests/tsconfig.json
deleted file mode 100644
index df890c6..0000000
--- a/tests/tsconfig.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "types": ["node"]
- }
-}
diff --git a/tests/unit/README.md b/tests/unit/README.md
deleted file mode 100644
index 2127a5e..0000000
--- a/tests/unit/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
-# Unit Tests
-
-These are for testing specific functions in the worker.
-
-Test file names correspond to the file name in the [`src`](../../src) directory that is being tested.
-For example, [`./util.test.ts`](./util.test.ts) tests functions defined from [`src/util.ts`](../../src/util.ts).
-
-## Adding a New Test File
-
-Create the file and name it the same name as the file in the [`src`](../../src) directory.
-See [Adding a New Test](#adding-a-new-test) for testing the functions.
-Make sure to import the new test file into [./index.test.ts](./index.test.ts).
-
-## Adding a New Test
-
-Each function has its tests wrapped in a `describe` call just for neatness.
-We usually prefer the `it` alias for defining tests. For example, tests for the
-function `doSomething` would look something like this:
-
-```js
-describe('doSomething', () => {
- it('does something', () => {
- /*...*/
- });
-});
-```
diff --git a/tests/unit/index.test.ts b/tests/unit/index.test.ts
deleted file mode 100644
index 6300633..0000000
--- a/tests/unit/index.test.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import './utils/object.test';
-import './utils/path.test';
-import './utils/request.test';
-import './utils/memo.test';
-import './router/router.test';
-import './middleware/substituteMiddleware.test';
diff --git a/tests/unit/middleware/substituteMiddleware.test.ts b/tests/unit/middleware/substituteMiddleware.test.ts
deleted file mode 100644
index 893bf2a..0000000
--- a/tests/unit/middleware/substituteMiddleware.test.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import assert from 'node:assert';
-import { it } from 'node:test';
-import type { Request as WorkerRequest } from '../../../src/routes/request';
-import { SubtitutionMiddleware } from '../../../src/middleware/subtituteMiddleware';
-import { Router } from '../../../src/routes';
-
-it('correctly substitutes url `/dist/latest` to `/dist/v1.0.0`', async () => {
- const originalUrl = 'https://localhost/dist/latest';
-
- const originalRequest: Partial = {
- ...new Request(originalUrl),
- url: originalUrl,
- urlObj: new URL(originalUrl),
- };
-
- const router: Partial = {
- handle: (substitutedRequest: WorkerRequest) => {
- // Is the url is now substituted (latest -> v1.0.0)
- assert.strictEqual(
- substitutedRequest.url,
- 'https://localhost/dist/v1.0.0'
- );
-
- // Did we save the unsubstituted path?
- assert.strictEqual(
- substitutedRequest.unsubtitutedUrl,
- originalRequest.urlObj
- );
-
- return Promise.resolve(new Response());
- },
- };
-
- // Pre-checks for sanity
- assert.strictEqual(originalRequest.unsubtitutedUrl, undefined);
- assert.strictEqual(originalRequest.urlObj!.pathname, '/dist/latest');
-
- // @ts-expect-error full router not needed
- const middleware = new SubtitutionMiddleware(router, 'latest', 'v1.0.0');
-
- // @ts-expect-error full request & ctx not needed
- middleware.handle(originalRequest, {
- sentry: {
- addBreadcrumb: () => {},
- },
- });
-});
diff --git a/tests/unit/utils/memo.test.ts b/tests/unit/utils/memo.test.ts
deleted file mode 100644
index 6cdc2ab..0000000
--- a/tests/unit/utils/memo.test.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import assert from 'node:assert';
-import { it } from 'node:test';
-import { once } from '../../../src/utils/memo';
-
-it('once works', () => {
- let callCount = 0;
- const getString = once(() => {
- callCount++;
- return 'asd123';
- });
-
- const str = getString();
- assert.strictEqual(str, 'asd123');
- assert.strictEqual(callCount, 1);
-
- const str2 = getString();
- assert.equal(str, str2);
- assert.strictEqual(str2, 'asd123');
- assert.strictEqual(callCount, 1);
-});
diff --git a/tests/unit/utils/object.test.ts b/tests/unit/utils/object.test.ts
deleted file mode 100644
index a86a9a4..0000000
--- a/tests/unit/utils/object.test.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import assert from 'node:assert';
-import { describe, it } from 'node:test';
-import { toReadableBytes } from '../../../src/utils/object';
-
-describe('toReadableBytes', () => {
- it('converts 10 to `10 B`', () => {
- const result = toReadableBytes(10);
- assert.strictEqual(result, '10 B');
- });
-
- it('converts 1024 to `1.0 KB`', () => {
- const result = toReadableBytes(1024);
- assert.strictEqual(result, '1.0 KB');
- });
-});
diff --git a/tests/unit/utils/path.test.ts b/tests/unit/utils/path.test.ts
deleted file mode 100644
index 887280d..0000000
--- a/tests/unit/utils/path.test.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import assert from 'node:assert';
-import { describe, it } from 'node:test';
-import { isDirectoryPath } from '../../../src/utils/path';
-
-describe('isDirectoryPath', () => {
- it('returns true for `/dist/`', () => {
- assert.strictEqual(isDirectoryPath('/dist/'), true);
- });
-
- it('returns true for `/dist`', () => {
- assert.strictEqual(isDirectoryPath('/dist'), true);
- });
-
- it('returns true for `/dist/latest-v20.x`', () => {
- assert.strictEqual(isDirectoryPath('/dist/latest-v20.x'), true);
- });
-
- it('returns true for `/dist/v20.20.2`', () => {
- assert.strictEqual(isDirectoryPath('/dist/v20.20.2'), true);
- });
-
- it('returns false for `/dist/index.json`', () => {
- assert.strictEqual(isDirectoryPath('/dist/index.json'), false);
- });
-
- // https://github.com/nodejs/release-cloudflare-worker/issues/71
- it('returns false for `/download/release/latest/win-x64/node_pdb.7z`', () => {
- assert.strictEqual(
- isDirectoryPath('/download/release/latest/win-x64/node_pdb.7z'),
- false
- );
- });
-
- // https://github.com/nodejs/release-cloudflare-worker/issues/99
- it('returns true for `/docs/latest/api`', () => {
- assert.strictEqual(isDirectoryPath('/docs/latest/api'), true);
- });
-});
diff --git a/tests/unit/utils/request.test.ts b/tests/unit/utils/request.test.ts
deleted file mode 100644
index bab0398..0000000
--- a/tests/unit/utils/request.test.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import assert from 'node:assert';
-import { describe, it } from 'node:test';
-import { parseRangeHeader } from '../../../src/utils/request';
-
-describe('parseRangeHeader', () => {
- it('`bytes=0-10`', () => {
- const result = parseRangeHeader('bytes=0-10');
- assert.notStrictEqual(result, undefined);
-
- assert.strictEqual(result.offset, 0);
- assert.strictEqual(result.length, 11);
- });
-
- it('`bytes=0-10, 15-20, 20-30`', () => {
- const result = parseRangeHeader('bytes=0-10, 15-20, 20-30');
- assert.notStrictEqual(result, undefined);
-
- assert.strictEqual(result.offset, 0);
- assert.strictEqual(result.length, 11);
- });
-
- it('`bytes=0-`', () => {
- const result = parseRangeHeader('bytes=0-');
- assert.notStrictEqual(result, undefined);
-
- assert.strictEqual(result.offset, 0);
- assert.strictEqual(result.length, undefined);
- });
-
- it('`bytes=-10`', () => {
- const result = parseRangeHeader('bytes=-10');
- assert.notStrictEqual(result, undefined);
-
- assert.strictEqual(result.suffix, 10);
- });
-
- it('`bytes=-`', () => {
- const result = parseRangeHeader('bytes=-');
- assert.strictEqual(result, undefined);
- });
-
- it('`some-other-unit=-`', () => {
- const result = parseRangeHeader('some-other-unit=-');
- assert.strictEqual(result, undefined);
- });
-
- it('`bytes=10-0`', () => {
- const result = parseRangeHeader('bytes=10-0');
- assert.strictEqual(result, undefined);
- });
-});
diff --git a/tsconfig.json b/tsconfig.json
index d21695f..ed6ebfb 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,10 +1,10 @@
{
"compilerOptions": {
- "target": "es2021",
- "lib": ["es2021", "es2022"],
+ "target": "esnext",
+ "lib": ["esnext"],
"module": "esnext",
- "moduleResolution": "node",
- "types": ["@cloudflare/workers-types"],
+ "moduleResolution": "Bundler",
+ "types": ["@cloudflare/workers-types/2023-07-01"],
"resolveJsonModule": true,
"allowJs": true,
"checkJs": false,
diff --git a/wrangler.toml b/wrangler.toml
index 8846ed0..6ae2a5f 100644
--- a/wrangler.toml
+++ b/wrangler.toml
@@ -8,6 +8,7 @@ logpush = true
[vars]
workers_dev = true
ENVIRONMENT = 'dev'
+LOG_ERRORS = true
S3_ENDPOINT = 'https://07be8d2fbc940503ca1be344714cb0d1.r2.cloudflarestorage.com'
BUCKET_NAME = 'dist-prod'
ORIGIN_HOST = 'https://origin.nodejs.org'