working expo updates on iOS (broken on Android for some reason)
diff --git a/package-lock.json b/package-lock.json
index 402b3e2..630a315 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
         "@sanity/client": "^6.6.0",
         "domutils": "^3.1.0",
         "htmlparser2": "^9.0.0",
+        "mime": "^4.0.1",
         "react": "^18.2.0",
         "react-dom": "^18.2.0"
       },
@@ -32,6 +33,18 @@
         "mime": "^3.0.0"
       }
     },
+    "node_modules/@cloudflare/kv-asset-handler/node_modules/mime": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+      "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+      "dev": true,
+      "bin": {
+        "mime": "cli.js"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/@cloudflare/workerd-darwin-64": {
       "version": "1.20231010.0",
       "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20231010.0.tgz",
@@ -1126,15 +1139,17 @@
       }
     },
     "node_modules/mime": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
-      "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
-      "dev": true,
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz",
+      "integrity": "sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA==",
+      "funding": [
+        "https://github.com/sponsors/broofa"
+      ],
       "bin": {
-        "mime": "cli.js"
+        "mime": "bin/cli.js"
       },
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">=16"
       }
     },
     "node_modules/mimic-response": {
@@ -1625,6 +1640,14 @@
       "dev": true,
       "requires": {
         "mime": "^3.0.0"
+      },
+      "dependencies": {
+        "mime": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
+          "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+          "dev": true
+        }
       }
     },
     "@cloudflare/workerd-darwin-64": {
@@ -2302,10 +2325,9 @@
       }
     },
     "mime": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
-      "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
-      "dev": true
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-4.0.1.tgz",
+      "integrity": "sha512-5lZ5tyrIfliMXzFtkYyekWbtRXObT9OWa8IwQ5uxTBDHucNNwniRqo0yInflj+iYi5CBa6qxadGzGarDfuEOxA=="
     },
     "mimic-response": {
       "version": "3.1.0",
diff --git a/package.json b/package.json
index c3c394f..fc13bec 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,7 @@
     "@sanity/client": "^6.6.0",
     "domutils": "^3.1.0",
     "htmlparser2": "^9.0.0",
+    "mime": "^4.0.1",
     "react": "^18.2.0",
     "react-dom": "^18.2.0"
   }
diff --git a/src/deployments/expo.ts b/src/deployments/expo.ts
index 82df7b9..0f83d0f 100644
--- a/src/deployments/expo.ts
+++ b/src/deployments/expo.ts
@@ -1,19 +1,30 @@
 // Aero
 // Copyright (c) jottocraft 2023
 
+import mime from "mime";
+import { createClient } from "@sanity/client";
+
 import { isSafe } from "../util/isSafe";
 import { serveR2 } from "../util/r2";
 
 export function expoRouter(request: Request, env: Env, ctx: AeroCtx, product: string, pathname: string): Promise<Response> {
-    const [, resource, key] = pathname.match(/\/(.+?)(?:$|\/(.*?)(?:$|\/))/) ?? [];
+    const [, resource, filename] = pathname.match(/\/(.+?)(?:$|\/(.*?)(?:$|\/))/) ?? [];
 
     switch (resource) {
         case "protocol1": return serveExpoProtocol1(request, env, ctx, product, pathname);
-        case "assets": return serveExpoAsset(request, env, ctx, product, key);
+        case "assets": return serveExpoAsset(request, env, ctx, product, filename);
         default: return serveExpoManifest(request, env, ctx, product, resource);
     }
 }
 
+const EXPO_CHANNEL_NAMES = {
+    staging: 4,
+    techpreview: 3,
+    alpha: 2,
+    beta: 1,
+    production: 0
+};
+
 async function serveExpoProtocol1(request: Request, env: Env, ctx: AeroCtx, product: string, pathname: string) {
     const url = new URL(request.url);
 
@@ -21,15 +32,37 @@
     const runtime = request.headers.get("Expo-Runtime-Version") ?? url.searchParams.get("runtime-version");
     const channel = request.headers.get("Expo-Channel-Name") ?? url.searchParams.get("channel-name");
 
-    return new Response(null, { status: 501 });
+    if ((platform !== "android" && platform !== "ios") || !runtime || !isSafe(runtime) || !channel || !Object.keys(EXPO_CHANNEL_NAMES).includes(channel)) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=400");
+
+    const client = createClient({
+        projectId: "xk715cpi",
+        dataset: "production",
+        useCdn: true,
+        apiVersion: "2023-09-20",
+        token: env.SANITY_PRODUCTS_TOKEN,
+        perspective: "published"
+    });
+
+    const manifest = await client.fetch<string | null>(`*[_type == "release" && channel <= $channel && count(artifacts[_type == "expo" && platform == $platform && runtime == $runtime]) == 1] {
+        "manifest": select(artifacts[_type == "expo" && platform == $platform && runtime == $runtime])[0].manifest,
+        version
+    } | order(version desc)[0].manifest`, {
+        platform,
+        runtime,
+        channel: EXPO_CHANNEL_NAMES[channel as keyof typeof EXPO_CHANNEL_NAMES]
+    });
+
+    if (!manifest) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=404");
+
+    return serveExpoManifest(request, env, ctx, product, manifest, channel);
 }
 
-async function serveExpoAsset(request: Request, env: Env, ctx: AeroCtx, product: string, key: string) {
-    if (!isSafe(key)) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=400");
-    return serveR2(request, env, `${product}/expo/${key}`);
+async function serveExpoAsset(request: Request, env: Env, ctx: AeroCtx, product: string, filename: string) {
+    if (!isSafe(filename)) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=400");
+    return serveR2(request, env, ctx, `${product}/expo/${filename}`, "public, max-age=31536000, immutable", mime.getType(filename.slice(filename.indexOf(".") + 1)) ?? "");
 }
 
-async function serveExpoManifest(request: Request, env: Env, ctx: AeroCtx, product: string, manifestID: string) {
+async function serveExpoManifest(request: Request, env: Env, ctx: AeroCtx, product: string, manifestID: string, channel?: string) {
     if (!isSafe(manifestID)) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=400");
 
     const object = await env.ARTIFACTS.get(`${product}/expo/${manifestID}.json`);
@@ -37,12 +70,12 @@
     const buildManifest = await object.json<any>();
 
     // Add URLs to the manifest
-    buildManifest.launchAsset.url = `https://aero.jottocraft.com/${product}/expo/assets/${buildManifest.launchAsset.key}`;
-    buildManifest.assets.forEach((asset: any) => asset.url = `https://aero.jottocraft.com/${product}/expo/assets/${asset.key}`)
+    buildManifest.launchAsset.url = `https://aero.jottocraft.com/${product}/expo/assets/${buildManifest.launchAsset.key}.js`;
+    buildManifest.assets.forEach((asset: any) => asset.url = `https://aero.jottocraft.com/${product}/expo/assets/${asset.key}${asset.fileExtension}`);
 
     const boundary = "jottocraft-Aero-Boundary-" + crypto.randomUUID();
 
-    let multipartBody = [""];
+    let multipartBody = [];
     multipartBody.push("--" + boundary);
     multipartBody.push("Content-Disposition: inline; name=\"manifest\"");
     multipartBody.push("Content-Type: application/json\n");
@@ -50,13 +83,19 @@
     multipartBody.push(JSON.stringify(buildManifest));
 
     multipartBody.push("--" + boundary + "--");
+    multipartBody.push("");
+
+    const headers = new Headers({
+        "Content-Type": "multipart/mixed; boundary=" + boundary,
+        "Cache-Control": "cache-control: private, max-age=0",
+        "Expo-Protocol-Version": "1",
+        "Expo-SFV-Version": "0"
+    });
+
+    if (channel) headers.set("Expo-Manifest-Filters", `channel="${channel}"`);
 
     return new Response(multipartBody.join("\r\n"), {
         status: 200,
-        headers: {
-            "Content-Type": "multipart/mixed; boundary=" + boundary,
-            "Expo-Protocol-Version": "1",
-            "Expo-SFV-Version": "0"
-        }
+        headers
     })
 }
\ No newline at end of file
diff --git a/src/deployments/msix.tsx b/src/deployments/msix.tsx
index 36d7564..ce59c01 100644
--- a/src/deployments/msix.tsx
+++ b/src/deployments/msix.tsx
@@ -131,7 +131,7 @@
     const deployment = release.deployments.find(deployment => deployment._type === "msix");
     if (!deployment || (deployment._type !== "msix")) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=404");
 
-    return serveR2(reqData.request, reqData.env, `${release.product.identity}/msix/${deployment.folder}/${release.product.identity}.msixbundle`);
+    return serveR2(reqData.request, reqData.env, reqData.ctx, `${release.product.identity}/msix/${deployment.folder}/${release.product.identity}.msixbundle`);
 }
 
 export async function serveDependency(reqData: MSIXRequestData) {
@@ -142,7 +142,7 @@
     const deployment = release.deployments.find(deployment => deployment._type === "msix");
     if (!deployment || (deployment._type !== "msix")) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=404");
 
-    return serveR2(reqData.request, reqData.env, `${release.product.identity}/msix/${deployment.folder}/Dependencies/${arg}`);
+    return serveR2(reqData.request, reqData.env, reqData.ctx, `${release.product.identity}/msix/${deployment.folder}/Dependencies/${arg}`);
 }
 
 export async function serveMsAppInstaller(reqData: MSIXRequestData) {
diff --git a/src/index.ts b/src/index.ts
index 7d1999b..a113d28 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -27,9 +27,13 @@
 			}
 		})();
 
-		const modifiable = new Response(response.body, response);
-		if (headersPatch.current) applyModifications(modifiable.headers, headersPatch.current);
+		return response;
 
-		return modifiable;
+		//const modifiable = new Response(response.body, response);
+		//if (headersPatch.current) applyModifications(modifiable.headers, headersPatch.current);
+//
+		//for (const [h, v] of modifiable.headers.entries()) console.log(h + ": " + v);
+//
+		//return modifiable;
 	}
 };
diff --git a/src/util/r2.ts b/src/util/r2.ts
index ba4d432..a7ed19e 100644
--- a/src/util/r2.ts
+++ b/src/util/r2.ts
@@ -5,7 +5,7 @@
 // hits R2 HARD with Range-d requests when installing (I'm talking 300+ REQUESTS PER MINUTE from a single client...)
 // MAKE SURE RANGED REQUESTS WORK AND ARE EFFICIENT!!! OTHERWISE INFRA WILL NOT BE HAPPY!!!
 
-export async function serveR2(request: Request, env: Env, key: string) {
+export async function serveR2(request: Request, env: Env, ctx: ExecutionContext, key: string, cacheControl?: string, contentType?: string) {
     if (request.method === "HEAD") {
         const object = await env.ARTIFACTS.head(key);
         if (object === null) return env.HTTP_STATUS_PAGES.fetch("https://http-status-pages.jottocraft.com/?status=404");
@@ -13,8 +13,10 @@
         const headers = new Headers();
         object.writeHttpMetadata(headers);
         headers.set("Etag", object.httpEtag);
-        headers.set("Content-Type", "application/octet-stream");
+        headers.set("Content-Type", contentType ?? "application/octet-stream");
+        headers.set("Cache-Control", (cacheControl ? cacheControl + ", " : "") + "no-transform");
         headers.set("Accept-Ranges", "bytes");
+        headers.set("Content-Length", String(object.size));
 
         return new Response(null, {
             status: 200,
@@ -22,6 +24,8 @@
         });
     }
 
+    const hasRequestedRange = request.headers.get("Range") !== null;
+
     const object = await env.ARTIFACTS.get(key, {
         range: request.headers,
         onlyIf: request.headers
@@ -32,18 +36,20 @@
     const headers = new Headers();
     object.writeHttpMetadata(headers);
     headers.set("ETag", object.httpEtag);
-    if (object.range) {
+    headers.set("Content-Type", contentType ?? "application/octet-stream");
+    headers.set("Cache-Control", (cacheControl ? cacheControl + ", " : "") + "no-transform");
+    headers.set("Accept-Ranges", "bytes");
+
+    if (hasRequestedRange && object.range) {
         if (("offset" in object.range) || ("length" in object.range)) {
             headers.set("Content-Range", `bytes ${object.range.offset ?? 0}-${object.range.length !== undefined ? (object.range.offset ?? 0) + (object.range.length - 1) : object.size - 1}/${object.size}`);
         } else if ("suffix" in object.range) {
             headers.set("Content-Range", `bytes ${object.size - object.range.suffix}-${object.size - 1}/${object.size}`);
         }
     }
-    headers.set("Content-Type", "application/octet-stream");
-    headers.set("Accept-Ranges", "bytes");
 
-    return new Response(object.body ?? null, {
-        status: object.body ? (request.headers.get("Range") !== null ? 206 : 200) : 304,
+    return new Response(object.body ? await object.arrayBuffer() : null, {
+        status: object.body ? (hasRequestedRange ? 206 : 200) : 304,
         headers
     });
 }
\ No newline at end of file