From f3311f3193a83442a674b8de52668edd3522a771 Mon Sep 17 00:00:00 2001 From: hazre Date: Mon, 11 May 2026 13:45:24 +0200 Subject: [PATCH 1/6] chore: add pnpm-workspace.yaml with build allowlist --- pnpm-workspace.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 pnpm-workspace.yaml diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..661850f --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +allowBuilds: + esbuild: true + sharp: true + workerd: true From 196594e62cb82525c239cc95a9f58d8e55702de2 Mon Sep 17 00:00:00 2001 From: hazre Date: Mon, 11 May 2026 14:10:43 +0200 Subject: [PATCH 2/6] refactor: extract multipart encoding into encodeMultipart helper in proxy --- src/proxy.ts | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/proxy.ts b/src/proxy.ts index a97b6bf..172a981 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -41,21 +41,44 @@ function decodeMatrixId(rawId: string): string | MatrixError { return id; } +type MultipartPartLike = { + headers: Headers; + body?: BodyInit | null; +}; + +function encodeMultipart(boundary: string, parts: MultipartPartLike[]): string { + const lines: string[] = []; + + for (const part of parts) { + lines.push(`--${boundary}`); + for (const [key, value] of Object.entries(part.headers)) { + lines.push(`${key}: ${value}`); + } + lines.push(''); + if (typeof part.body === 'string') { + lines.push(part.body); + } + } + + lines.push(`--${boundary}--`, ''); + return lines.join('\r\n'); +} + function buildMultipartRedirect(targetLocation: string): Response { const boundary = `soliditas${Date.now().toString(16)}${Math.random().toString(16).slice(2)}`; - const body = Buffer.from( - `--${boundary}\r\n` + - `Content-Type: application/json\r\n` + - `\r\n` + - `{}\r\n` + - `--${boundary}\r\n` + - `Content-Type: application/octet-stream\r\n` + - `Location: ${targetLocation}\r\n` + - `\r\n` + - `\r\n` + - `--${boundary}--\r\n`, - 'utf8' - ); + + const body = encodeMultipart(boundary, [ + { + headers: new Headers({ 'Content-Type': 'application/json' }), + body: '{}', + }, + { + headers: new Headers({ + 'Content-Type': 'application/octet-stream', + Location: targetLocation, + }), + }, + ]); return new Response(body, { headers: { From 682e527554d6711c235de574ea47fda370671a10 Mon Sep 17 00:00:00 2001 From: hazre Date: Mon, 11 May 2026 14:33:14 +0200 Subject: [PATCH 3/6] refactor: remove nodejs_compat, replace Buffer with Web Platform APIs in mxcId --- README.md | 18 +++++------------- src/mxcId.ts | 36 ++++++++++-------------------------- wrangler.jsonc | 4 +--- 3 files changed, 16 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index e00c70c..1d1ab2d 100644 --- a/README.md +++ b/README.md @@ -13,22 +13,14 @@ Example in TypeScript, as seen in `mxcId.ts`: ```typescript function toBase64Url(value: string): string { - if (typeof btoa === 'function') { - const bytes = new TextEncoder().encode(value); - let binary = ''; + const bytes = new TextEncoder().encode(value); + let binary = ''; - for (const byte of bytes) { - binary += String.fromCodePoint(byte); - } - - return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll(/=+$/g, ''); - } - - if (typeof Buffer !== 'undefined') { - return Buffer.from(value).toString('base64url'); + for (const byte of bytes) { + binary += String.fromCodePoint(byte); } - throw new Error('No base64 encoder available in this runtime'); + return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll(/=+$/g, ''); } function toMatrixID(fname: string, prefix: string): string { diff --git a/src/mxcId.ts b/src/mxcId.ts index a6bedc5..e628b4c 100644 --- a/src/mxcId.ts +++ b/src/mxcId.ts @@ -23,22 +23,14 @@ * @return {*} {string} the url-safe-base64 encoded string */ function toBase64Url(value: string): string { - if (typeof btoa === 'function') { - const bytes = new TextEncoder().encode(value); - let binary = ''; + const bytes = new TextEncoder().encode(value); + let binary = ''; - for (const byte of bytes) { - binary += String.fromCodePoint(byte); - } - - return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll(/=+$/g, ''); - } - - if (typeof Buffer !== 'undefined') { - return Buffer.from(value).toString('base64url'); + for (const byte of bytes) { + binary += String.fromCodePoint(byte); } - throw new Error('No base64 encoder available in this runtime'); + return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll(/=+$/g, ''); } /** @@ -48,19 +40,11 @@ function toBase64Url(value: string): string { * @return {*} {string} the usable string */ function fromBase64Url(value: string): string { - if (typeof atob === 'function') { - const normalized = value.replace(/-/g, '+').replace(/_/g, '/'); - const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4); - const binary = atob(padded); - const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); - return new TextDecoder().decode(bytes); - } - - if (typeof Buffer !== 'undefined') { - return Buffer.from(value, 'base64url').toString(); - } - - throw new Error('No base64 decoder available in this runtime'); + const normalized = value.replace(/-/g, '+').replace(/_/g, '/'); + const padded = normalized + '='.repeat((4 - (normalized.length % 4)) % 4); + const binary = atob(padded); + const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); + return new TextDecoder().decode(bytes); } /** diff --git a/wrangler.jsonc b/wrangler.jsonc index 1a45a26..ea9b6a8 100644 --- a/wrangler.jsonc +++ b/wrangler.jsonc @@ -11,9 +11,7 @@ "enabled": true }, "upload_source_maps": true, - "compatibility_flags": [ - "nodejs_compat" - ], + "compatibility_flags": [], /** * Smart Placement * https://developers.cloudflare.com/workers/configuration/smart-placement/#smart-placement From 1718649e93d1f7ff058b9b51dd569fbc131de5ed Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 11 May 2026 16:25:04 +0200 Subject: [PATCH 4/6] refactor: remove runtime fallback tests for base64 encoding/decoding --- test/mxcId.spec.ts | 52 ---------------------------------------------- 1 file changed, 52 deletions(-) diff --git a/test/mxcId.spec.ts b/test/mxcId.spec.ts index fe1da14..d9b0ea9 100644 --- a/test/mxcId.spec.ts +++ b/test/mxcId.spec.ts @@ -60,55 +60,3 @@ describe('fromMatrixID', () => { expect(fromMatrixID(`custom_${encoded.split('_')[1]}`)).toBe('file.txt'); }); }); - -describe('runtime fallbacks', () => { - const originalBtoa = globalThis.btoa; - const originalAtob = globalThis.atob; - const originalBuffer = globalThis.Buffer; - - afterEach(() => { - globalThis.btoa = originalBtoa; - globalThis.atob = originalAtob; - globalThis.Buffer = originalBuffer; - }); - - it('uses Buffer fallback when btoa is unavailable', () => { - // @ts-expect-error test override - globalThis.btoa = undefined; - - const result = toMatrixID('buffer-test.txt', 'mxc_'); - - expect(result).toBe('mxc_YnVmZmVyLXRlc3QudHh0'); - }); - - it('uses Buffer fallback when atob is unavailable', () => { - // @ts-expect-error test override - globalThis.atob = undefined; - - const encoded = toMatrixID('buffer-decode.txt', 'mxc_'); - - expect(fromMatrixID(encoded)).toBe('buffer-decode.txt'); - }); - - it('throws if no encoder runtime is available', async () => { - // @ts-expect-error test override - globalThis.btoa = undefined; - // @ts-expect-error test override - globalThis.Buffer = undefined; - - expect(() => toMatrixID('fail.txt', 'mxc_')).toThrow( - 'No base64 encoder available in this runtime', - ); - }); - - it('throws if no decoder runtime is available', async () => { - // @ts-expect-error test override - globalThis.atob = undefined; - // @ts-expect-error test override - globalThis.Buffer = undefined; - - expect(() => fromMatrixID('mxc_ZmlsZS50eHQ')).toThrow( - 'No base64 decoder available in this runtime', - ); - }); -}); \ No newline at end of file From a774bc33c52d33695083cdf5b440d5299a8b1081 Mon Sep 17 00:00:00 2001 From: Rye Date: Mon, 11 May 2026 17:26:18 +0200 Subject: [PATCH 5/6] Add packages section to pnpm-workspace.yaml --- pnpm-workspace.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 661850f..7c942a8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,3 +2,5 @@ allowBuilds: esbuild: true sharp: true workerd: true +packages: + - '.' \ No newline at end of file From 1c41470ccc09317823ad874b7930468451257054 Mon Sep 17 00:00:00 2001 From: hazre Date: Mon, 11 May 2026 18:05:54 +0200 Subject: [PATCH 6/6] fix: return empty JSON object in multipart redirect responses --- src/proxy.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/proxy.ts b/src/proxy.ts index 172a981..d513c01 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -42,8 +42,8 @@ function decodeMatrixId(rawId: string): string | MatrixError { return id; } type MultipartPartLike = { - headers: Headers; - body?: BodyInit | null; + headers: HeadersInit; + body?: BodyInit; }; function encodeMultipart(boundary: string, parts: MultipartPartLike[]): string { @@ -55,9 +55,7 @@ function encodeMultipart(boundary: string, parts: MultipartPartLike[]): string { lines.push(`${key}: ${value}`); } lines.push(''); - if (typeof part.body === 'string') { - lines.push(part.body); - } + lines.push(part.body ? String(part.body) : ''); } lines.push(`--${boundary}--`, ''); @@ -69,14 +67,14 @@ function buildMultipartRedirect(targetLocation: string): Response { const body = encodeMultipart(boundary, [ { - headers: new Headers({ 'Content-Type': 'application/json' }), + headers: { 'Content-Type': 'application/json' }, body: '{}', }, { - headers: new Headers({ + headers: { 'Content-Type': 'application/octet-stream', - Location: targetLocation, - }), + 'Location': targetLocation, + }, }, ]);