Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
allowBuilds:
esbuild: true
sharp: true
workerd: true
packages:
- '.'
36 changes: 10 additions & 26 deletions src/mxcId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, '');
}

/**
Expand All @@ -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);
}

/**
Expand Down
47 changes: 34 additions & 13 deletions src/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,42 @@ function decodeMatrixId(rawId: string): string | MatrixError {

return id;
}
type MultipartPartLike = {
headers: HeadersInit;
body?: BodyInit;
};

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('');
lines.push(part.body ? String(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: { 'Content-Type': 'application/json' },
body: '{}',
},
{
headers: {
'Content-Type': 'application/octet-stream',
'Location': targetLocation,
},
},
]);

return new Response(body, {
headers: {
Expand Down
52 changes: 0 additions & 52 deletions test/mxcId.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
);
});
});
4 changes: 1 addition & 3 deletions wrangler.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading