Просмотр исходного кода

core(enterprise): use aws4fetch for smaller bundle size on edge runtimes

Dax Raad 2 месяцев назад
Родитель
Сommit
4000705701
3 измененных файлов с 40 добавлено и 57 удалено
  1. 1 0
      bun.lock
  2. 1 0
      packages/enterprise/package.json
  3. 38 57
      packages/enterprise/src/core/storage.ts

+ 1 - 0
bun.lock

@@ -171,6 +171,7 @@
         "@solidjs/meta": "catalog:",
         "@solidjs/meta": "catalog:",
         "@solidjs/router": "catalog:",
         "@solidjs/router": "catalog:",
         "@solidjs/start": "catalog:",
         "@solidjs/start": "catalog:",
+        "aws4fetch": "^1.0.20",
         "hono": "catalog:",
         "hono": "catalog:",
         "hono-openapi": "catalog:",
         "hono-openapi": "catalog:",
         "luxon": "catalog:",
         "luxon": "catalog:",

+ 1 - 0
packages/enterprise/package.json

@@ -13,6 +13,7 @@
   "dependencies": {
   "dependencies": {
     "@opencode-ai/util": "workspace:*",
     "@opencode-ai/util": "workspace:*",
     "@opencode-ai/ui": "workspace:*",
     "@opencode-ai/ui": "workspace:*",
+    "aws4fetch": "^1.0.20",
     "@pierre/precision-diffs": "catalog:",
     "@pierre/precision-diffs": "catalog:",
     "@solidjs/router": "catalog:",
     "@solidjs/router": "catalog:",
     "@solidjs/start": "catalog:",
     "@solidjs/start": "catalog:",

+ 38 - 57
packages/enterprise/src/core/storage.ts

@@ -1,10 +1,4 @@
-import {
-  S3Client,
-  PutObjectCommand,
-  GetObjectCommand,
-  DeleteObjectCommand,
-  ListObjectsV2Command,
-} from "@aws-sdk/client-s3"
+import { AwsClient } from "aws4fetch"
 import { lazy } from "@opencode-ai/util/lazy"
 import { lazy } from "@opencode-ai/util/lazy"
 
 
 export namespace Storage {
 export namespace Storage {
@@ -15,81 +9,68 @@ export namespace Storage {
     list(prefix: string): Promise<string[]>
     list(prefix: string): Promise<string[]>
   }
   }
 
 
-  function createAdapter(client: S3Client, bucket: string): Adapter {
+  function createAdapter(client: AwsClient, endpoint: string, bucket: string): Adapter {
+    const base = `${endpoint}/${bucket}`
     return {
     return {
       async read(path: string): Promise<string | undefined> {
       async read(path: string): Promise<string | undefined> {
-        try {
-          const command = new GetObjectCommand({
-            Bucket: bucket,
-            Key: path,
-          })
-          const response = await client.send(command)
-          if (!response.Body) return undefined
-          return response.Body.transformToString()
-        } catch (e: any) {
-          if (e.name === "NoSuchKey") return undefined
-          throw e
-        }
+        const response = await client.fetch(`${base}/${path}`)
+        if (response.status === 404) return undefined
+        if (!response.ok) throw new Error(`Failed to read ${path}: ${response.status}`)
+        return response.text()
       },
       },
 
 
       async write(path: string, value: string): Promise<void> {
       async write(path: string, value: string): Promise<void> {
-        const command = new PutObjectCommand({
-          Bucket: bucket,
-          Key: path,
-          Body: value,
-          ContentType: "application/json",
+        const response = await client.fetch(`${base}/${path}`, {
+          method: "PUT",
+          body: value,
+          headers: {
+            "Content-Type": "application/json",
+          },
         })
         })
-        await client.send(command)
+        if (!response.ok) throw new Error(`Failed to write ${path}: ${response.status}`)
       },
       },
 
 
       async remove(path: string): Promise<void> {
       async remove(path: string): Promise<void> {
-        const command = new DeleteObjectCommand({
-          Bucket: bucket,
-          Key: path,
+        const response = await client.fetch(`${base}/${path}`, {
+          method: "DELETE",
         })
         })
-        await client.send(command)
+        if (!response.ok) throw new Error(`Failed to remove ${path}: ${response.status}`)
       },
       },
 
 
       async list(prefix: string): Promise<string[]> {
       async list(prefix: string): Promise<string[]> {
-        const command = new ListObjectsV2Command({
-          Bucket: bucket,
-          Prefix: prefix,
-        })
-        const response = await client.send(command)
-        return response.Contents?.map((c) => c.Key!) || []
+        const params = new URLSearchParams({ "list-type": "2", prefix })
+        const response = await client.fetch(`${base}?${params}`)
+        if (!response.ok) throw new Error(`Failed to list ${prefix}: ${response.status}`)
+        const xml = await response.text()
+        const keys: string[] = []
+        const regex = /<Key>([^<]+)<\/Key>/g
+        let match
+        while ((match = regex.exec(xml)) !== null) {
+          keys.push(match[1])
+        }
+        return keys
       },
       },
     }
     }
   }
   }
 
 
   function s3(): Adapter {
   function s3(): Adapter {
     const bucket = process.env.OPENCODE_STORAGE_BUCKET!
     const bucket = process.env.OPENCODE_STORAGE_BUCKET!
-    const client = new S3Client({
-      region: process.env.OPENCODE_STORAGE_REGION,
-      credentials: process.env.OPENCODE_STORAGE_ACCESS_KEY_ID
-        ? {
-            accessKeyId: process.env.OPENCODE_STORAGE_ACCESS_KEY_ID!,
-            secretAccessKey: process.env.OPENCODE_STORAGE_SECRET_ACCESS_KEY!,
-          }
-        : undefined,
+    const region = process.env.OPENCODE_STORAGE_REGION || "us-east-1"
+    const client = new AwsClient({
+      region,
+      accessKeyId: process.env.OPENCODE_STORAGE_ACCESS_KEY_ID!,
+      secretAccessKey: process.env.OPENCODE_STORAGE_SECRET_ACCESS_KEY!,
     })
     })
-    return createAdapter(client, bucket)
+    return createAdapter(client, `https://s3.${region}.amazonaws.com`, bucket)
   }
   }
 
 
   function r2() {
   function r2() {
     const accountId = process.env.OPENCODE_STORAGE_ACCOUNT_ID!
     const accountId = process.env.OPENCODE_STORAGE_ACCOUNT_ID!
-    const accessKeyId = process.env.OPENCODE_STORAGE_ACCESS_KEY_ID!
-    const secretAccessKey = process.env.OPENCODE_STORAGE_SECRET_ACCESS_KEY!
-    const bucket = process.env.OPENCODE_STORAGE_BUCKET!
-
-    const client = new S3Client({
-      region: "auto",
-      endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
-      credentials: {
-        accessKeyId,
-        secretAccessKey,
-      },
+    const client = new AwsClient({
+      accessKeyId: process.env.OPENCODE_STORAGE_ACCESS_KEY_ID!,
+      secretAccessKey: process.env.OPENCODE_STORAGE_SECRET_ACCESS_KEY!,
     })
     })
-    return createAdapter(client, bucket)
+    return createAdapter(client, `https://${accountId}.r2.cloudflarestorage.com`, process.env.OPENCODE_STORAGE_BUCKET!)
   }
   }
 
 
   const adapter = lazy(() => {
   const adapter = lazy(() => {