| 1 | // Cloudflare Workers compatibility patches for @atproto libraries. |
| 2 | // |
| 3 | // 1. Workers don't support `redirect: 'error'` — simulate it with 'manual'. |
| 4 | // 2. Workers don't support the standard `cache` option in Request — strip it. |
| 5 | |
| 6 | function sanitizeInit(init?: RequestInit): RequestInit | undefined { |
| 7 | if (!init) return init; |
| 8 | const { cache, redirect, ...rest } = init; |
| 9 | return { |
| 10 | ...rest, |
| 11 | // Workers only support 'follow' and 'manual' |
| 12 | redirect: redirect === "error" ? "manual" : redirect, |
| 13 | // Workers don't support standard cache modes — omit entirely |
| 14 | ...(cache ? {} : {}), |
| 15 | }; |
| 16 | } |
| 17 | |
| 18 | const errorRedirectRequests = new WeakSet<Request>(); |
| 19 | const OriginalRequest = globalThis.Request; |
| 20 | |
| 21 | globalThis.Request = class extends OriginalRequest { |
| 22 | constructor( |
| 23 | input: RequestInfo | URL, |
| 24 | init?: RequestInit, |
| 25 | ) { |
| 26 | super(input, sanitizeInit(init)); |
| 27 | if (init?.redirect === "error") { |
| 28 | errorRedirectRequests.add(this); |
| 29 | } |
| 30 | } |
| 31 | } as typeof Request; |
| 32 | |
| 33 | const originalFetch = globalThis.fetch; |
| 34 | globalThis.fetch = (async ( |
| 35 | input: RequestInfo | URL, |
| 36 | init?: RequestInit, |
| 37 | ): Promise<Response> => { |
| 38 | const cleanInit = sanitizeInit(init); |
| 39 | const response = await originalFetch(input, cleanInit); |
| 40 | |
| 41 | // Simulate redirect: 'error' — throw on 3xx |
| 42 | const wantsRedirectError = |
| 43 | init?.redirect === "error" || |
| 44 | (input instanceof Request && errorRedirectRequests.has(input)); |
| 45 | |
| 46 | if (wantsRedirectError && response.status >= 300 && response.status < 400) { |
| 47 | throw new TypeError("unexpected redirect"); |
| 48 | } |
| 49 | |
| 50 | return response; |
| 51 | }) as typeof fetch; |