diff --git a/.dev.vars.example b/.dev.vars.example
new file mode 100644
index 0000000..0eb49c9
--- /dev/null
+++ b/.dev.vars.example
@@ -0,0 +1,27 @@
+# Local-only Cloudflare Worker bindings. Copy to .dev.vars and replace each value.
+# Secrets must never be committed.
+# AUTH_KV, FOLDER_CACHE, and MPU are configured bindings in wrangler.jsonc, not .dev.vars values.
+
+# S3 Signature V4 credentials held by CLI clients or your BFF.
+ACCESS_KEY=replace-with-access-key-id
+SECRET_KEY=replace-with-long-random-secret
+REGION=auto
+
+# Google OAuth credentials with Drive API access.
+GOOGLE_CLIENT_ID=replace-with-google-oauth-client-id
+GOOGLE_CLIENT_SECRET=replace-with-google-oauth-client-secret
+GOOGLE_REFRESH_TOKEN=replace-with-google-refresh-token
+
+# Comma-separated, exact bucket names. Unset denies every bucket.
+ALLOWED_BUCKETS=assets
+# Optional comma-separated subset of ALLOWED_BUCKETS that permits unsigned GET/HEAD.
+PUBLIC_READ_BUCKETS=
+
+# Durable Object multipart uploads and ETag result behavior.
+ALLOW_MULTIPART=true
+ETAG_STYLE=md5
+
+# Browser CORS: comma-separated exact origins or *. Leave empty to disable CORS.
+CORS_ALLOWED_ORIGINS=http://localhost:5173
+# Set false to disable /docs and /openapi.yaml.
+ENABLE_DOCS=true
diff --git a/README.md b/README.md
index 2fe24f9..3603d6d 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,10 @@ I.R.I.S. (Integrated Reliable Interop Storage)
Use Cloudflare Workers to turn your Google Drive into S3 object storage at no extra cost.
+## API documentation
+
+With `ENABLE_DOCS` left enabled (the default), each deployment serves an interactive API reference at `/docs` and the raw OpenAPI 3.1 document at `/openapi.yaml`. Frontend-specific setup, authentication details, limitations, and runnable examples live in [`docs/`](./docs/README.md).
+
## About
This is a Workers script that converts the Google Drive API into an S3-compatible API. Turn your Google Drive into object storage at no extra cost!
@@ -74,6 +78,8 @@ https://developers.cloudflare.com/workers/configuration/secrets/#via-the-dashboa
| `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, `GOOGLE_REFRESH_TOKEN` | Google API credentials obtained from rclone. |
| `ALLOWED_BUCKETS` | Set the buckets allowed, separated by `,`. A directory with the bucket name will be created directly under Google Drive. |
| `PUBLIC_READ_BUCKETS` | *(Optional)* Buckets that allow unauthenticated GET/HEAD access without signature, separated by `,`. Write operations (PUT/POST/DELETE) still require authentication. Must be a subset of `ALLOWED_BUCKETS`. |
+| `CORS_ALLOWED_ORIGINS` | *(Optional)* Comma-separated exact browser origins, or `*`. Unset emits no CORS headers. |
+| `ENABLE_DOCS` | *(Optional)* Set to `false` to disable `/docs` and `/openapi.yaml`; enabled by default. |
### 4. Enable Multipart Uploads
@@ -95,5 +101,12 @@ Google Drive's free tier has 15 GB total storage, and Google applies a 750 GB da
### 5. CORS Configuration
-If you need to configure CORS, set up your own domain for Workers and use Cloudflare's Response Header Transform Rules to add the necessary headers.
-https://developers.cloudflare.com/rules/transform/response-header-modification/
+I.R.I.S. provides native, deny-by-default CORS handling. Set `CORS_ALLOWED_ORIGINS` to a comma-separated list of exact origins:
+
+```ini
+CORS_ALLOWED_ORIGINS=https://app.example.com,http://localhost:5173
+```
+
+Use `*` only for a public, credential-free integration. Leave the value unset to emit no CORS headers, preserving CLI-only behavior. Allowed origins receive preflight support for S3 methods and the browser-readable response headers `ETag`, `Content-Range`, `Content-Length`, `Last-Modified`, `Accept-Ranges`, and `x-amz-request-id`. Server-to-server clients do not need CORS: requests without an `Origin` retain their normal S3 response and receive no `Access-Control-*` headers. Exact origin allow-lists add the harmless `Vary: Origin` response header for cache correctness.
+
+For browser uploads, keep `SECRET_KEY` in a BFF and give the browser short-lived presigned URLs. See the [frontend integration guide](./docs/integration-guide.md).
diff --git a/biome.json b/biome.json
index 653f73c..c48d2de 100644
--- a/biome.json
+++ b/biome.json
@@ -7,7 +7,7 @@
},
"files": {
"ignoreUnknown": true,
- "includes": ["**", "!node_modules", "!.next", "!dist", "!build", "!src/app/globals.css"]
+ "includes": ["**", "!node_modules", "!.next", "!dist", "!build", "!src/app/globals.css", "!worker-configuration.d.ts"]
},
"formatter": {
"lineWidth": 320,
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..a89e4e5
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,29 @@
+# I.R.I.S. frontend integration docs
+
+I.R.I.S. exposes a focused S3-compatible API on top of Google Drive. The live API reference is available from every enabled Worker deployment at [`/docs`](/docs), with the source OpenAPI document at [`/openapi.yaml`](/openapi.yaml).
+
+## Start here
+
+1. Configure the Worker with `CORS_ALLOWED_ORIGINS` for every browser origin that will upload or download objects.
+2. Keep `SECRET_KEY` in a backend-for-frontend (BFF), not in browser code.
+3. Let the BFF issue short-lived presigned URLs for a constrained bucket/key/method.
+4. Upload or download through the presigned URL from the browser.
+
+| Document | Purpose |
+| --- | --- |
+| [Integration guide](./integration-guide.md) | Browser upload, multipart upload, listing, CORS, and recommended architecture. |
+| [Authentication](./authentication.md) | The precise AWS Signature V4 behavior enforced by I.R.I.S. |
+| [Limitations](./limitations.md) | Unsupported S3 features and Google Drive operational limits. |
+| [`examples/bff-presign.ts`](./examples/bff-presign.ts) | A Workers BFF endpoint that creates short-lived PUT URLs. |
+| [`examples/browser-upload.ts`](./examples/browser-upload.ts) | Single object upload from a browser. |
+| [`examples/browser-multipart.ts`](./examples/browser-multipart.ts) | Strictly sequential browser multipart upload. |
+
+## Browser security model
+
+```text
+Browser ── POST /presign ──> BFF (holds SECRET_KEY)
+ │ │
+ └──── presigned S3 request ────┴──> I.R.I.S. Worker ──> Google Drive
+```
+
+The browser never receives `SECRET_KEY`. Presigned URLs must be short lived and scoped to the single object operation the browser needs.
diff --git a/docs/authentication.md b/docs/authentication.md
new file mode 100644
index 0000000..046fb1d
--- /dev/null
+++ b/docs/authentication.md
@@ -0,0 +1,39 @@
+# Authentication and Signature V4
+
+I.R.I.S. accepts AWS Signature Version 4 in either form:
+
+- an `Authorization: AWS4-HMAC-SHA256 …` header with `x-amz-date`; or
+- a presigned request with `X-Amz-Algorithm`, `X-Amz-Credential`, `X-Amz-Date`, `X-Amz-SignedHeaders`, and `X-Amz-Signature` query parameters.
+
+The `Credential` access-key ID must equal the Worker's `ACCESS_KEY`; the signing key is derived from `SECRET_KEY`, `REGION`, service `s3`, and the `YYYYMMDD` request date.
+
+## Presigned URL expiry
+
+Query-authenticated requests require `X-Amz-Expires` from 1 through 604800 seconds (seven days). The Worker returns `403 AccessDenied` with `Request has expired` when it is missing, invalid, out of range, or its signed timestamp plus expiry is in the past.
+
+BFFs should normally use a much shorter period such as five minutes.
+
+## Header-authenticated requests
+
+The Worker requires a valid `x-amz-date` and rejects a request when the server time differs from it by more than 15 minutes. This returns `403 RequestTimeTooSkewed`.
+
+## Canonical request behavior
+
+The canonical request is built from:
+
+1. method;
+2. URL path;
+3. sorted query string (excluding `X-Amz-Signature` for presigned URLs);
+4. the signed-header list and normalized values;
+5. the signed-header list; and
+6. `x-amz-content-sha256`, defaulting to `UNSIGNED-PAYLOAD`.
+
+I.R.I.S. deliberately canonicalizes a signed `accept-encoding` header to `identity`. Cloudflare can rewrite the received value at the edge; S3 SDKs that sign this header use `identity` for this reason. Browser code must not sign `accept-encoding` because browser networking controls it.
+
+Payload hashes are **not** verified. Browser and BFF clients should use `x-amz-content-sha256: UNSIGNED-PAYLOAD`; this is a deliberate streaming limitation, not an integrity guarantee.
+
+## Public-read buckets
+
+Buckets listed in `PUBLIC_READ_BUCKETS` permit unsigned `GET` and `HEAD` requests. All write operations still require valid Signature V4 authentication. `PUBLIC_READ_BUCKETS` must be a subset of `ALLOWED_BUCKETS`.
+
+For a tested signing reference, see the `signed()` helper in [`test/s3.test.ts`](../test/s3.test.ts).
diff --git a/docs/examples/bff-presign.ts b/docs/examples/bff-presign.ts
new file mode 100644
index 0000000..9a27134
--- /dev/null
+++ b/docs/examples/bff-presign.ts
@@ -0,0 +1,95 @@
+import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, PutObjectCommand, S3Client, UploadPartCommand } from "@aws-sdk/client-s3";
+import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
+
+interface Env {
+ IRIS_ENDPOINT: string;
+ IRIS_ACCESS_KEY: string;
+ IRIS_SECRET_KEY: string;
+ IRIS_REGION: string;
+ IRIS_BUCKET: string;
+}
+
+interface PresignRequest {
+ key: string;
+ contentType?: string;
+ operation?: "put" | "createMultipart" | "uploadPart" | "completeMultipart" | "abortMultipart";
+ uploadId?: string;
+ partNumber?: number;
+ parts?: Array<{ ETag: string; PartNumber: number }>;
+}
+
+function escapeXml(value: string): string {
+ return value.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'");
+}
+
+function completionXml(parts: Array<{ ETag: string; PartNumber: number }> | undefined): string | null {
+ if (!parts?.length || parts.some((part, index) => !Number.isInteger(part.PartNumber) || part.PartNumber !== index + 1 || typeof part.ETag !== "string" || part.ETag.length === 0)) return null;
+ return `${parts.map((part) => `${part.PartNumber}${escapeXml(part.ETag)}`).join("")}`;
+}
+
+function json(data: unknown, status = 200): Response {
+ return Response.json(data, { status, headers: { "Cache-Control": "no-store" } });
+}
+
+function s3(env: Env): S3Client {
+ return new S3Client({
+ endpoint: env.IRIS_ENDPOINT,
+ region: env.IRIS_REGION,
+ forcePathStyle: true,
+ credentials: { accessKeyId: env.IRIS_ACCESS_KEY, secretAccessKey: env.IRIS_SECRET_KEY },
+ });
+}
+
+function validKey(key: unknown): key is string {
+ return typeof key === "string" && key.length > 0 && key.length <= 1024 && !key.startsWith("/") && !key.split("/").includes("..");
+}
+
+export default {
+ async fetch(request: Request, env: Env): Promise {
+ if (request.method !== "POST" || new URL(request.url).pathname !== "/presign") return new Response("Not found", { status: 404 });
+
+ let input: PresignRequest;
+ try {
+ input = await request.json();
+ } catch {
+ return json({ error: "Expected JSON body" }, 400);
+ }
+ if (!validKey(input.key)) return json({ error: "Invalid object key" }, 400);
+
+ const client = s3(env);
+ const operation = input.operation ?? "put";
+ const expiresIn = 5 * 60;
+
+ if (operation === "put") {
+ const contentType = input.contentType || "application/octet-stream";
+ const url = await getSignedUrl(client, new PutObjectCommand({ Bucket: env.IRIS_BUCKET, Key: input.key, ContentType: contentType }), { expiresIn });
+ return json({ url, method: "PUT", headers: { "Content-Type": contentType }, expiresIn });
+ }
+
+ if (operation === "createMultipart") {
+ const contentType = input.contentType || "application/octet-stream";
+ const url = await getSignedUrl(client, new CreateMultipartUploadCommand({ Bucket: env.IRIS_BUCKET, Key: input.key, ContentType: contentType }), { expiresIn });
+ return json({ url, method: "POST", headers: { "Content-Type": contentType }, expiresIn });
+ }
+
+ if (!input.uploadId) return json({ error: "uploadId is required" }, 400);
+ if (operation === "uploadPart") {
+ const partNumber = input.partNumber;
+ if (!Number.isInteger(partNumber) || partNumber === undefined || partNumber < 1 || partNumber > 10_000) return json({ error: "partNumber must be 1 through 10000" }, 400);
+ const url = await getSignedUrl(client, new UploadPartCommand({ Bucket: env.IRIS_BUCKET, Key: input.key, UploadId: input.uploadId, PartNumber: partNumber }), { expiresIn });
+ return json({ url, method: "PUT", expiresIn });
+ }
+ if (operation === "completeMultipart") {
+ const body = completionXml(input.parts);
+ if (!body) return json({ error: "parts must have sequential PartNumber values beginning at 1 and non-empty ETag values" }, 400);
+ const url = await getSignedUrl(client, new CompleteMultipartUploadCommand({ Bucket: env.IRIS_BUCKET, Key: input.key, UploadId: input.uploadId, MultipartUpload: { Parts: input.parts } }), { expiresIn });
+ return json({ url, method: "POST", headers: { "Content-Type": "application/xml" }, body, expiresIn });
+ }
+ if (operation === "abortMultipart") {
+ const url = await getSignedUrl(client, new AbortMultipartUploadCommand({ Bucket: env.IRIS_BUCKET, Key: input.key, UploadId: input.uploadId }), { expiresIn });
+ return json({ url, method: "DELETE", expiresIn });
+ }
+
+ return json({ error: "Unsupported operation" }, 400);
+ },
+} satisfies ExportedHandler;
diff --git a/docs/examples/browser-multipart.ts b/docs/examples/browser-multipart.ts
new file mode 100644
index 0000000..56ff2f0
--- /dev/null
+++ b/docs/examples/browser-multipart.ts
@@ -0,0 +1,48 @@
+interface PresignResponse {
+ url: string;
+ method: string;
+ headers?: Record;
+ body?: string;
+}
+
+async function presign(body: Record): Promise {
+ const response = await fetch("/presign", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(body),
+ });
+ if (!response.ok) throw new Error(await response.text());
+ return response.json();
+}
+
+function uploadIdFromXml(xml: string): string {
+ const value = /([^<]+)<\/UploadId>/.exec(xml)?.[1];
+ if (!value) throw new Error("CreateMultipartUpload did not return UploadId");
+ return value;
+}
+
+/** Uploads parts in order. I.R.I.S. does not allow parallel or out-of-order part uploads. */
+export async function uploadLargeFile(file: File, key: string, partSize = 16 * 1024 * 1024): Promise {
+ const contentType = file.type || "application/octet-stream";
+ const create = await presign({ key, contentType, operation: "createMultipart" });
+ const createResponse = await fetch(create.url, { method: create.method, headers: create.headers });
+ if (!createResponse.ok) throw new Error(await createResponse.text());
+ const uploadId = uploadIdFromXml(await createResponse.text());
+
+ const parts: Array<{ ETag: string; PartNumber: number }> = [];
+ for (let index = 0, offset = 0; offset < file.size; index++, offset += partSize) {
+ const partNumber = index + 1;
+ const body = file.slice(offset, Math.min(offset + partSize, file.size));
+ const signed = await presign({ key, uploadId, partNumber, operation: "uploadPart" });
+ const response = await fetch(signed.url, { method: signed.method, body });
+ if (!response.ok) throw new Error(`Part ${partNumber} failed: ${await response.text()}`);
+ const etag = response.headers.get("ETag");
+ if (!etag) throw new Error(`Part ${partNumber} did not return ETag; check CORS_ALLOWED_ORIGINS`);
+ parts.push({ ETag: etag, PartNumber: partNumber });
+ }
+
+ const complete = await presign({ key, uploadId, parts, operation: "completeMultipart" });
+ if (!complete.body) throw new Error("BFF did not return the completion XML body");
+ const completeResponse = await fetch(complete.url, { method: complete.method, headers: complete.headers, body: complete.body });
+ if (!completeResponse.ok) throw new Error(await completeResponse.text());
+}
diff --git a/docs/examples/browser-upload.ts b/docs/examples/browser-upload.ts
new file mode 100644
index 0000000..3e5edfe
--- /dev/null
+++ b/docs/examples/browser-upload.ts
@@ -0,0 +1,29 @@
+interface PresignedPut {
+ url: string;
+ method: "PUT";
+ headers: { "Content-Type": string };
+ expiresIn: number;
+}
+
+async function getPresignedPut(key: string, contentType: string): Promise {
+ const response = await fetch("/presign", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ key, contentType, operation: "put" }),
+ });
+ if (!response.ok) throw new Error(await response.text());
+ return response.json();
+}
+
+export async function uploadFile(file: File, key: string): Promise {
+ const signed = await getPresignedPut(key, file.type || "application/octet-stream");
+ const response = await fetch(signed.url, {
+ method: signed.method,
+ headers: signed.headers,
+ body: file,
+ });
+ if (!response.ok) throw new Error(`Upload failed: ${await response.text()}`);
+
+ // Requires CORS_ALLOWED_ORIGINS to include this page's exact origin.
+ return response.headers.get("ETag");
+}
diff --git a/docs/integration-guide.md b/docs/integration-guide.md
new file mode 100644
index 0000000..8ea42ae
--- /dev/null
+++ b/docs/integration-guide.md
@@ -0,0 +1,98 @@
+# Frontend integration guide
+
+## Architecture
+
+Use a backend-for-frontend (BFF) as the trust boundary:
+
+```text
+Browser → BFF (holds ACCESS_KEY + SECRET_KEY, issues presigned URLs) → I.R.I.S. Worker → Google Drive
+```
+
+Never put `SECRET_KEY` in browser code, a static site, or a public environment variable. The browser receives only a short-lived presigned URL for one method and object key.
+
+## Enable browser access
+
+Configure the Worker with an explicit allow-list of web origins:
+
+```ini
+CORS_ALLOWED_ORIGINS=https://app.example.com,http://localhost:5173
+```
+
+Set it to `*` only for a genuinely public, credential-free integration. Leaving it unset emits no CORS headers. I.R.I.S. exposes `ETag`, `Content-Range`, `Content-Length`, `Last-Modified`, `Accept-Ranges`, and `x-amz-request-id`, so browser code can inspect the headers that form the effective S3 response payload.
+
+### Server-to-server compatibility
+
+CORS does not restrict non-browser clients. `Access-Control-*` headers are emitted only when a request has an allowed `Origin`; a CLI, Dokploy, rclone, aws-cli, or other service request with no `Origin` keeps its normal S3 status and body. With an exact origin allow-list, responses also include `Vary: Origin`, including requests without an `Origin`, so shared caches cannot replay a no-origin object response to a browser. S3 clients ignore this cache-control metadata.
+
+An `OPTIONS` request with no `Origin`, or a preflight from a disallowed origin, receives the same bare `204` as a CORS-disabled Worker. A disallowed `Origin` never changes the underlying S3 result—it simply receives no `Access-Control-*` headers.
+
+## Presign in the BFF
+
+Use `@aws-sdk/s3-request-presigner` and send a limited expiry. I.R.I.S. validates `X-Amz-Expires` and rejects URLs after their signing time plus that value.
+
+```ts
+import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
+import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
+
+const client = new S3Client({
+ endpoint: env.IRIS_ENDPOINT,
+ region: env.IRIS_REGION,
+ credentials: {
+ accessKeyId: env.IRIS_ACCESS_KEY,
+ secretAccessKey: env.IRIS_SECRET_KEY,
+ },
+ forcePathStyle: true,
+});
+
+const url = await getSignedUrl(
+ client,
+ new PutObjectCommand({
+ Bucket: "assets",
+ Key: "uploads/avatar.png",
+ ContentType: "image/png",
+ }),
+ { expiresIn: 300 },
+);
+```
+
+Keep the `ContentType` in the command and send precisely that same `Content-Type` from the browser. It becomes the Drive `mimeType`. A non-simple content type causes a preflight request, which is why the Worker CORS configuration is necessary.
+
+## Upload one object
+
+Use `Blob`, `File`, `ArrayBuffer`, or a typed array as the body. This lets the browser provide a known body length. Do **not** use a `ReadableStream` unless the BFF signs `x-amz-decoded-content-length` and the browser sends it: multipart part uploads require `Content-Length` or `x-amz-decoded-content-length`.
+
+```ts
+const response = await fetch(presignedUrl, {
+ method: "PUT",
+ headers: { "Content-Type": file.type || "application/octet-stream" },
+ body: file,
+});
+
+if (!response.ok) throw new Error(await response.text());
+const etag = response.headers.get("ETag");
+```
+
+`ETag` is readable only when the request `Origin` is configured in `CORS_ALLOWED_ORIGINS`.
+
+## Multipart upload
+
+Cloudflare request limits mean a single PUT is best kept below roughly 100 MB. For larger files, have the BFF initiate the upload, issue a presigned URL for each part, and complete the upload after the browser returns all ETags.
+
+Use 16 MB parts as a practical baseline. Upload parts **strictly sequentially starting at 1**. I.R.I.S. waits up to 20 seconds for an earlier part; out-of-order work then receives `503 SlowDown`. Requests more than 64 part numbers ahead are rejected immediately.
+
+1. BFF signs `POST /bucket/key?uploads` and starts the multipart upload.
+2. BFF issues a signed `PUT` URL for part 1; browser uploads it and records the `ETag`.
+3. Repeat one part at a time for parts 2 through $n$.
+4. BFF signs `POST /bucket/key?uploadId=…`; browser sends the `CompleteMultipartUpload` XML body containing the sequential part numbers and ETags.
+
+See [`examples/browser-multipart.ts`](./examples/browser-multipart.ts) for a complete browser-side loop.
+
+## Build a file browser
+
+List with `prefix` and `delimiter=/`:
+
+```text
+GET /assets?prefix=photos/&delimiter=/
+```
+
+Files appear in `Contents`; immediate child folders appear as `CommonPrefixes`. Google Drive directories are physical folders, not zero-byte marker objects. Read [limitations](./limitations.md) before designing pagination or rename/move features.
diff --git a/docs/limitations.md b/docs/limitations.md
new file mode 100644
index 0000000..d60ca7e
--- /dev/null
+++ b/docs/limitations.md
@@ -0,0 +1,35 @@
+# Limitations
+
+I.R.I.S. is a narrow S3 compatibility layer. Design frontend features around the behavior below.
+
+## Listing is not pageable
+
+`max-keys`, `continuation-token`, and `marker` are ignored. I.R.I.S. scans at most 5000 Google Drive nodes and then sets `IsTruncated=true`; it does not provide a continuation token, so additional results cannot be requested. The XML value `MaxKeys=1000` is a fixed compatibility value, not the real scan limit.
+
+Use narrow prefixes and `delimiter=/` for a file browser. Do not build an infinite-scrolling full-drive browser.
+
+## No server-side rename or move
+
+`CopyObject` and `UploadPartCopy` return `501 NotImplemented`. There is no server-side rename or move because both require copying. Implement rename/move as download, re-upload under the target key, then delete the original—subject to bandwidth, time, and error handling.
+
+## Unsupported S3 features
+
+- `ListBuckets`, `CreateBucket`, and `DeleteBucket`
+- batch `DeleteObjects`
+- object versioning, ACLs, object tags, lifecycle rules, and `x-amz-meta-*` user metadata
+- CopyObject and UploadPartCopy
+- discovering active multipart uploads: `ListMultipartUploads` always reports an empty list
+
+Bucket `?acl`, `?versioning`, and `?location` requests are currently treated as regular bucket listing requests rather than errors. Do not rely on this accidental compatibility.
+
+## Directories and objects
+
+Directories are physical Google Drive folders created from object-key path segments. I.R.I.S. does not create zero-byte directory marker objects. An empty prefix can therefore exist as a Drive folder without an S3 marker object.
+
+## Multipart behavior
+
+Parts must be uploaded strictly in consecutive order beginning at part 1. Parts are immutable after they have committed. An incomplete upload expires after 24 hours. `ETAG_STYLE=md5` returns Google Drive's file MD5 for completed multipart objects; `ETAG_STYLE=multipart` is available only for clients that require an S3-style composite ETag.
+
+## Quotas and plan limits
+
+Google Drive's free tier has 15 GB total storage and a 750 GB/day upload limit. Cloudflare applies request-size limits before a request reaches the Worker; use multipart uploads for large browser uploads.
diff --git a/docs/openapi.yaml b/docs/openapi.yaml
new file mode 100644
index 0000000..0722319
--- /dev/null
+++ b/docs/openapi.yaml
@@ -0,0 +1,391 @@
+openapi: 3.1.0
+info:
+ title: I.R.I.S. Google Drive S3 API
+ version: 1.0.0
+ description: |
+ An intentionally small S3-compatible object-storage API backed by Google Drive.
+ Authenticate every non-public request with AWS Signature Version 4. Browser clients
+ should receive short-lived presigned URLs from a backend-for-frontend, never credentials.
+servers:
+ - url: https://{worker-host}
+ variables:
+ worker-host:
+ default: your-worker.example.workers.dev
+security:
+ - sigv4: []
+tags:
+ - name: Objects
+ - name: Buckets
+ - name: Multipart uploads
+paths:
+ /{bucket}:
+ parameters:
+ - $ref: '#/components/parameters/Bucket'
+ get:
+ tags: [Buckets]
+ operationId: listObjects
+ summary: List objects in a bucket
+ description: List objects recursively, or use `delimiter=/` to return immediate folders as `CommonPrefixes`.
+ parameters:
+ - $ref: '#/components/parameters/Prefix'
+ - $ref: '#/components/parameters/Delimiter'
+ responses:
+ '200':
+ description: Object listing.
+ content:
+ application/xml:
+ schema: { $ref: '#/components/schemas/ListBucketResult' }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '405': { $ref: '#/components/responses/MethodNotAllowed' }
+ head:
+ tags: [Buckets]
+ operationId: headBucket
+ summary: Check bucket access
+ responses:
+ '200': { description: The bucket is configured and accessible. }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '405': { $ref: '#/components/responses/MethodNotAllowed' }
+ /{bucket}?uploads:
+ parameters:
+ - $ref: '#/components/parameters/Bucket'
+ get:
+ tags: [Multipart uploads]
+ operationId: listMultipartUploads
+ summary: List multipart uploads
+ description: Always returns an empty list; active multipart uploads cannot be enumerated.
+ responses:
+ '200':
+ description: Empty multipart upload listing.
+ content:
+ application/xml:
+ schema: { $ref: '#/components/schemas/ListMultipartUploadsResult' }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ /{bucket}/{key}:
+ parameters:
+ - $ref: '#/components/parameters/Bucket'
+ - $ref: '#/components/parameters/Key'
+ put:
+ tags: [Objects]
+ operationId: putObject
+ summary: Create or replace an object
+ parameters:
+ - $ref: '#/components/parameters/ContentType'
+ requestBody:
+ required: true
+ content:
+ application/octet-stream:
+ schema: { type: string, format: binary }
+ '*/*':
+ schema: { type: string, format: binary }
+ responses:
+ '200':
+ description: Object uploaded.
+ headers:
+ ETag: { $ref: '#/components/headers/ETag' }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '501': { $ref: '#/components/responses/NotImplemented' }
+ get:
+ tags: [Objects]
+ operationId: getObject
+ summary: Download an object
+ parameters:
+ - name: Range
+ in: header
+ schema: { type: string, example: bytes=0-1023 }
+ responses:
+ '200':
+ description: Full object data.
+ headers:
+ ETag: { $ref: '#/components/headers/ETag' }
+ Content-Length: { $ref: '#/components/headers/ContentLength' }
+ Last-Modified: { $ref: '#/components/headers/LastModified' }
+ Accept-Ranges: { schema: { type: string, example: bytes } }
+ content:
+ application/octet-stream:
+ schema: { type: string, format: binary }
+ '206':
+ description: Requested byte range.
+ headers:
+ Content-Range: { schema: { type: string, example: bytes 0-1023/4096 } }
+ Content-Length: { $ref: '#/components/headers/ContentLength' }
+ content:
+ application/octet-stream:
+ schema: { type: string, format: binary }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '404': { $ref: '#/components/responses/NoSuchKey' }
+ head:
+ tags: [Objects]
+ operationId: headObject
+ summary: Get object metadata
+ responses:
+ '200':
+ description: Object metadata in headers.
+ headers:
+ ETag: { $ref: '#/components/headers/ETag' }
+ Content-Length: { $ref: '#/components/headers/ContentLength' }
+ Last-Modified: { $ref: '#/components/headers/LastModified' }
+ Accept-Ranges: { schema: { type: string, example: bytes } }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '404': { $ref: '#/components/responses/NoSuchKey' }
+ delete:
+ tags: [Objects]
+ operationId: deleteObject
+ summary: Delete an object
+ responses:
+ '204': { description: Object deleted. }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '404': { $ref: '#/components/responses/NoSuchKey' }
+ /{bucket}/{key}?uploads:
+ parameters:
+ - $ref: '#/components/parameters/Bucket'
+ - $ref: '#/components/parameters/Key'
+ post:
+ tags: [Multipart uploads]
+ operationId: createMultipartUpload
+ summary: Initiate a multipart upload
+ parameters:
+ - $ref: '#/components/parameters/ContentType'
+ responses:
+ '200':
+ description: Upload initiated.
+ content:
+ application/xml:
+ schema: { $ref: '#/components/schemas/InitiateMultipartUploadResult' }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '501': { $ref: '#/components/responses/NotImplemented' }
+ /{bucket}/{key}?uploadId={uploadId}&partNumber={partNumber}:
+ parameters:
+ - $ref: '#/components/parameters/Bucket'
+ - $ref: '#/components/parameters/Key'
+ - $ref: '#/components/parameters/UploadId'
+ - $ref: '#/components/parameters/PartNumber'
+ put:
+ tags: [Multipart uploads]
+ operationId: uploadPart
+ summary: Upload one multipart part
+ description: Parts must arrive strictly sequentially beginning with part number 1.
+ requestBody:
+ required: true
+ content:
+ application/octet-stream:
+ schema: { type: string, format: binary }
+ responses:
+ '200':
+ description: Part accepted.
+ headers:
+ ETag: { $ref: '#/components/headers/ETag' }
+ '400': { $ref: '#/components/responses/InvalidArgument' }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '404': { $ref: '#/components/responses/NoSuchUpload' }
+ '503': { $ref: '#/components/responses/SlowDown' }
+ /{bucket}/{key}?uploadId={uploadId}:
+ parameters:
+ - $ref: '#/components/parameters/Bucket'
+ - $ref: '#/components/parameters/Key'
+ - $ref: '#/components/parameters/UploadId'
+ post:
+ tags: [Multipart uploads]
+ operationId: completeMultipartUpload
+ summary: Complete a multipart upload
+ requestBody:
+ required: true
+ content:
+ application/xml:
+ schema: { $ref: '#/components/schemas/CompleteMultipartUpload' }
+ responses:
+ '200':
+ description: Upload completed.
+ content:
+ application/xml:
+ schema: { $ref: '#/components/schemas/CompleteMultipartUploadResult' }
+ '400': { $ref: '#/components/responses/InvalidArgument' }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '404': { $ref: '#/components/responses/NoSuchUpload' }
+ get:
+ tags: [Multipart uploads]
+ operationId: listParts
+ summary: List uploaded parts
+ parameters:
+ - name: part-number-marker
+ in: query
+ schema: { type: integer, minimum: 0, default: 0 }
+ - name: max-parts
+ in: query
+ schema: { type: integer, minimum: 1, maximum: 1000, default: 1000 }
+ responses:
+ '200':
+ description: Current part list.
+ content:
+ application/xml:
+ schema: { $ref: '#/components/schemas/ListPartsResult' }
+ '400': { $ref: '#/components/responses/InvalidArgument' }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '404': { $ref: '#/components/responses/NoSuchUpload' }
+ delete:
+ tags: [Multipart uploads]
+ operationId: abortMultipartUpload
+ summary: Abort a multipart upload
+ responses:
+ '204': { description: Upload aborted. }
+ '403': { $ref: '#/components/responses/AccessDenied' }
+ '404': { $ref: '#/components/responses/NoSuchUpload' }
+components:
+ securitySchemes:
+ sigv4:
+ type: apiKey
+ in: header
+ name: Authorization
+ description: AWS Signature Version 4 header authentication or equivalent `X-Amz-*` presigned query parameters.
+ parameters:
+ Bucket:
+ name: bucket
+ in: path
+ required: true
+ schema: { type: string }
+ Key:
+ name: key
+ in: path
+ required: true
+ schema: { type: string }
+ Prefix:
+ name: prefix
+ in: query
+ schema: { type: string, default: '' }
+ Delimiter:
+ name: delimiter
+ in: query
+ schema: { type: string, example: / }
+ UploadId:
+ name: uploadId
+ in: query
+ required: true
+ schema: { type: string }
+ PartNumber:
+ name: partNumber
+ in: query
+ required: true
+ schema: { type: integer, minimum: 1, maximum: 10000 }
+ ContentType:
+ name: Content-Type
+ in: header
+ schema: { type: string, example: application/octet-stream }
+ headers:
+ ETag:
+ schema: { type: string, example: '"d41d8cd98f00b204e9800998ecf8427e"' }
+ ContentLength:
+ schema: { type: integer, minimum: 0 }
+ LastModified:
+ schema: { type: string, format: date-time }
+ responses:
+ AccessDenied:
+ description: Authentication failed, the access key is not recognized, the presigned URL expired, or bucket access is denied.
+ content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
+ NoSuchKey:
+ description: The object does not exist.
+ content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
+ NoSuchUpload:
+ description: The multipart upload does not exist.
+ content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
+ InvalidArgument:
+ description: A required parameter or body is invalid.
+ content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
+ NotImplemented:
+ description: This S3 operation is not supported.
+ content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
+ SlowDown:
+ description: Parts are out of sequence or the upload is busy; retry later.
+ content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
+ MethodNotAllowed:
+ description: The method/path combination is unsupported.
+ content: { application/xml: { schema: { $ref: '#/components/schemas/S3Error' } } }
+ schemas:
+ S3Error:
+ type: object
+ xml: { name: Error }
+ required: [Code, Message, RequestId]
+ properties:
+ Code: { type: string, xml: { name: Code } }
+ Message: { type: string, xml: { name: Message } }
+ Resource: { type: string, xml: { name: Resource } }
+ RequestId: { type: string, xml: { name: RequestId } }
+ Object:
+ type: object
+ xml: { name: Contents }
+ properties:
+ Key: { type: string }
+ LastModified: { type: string, format: date-time }
+ ETag: { type: string }
+ Size: { type: integer }
+ StorageClass: { type: string, example: STANDARD }
+ ListBucketResult:
+ type: object
+ xml: { name: ListBucketResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
+ properties:
+ Name: { type: string }
+ Prefix: { type: string }
+ Delimiter: { type: string }
+ MaxKeys: { type: integer, example: 1000 }
+ IsTruncated: { type: boolean }
+ Contents: { type: array, items: { $ref: '#/components/schemas/Object' }, xml: { wrapped: false } }
+ InitiateMultipartUploadResult:
+ type: object
+ xml: { name: InitiateMultipartUploadResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
+ required: [Bucket, Key, UploadId]
+ properties:
+ Bucket: { type: string }
+ Key: { type: string }
+ UploadId: { type: string }
+ CompleteMultipartUpload:
+ type: object
+ xml: { name: CompleteMultipartUpload }
+ required: [Part]
+ properties:
+ Part:
+ type: array
+ minItems: 1
+ xml: { wrapped: false }
+ items:
+ type: object
+ xml: { name: Part }
+ required: [PartNumber, ETag]
+ properties:
+ PartNumber: { type: integer }
+ ETag: { type: string }
+ CompleteMultipartUploadResult:
+ type: object
+ xml: { name: CompleteMultipartUploadResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
+ properties:
+ Location: { type: string }
+ Bucket: { type: string }
+ Key: { type: string }
+ ETag: { type: string }
+ ListPartsResult:
+ type: object
+ xml: { name: ListPartsResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
+ properties:
+ Bucket: { type: string }
+ Key: { type: string }
+ UploadId: { type: string }
+ NextPartNumberMarker: { type: integer }
+ IsTruncated: { type: boolean }
+ Part:
+ type: array
+ xml: { wrapped: false }
+ items:
+ type: object
+ properties:
+ PartNumber: { type: integer }
+ LastModified: { type: string, format: date-time }
+ ETag: { type: string }
+ Size: { type: integer }
+ ListMultipartUploadsResult:
+ type: object
+ xml: { name: ListMultipartUploadsResult, namespace: 'http://s3.amazonaws.com/doc/2006-03-01/' }
+ properties:
+ Bucket: { type: string }
+ KeyMarker: { type: string }
+ UploadIdMarker: { type: string }
+ NextKeyMarker: { type: string }
+ NextUploadIdMarker: { type: string }
+ MaxUploads: { type: integer, example: 1000 }
+ IsTruncated: { type: boolean, example: false }
diff --git a/scripts/get-google-refresh-token.mjs b/scripts/get-google-refresh-token.mjs
index 7e531d3..af0161e 100644
--- a/scripts/get-google-refresh-token.mjs
+++ b/scripts/get-google-refresh-token.mjs
@@ -20,9 +20,9 @@
// Optional: --port 8976 (must not be in use; Desktop-app clients accept any
// loopback port automatically, no need to register it in Google Cloud Console).
+import { exec } from "node:child_process";
import { randomBytes } from "node:crypto";
import http from "node:http";
-import { exec } from "node:child_process";
import { URL } from "node:url";
const SCOPE = "https://www.googleapis.com/auth/drive";
@@ -137,10 +137,7 @@ async function main() {
process.exit(1);
}
if (!tokenData.refresh_token) {
- console.error(
- "No refresh_token was returned. This usually means the account already has an active grant.\n" +
- "Revoke it at https://myaccount.google.com/permissions and re-run this script.",
- );
+ console.error("No refresh_token was returned. This usually means the account already has an active grant.\n" + "Revoke it at https://myaccount.google.com/permissions and re-run this script.");
process.exit(1);
}
diff --git a/src/aws-signature.ts b/src/aws-signature.ts
index 30b094b..46eaedb 100644
--- a/src/aws-signature.ts
+++ b/src/aws-signature.ts
@@ -1,5 +1,11 @@
+import type { S3ErrorCode } from "./s3-errors";
import type { Env } from "./types";
+const MAX_PRESIGN_EXPIRY_SECONDS = 7 * 24 * 60 * 60;
+const MAX_CLOCK_SKEW_MS = 15 * 60 * 1000;
+
+export type VerifyResult = { ok: true } | { ok: false; code: S3ErrorCode; message?: string };
+
function encodeRFC3986(str: string): string {
return encodeURIComponent(str).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
}
@@ -84,8 +90,35 @@ async function createCanonicalRequest(request: Request, isQueryAuth: boolean): P
return [method, canonicalUri, params, canonicalHeaders, signedHeaders, payloadHash].join("\n");
}
+function parseAmzDate(datetime: string): number | null {
+ const match = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(datetime);
+ if (!match) return null;
+
+ const [, year, month, day, hour, minute, second] = match;
+ const timestamp = Date.UTC(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second));
+ const parsed = new Date(timestamp);
+ if (parsed.getUTCFullYear() !== Number(year) || parsed.getUTCMonth() !== Number(month) - 1 || parsed.getUTCDate() !== Number(day) || parsed.getUTCHours() !== Number(hour) || parsed.getUTCMinutes() !== Number(minute) || parsed.getUTCSeconds() !== Number(second)) {
+ return null;
+ }
+
+ return timestamp;
+}
+
+function constantTimeEqual(left: string, right: string): boolean {
+ if (left.length !== right.length) return false;
+ let mismatch = 0;
+ for (let index = 0; index < left.length; index++) {
+ mismatch |= left.charCodeAt(index) ^ right.charCodeAt(index);
+ }
+ return mismatch === 0;
+}
+
+function signatureMismatch(): VerifyResult {
+ return { ok: false, code: "SignatureDoesNotMatch" };
+}
+
/** Verifies an AWS Signature V4 signature carried in either the Authorization header or presigned query params. */
-export async function verifySignature(request: Request, env: Env): Promise {
+export async function verifySignature(request: Request, env: Env): Promise {
const url = new URL(request.url);
const headers = request.headers;
@@ -99,15 +132,32 @@ export async function verifySignature(request: Request, env: Env): Promise MAX_PRESIGN_EXPIRY_SECONDS || Date.now() > signedAt + expiresIn * 1000) {
+ return { ok: false, code: "AccessDenied", message: "Request has expired" };
+ }
+ } else if (Math.abs(Date.now() - signedAt) > MAX_CLOCK_SKEW_MS) {
+ return { ok: false, code: "RequestTimeTooSkewed" };
+ }
const date = datetime.substring(0, 8);
+ const credential = isQueryAuth ? (url.searchParams.get("X-Amz-Credential") ?? "") : (/Credential=([^,\s]+)/.exec(headers.get("Authorization") ?? "")?.[1] ?? "");
+ const credentialParts = credential.split("/");
+ if (credentialParts.length !== 5 || credentialParts[0] !== env.ACCESS_KEY || credentialParts.slice(1).join("/") !== `${date}/${env.REGION}/s3/aws4_request`) {
+ return { ok: false, code: "AccessDenied" };
+ }
const canonicalRequest = await createCanonicalRequest(request, isQueryAuth);
const hashedCanonicalRequest = await sha256(canonicalRequest);
@@ -124,9 +174,9 @@ export async function verifySignature(request: Request, env: Env): Promise origin.trim())
+ .filter(Boolean);
+}
+
+function hasWildcardOrigin(env: Env): boolean {
+ return env.CORS_ALLOWED_ORIGINS?.trim() === "*";
+}
+
+function appendVary(headers: Headers, value: string): void {
+ const existing = headers.get("Vary");
+ if (!existing) {
+ headers.set("Vary", value);
+ return;
+ }
+
+ if (!existing.split(",").some((part) => part.trim().toLowerCase() === value.toLowerCase())) {
+ headers.set("Vary", `${existing}, ${value}`);
+ }
+}
+
+/** Returns the permitted value for Access-Control-Allow-Origin, or null when CORS is disabled or denied. */
+export function resolveOrigin(request: Request, env: Env): string | null {
+ const origin = request.headers.get("Origin");
+ if (!origin) return null;
+ if (hasWildcardOrigin(env)) return "*";
+ return configuredOrigins(env).includes(origin) ? origin : null;
+}
+
+/** Builds an unauthenticated browser preflight response. */
+export function preflightResponse(request: Request, env: Env): Response {
+ const headers = new Headers();
+ const origin = resolveOrigin(request, env);
+
+ if (origin) {
+ headers.set("Access-Control-Allow-Origin", origin);
+ headers.set("Access-Control-Allow-Methods", ALLOWED_METHODS);
+ headers.set("Access-Control-Allow-Headers", request.headers.get("Access-Control-Request-Headers") ?? DEFAULT_ALLOWED_HEADERS);
+ headers.set("Access-Control-Max-Age", "86400");
+ if (!hasWildcardOrigin(env)) appendVary(headers, "Origin");
+ }
+
+ return new Response(null, { status: 204, headers });
+}
+
+/** Adds CORS response headers without mutating the response supplied by the request handler. */
+export function withCors(response: Response, request: Request, env: Env): Response {
+ const headers = new Headers(response.headers);
+
+ if (env.CORS_ALLOWED_ORIGINS?.trim() && !hasWildcardOrigin(env)) {
+ appendVary(headers, "Origin");
+ }
+
+ const origin = resolveOrigin(request, env);
+ if (origin) {
+ headers.set("Access-Control-Allow-Origin", origin);
+ headers.set("Access-Control-Expose-Headers", EXPOSED_HEADERS);
+ }
+
+ return new Response(response.body, { status: response.status, statusText: response.statusText, headers });
+}
diff --git a/src/docs.ts b/src/docs.ts
new file mode 100644
index 0000000..de29098
--- /dev/null
+++ b/src/docs.ts
@@ -0,0 +1,36 @@
+import openApiSpec from "../docs/openapi.yaml";
+
+export const OPENAPI_PATH = "/openapi.yaml";
+export const DOCS_PATH = "/docs";
+
+export function openApiResponse(): Response {
+ return new Response(openApiSpec, {
+ headers: {
+ "Content-Type": "application/yaml; charset=utf-8",
+ "Cache-Control": "public, max-age=300",
+ },
+ });
+}
+
+export function docsResponse(): Response {
+ const html = `
+
+
+
+
+
+ I.R.I.S. S3 API reference
+
+
+
+
+
+`;
+
+ return new Response(html, {
+ headers: {
+ "Content-Type": "text/html; charset=utf-8",
+ "Cache-Control": "public, max-age=300",
+ },
+ });
+}
diff --git a/src/google-drive.ts b/src/google-drive.ts
index 1cbaf93..33c07f6 100644
--- a/src/google-drive.ts
+++ b/src/google-drive.ts
@@ -220,9 +220,10 @@ export async function streamDownloadFromDrive(accessToken: string, bucket: strin
console.error(await downloadRes.text());
throw new Error("Download failed");
}
+ if (!downloadRes.body) throw new Error("Download response had no body");
return {
- body: downloadRes.body!,
+ body: downloadRes.body,
contentType: file.mimeType || "application/octet-stream",
size: parseInt(file.size || "0", 10),
id: file.id,
diff --git a/src/index.ts b/src/index.ts
index d7b81b7..2e8220e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,7 @@
import { verifySignature } from "./aws-signature";
import { isAllowedBucket, isPublicReadBucket } from "./bucket-access";
+import { preflightResponse, withCors } from "./cors";
+import * as docs from "./docs";
import { getAccessToken } from "./google-drive";
import { MultipartUploadDO } from "./multipart-do";
import { dispatch } from "./router";
@@ -10,27 +12,33 @@ export { MultipartUploadDO };
export default {
async fetch(request: Request, env: Env, _ctx: ExecutionContext): Promise {
- if (request.method === "OPTIONS") return new Response(null, { status: 204 });
+ if (request.method === "OPTIONS") return preflightResponse(request, env);
const url = new URL(request.url);
+ if (env.ENABLE_DOCS !== "false") {
+ if (request.method === "GET" && url.pathname === docs.OPENAPI_PATH) return withCors(docs.openApiResponse(), request, env);
+ if (request.method === "GET" && url.pathname === docs.DOCS_PATH && !isAllowedBucket("docs", env)) return withCors(docs.docsResponse(), request, env);
+ }
+
const pathParts = url.pathname.split("/").filter(Boolean);
const bucket = pathParts[0] || "";
const objectKey = pathParts.slice(1).join("/");
const resource = url.pathname || "/";
try {
- if (!isAllowedBucket(bucket, env)) return s3Error("AccessDenied", 403, undefined, resource, request.method === "HEAD");
+ if (!isAllowedBucket(bucket, env)) return withCors(s3Error("AccessDenied", 403, undefined, resource, request.method === "HEAD"), request, env);
const isPublicRead = isPublicReadBucket(bucket, env) && (request.method === "GET" || request.method === "HEAD");
- if (!isPublicRead && !(await verifySignature(request, env))) {
- return s3Error("SignatureDoesNotMatch", 403, undefined, resource, request.method === "HEAD");
+ const signature = isPublicRead ? { ok: true as const } : await verifySignature(request, env);
+ if (!signature.ok) {
+ return withCors(s3Error(signature.code, 403, signature.message, resource, request.method === "HEAD"), request, env);
}
- return await dispatch(request, env, await getAccessToken(env), bucket, objectKey);
+ return withCors(await dispatch(request, env, await getAccessToken(env), bucket, objectKey), request, env);
} catch (error) {
- if (error instanceof S3Exception) return s3Error(error.code, error.status, error.message, resource, request.method === "HEAD", error.headers);
+ if (error instanceof S3Exception) return withCors(s3Error(error.code, error.status, error.message, resource, request.method === "HEAD", error.headers), request, env);
console.error(JSON.stringify({ message: "request failed", error: error instanceof Error ? error.message : String(error), method: request.method, path: url.pathname }));
- return s3Error("InternalError", 500, undefined, resource, request.method === "HEAD");
+ return withCors(s3Error("InternalError", 500, undefined, resource, request.method === "HEAD"), request, env);
}
},
} satisfies ExportedHandler;
diff --git a/src/multipart-do.ts b/src/multipart-do.ts
index 8d0fc4c..d8299ca 100644
--- a/src/multipart-do.ts
+++ b/src/multipart-do.ts
@@ -57,6 +57,12 @@ export class MultipartUploadDO extends DurableObject {
return this.ctx.storage.sql.exec("SELECT v FROM state WHERE k = ?", key).toArray()[0]?.v as T | undefined;
}
+ private requiredValue(key: string): T {
+ const value = this.getValue(key);
+ if (value === undefined) throw new Error(`Missing multipart upload state: ${key}`);
+ return value;
+ }
+
private setValue(key: string, value: ArrayBuffer | string | number | null): void {
this.ctx.storage.sql.exec("INSERT INTO state (k, v) VALUES (?, ?) ON CONFLICT(k) DO UPDATE SET v = excluded.v", key, value);
}
@@ -97,7 +103,7 @@ export class MultipartUploadDO extends DurableObject {
private async resyncExpiredLease(inFlight: InFlight, requestId: string, partLen: number): Promise {
if (partLen !== inFlight.partLen) return { kind: "error", code: "InvalidPart", message: "A retried part must have the same decoded length" };
const accessToken = await getAccessToken(this.env);
- const actualOffset = await queryStatus(this.getValue("uploadUrl")!, accessToken);
+ const actualOffset = await queryStatus(this.requiredValue("uploadUrl"), accessToken);
const partStartFileOffset = inFlight.driveOffsetAtStart + inFlight.carryLen;
let skipBytes = 0;
if (actualOffset === inFlight.driveOffsetAtStart) {
@@ -129,7 +135,7 @@ export class MultipartUploadDO extends DurableObject {
leaseExpiresAt: Date.now() + LEASE_MS,
};
this.setValue("inFlight", JSON.stringify(inFlight));
- return { kind: "admit", uploadUrl: this.getValue("uploadUrl")!, driveOffset, carry, sendLen, skipBytes };
+ return { kind: "admit", uploadUrl: this.requiredValue("uploadUrl"), driveOffset, carry, sendLen, skipBytes };
}
async beginPart(requestId: string, partNumber: number, partLen: number): Promise {
@@ -229,15 +235,15 @@ export class MultipartUploadDO extends DurableObject {
const accessToken = await getAccessToken(this.env);
let metadata: DriveUploadResult;
if (total === 0) {
- await cancelSession(this.getValue("uploadUrl")!, accessToken);
+ await cancelSession(this.requiredValue("uploadUrl"), accessToken);
metadata = await createEmptyFile(accessToken, {
- name: this.getValue("fileName")!,
- parents: [this.getValue("parentFolderId")!],
- mimeType: this.getValue("mimeType")!,
+ name: this.requiredValue("fileName"),
+ parents: [this.requiredValue("parentFolderId")],
+ mimeType: this.requiredValue("mimeType"),
existingFileId: this.getValue("existingFileId"),
});
} else {
- metadata = await putFinalChunk(this.getValue("uploadUrl")!, accessToken, driveOffset, total, carry);
+ metadata = await putFinalChunk(this.requiredValue("uploadUrl"), accessToken, driveOffset, total, carry);
}
const partEtags = stored.map((part) => part.etag);
await this.ctx.storage.deleteAlarm();
@@ -259,7 +265,7 @@ export class MultipartUploadDO extends DurableObject {
async abort(): Promise {
if (!this.hasUpload()) return false;
const accessToken = await getAccessToken(this.env);
- await cancelSession(this.getValue("uploadUrl")!, accessToken);
+ await cancelSession(this.requiredValue("uploadUrl"), accessToken);
await this.ctx.storage.deleteAlarm();
await this.ctx.storage.deleteAll();
for (const waiter of this.waiters.values()) {
diff --git a/src/s3-errors.ts b/src/s3-errors.ts
index 549eda4..59d9bcd 100644
--- a/src/s3-errors.ts
+++ b/src/s3-errors.ts
@@ -1,10 +1,11 @@
import { escapeXml } from "./s3-xml";
-export type S3ErrorCode = "AccessDenied" | "SignatureDoesNotMatch" | "NoSuchKey" | "NoSuchUpload" | "InvalidPart" | "InvalidPartOrder" | "EntityTooLarge" | "MalformedXML" | "InvalidArgument" | "MethodNotAllowed" | "NotImplemented" | "SlowDown" | "InternalError";
+export type S3ErrorCode = "AccessDenied" | "SignatureDoesNotMatch" | "RequestTimeTooSkewed" | "NoSuchKey" | "NoSuchUpload" | "InvalidPart" | "InvalidPartOrder" | "EntityTooLarge" | "MalformedXML" | "InvalidArgument" | "MethodNotAllowed" | "NotImplemented" | "SlowDown" | "InternalError";
const DEFAULT_MESSAGES: Record = {
AccessDenied: "Access Denied",
SignatureDoesNotMatch: "The request signature we calculated does not match the signature you provided.",
+ RequestTimeTooSkewed: "The difference between the request time and the server's time is too large.",
NoSuchKey: "The specified key does not exist.",
NoSuchUpload: "The specified multipart upload does not exist.",
InvalidPart: "One or more of the specified parts could not be found.",
diff --git a/src/text-modules.d.ts b/src/text-modules.d.ts
new file mode 100644
index 0000000..3897aa2
--- /dev/null
+++ b/src/text-modules.d.ts
@@ -0,0 +1,4 @@
+declare module "*.yaml" {
+ const content: string;
+ export default content;
+}
diff --git a/src/types.ts b/src/types.ts
index 44772fc..a30ec2b 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -12,6 +12,8 @@ export interface Env {
PUBLIC_READ_BUCKETS?: string;
ALLOW_MULTIPART?: string;
ETAG_STYLE?: "md5" | "multipart";
+ CORS_ALLOWED_ORIGINS?: string;
+ ENABLE_DOCS?: string;
}
export interface GoogleDriveFile {
diff --git a/test/cors.test.ts b/test/cors.test.ts
new file mode 100644
index 0000000..aee85c7
--- /dev/null
+++ b/test/cors.test.ts
@@ -0,0 +1,119 @@
+import { AwsClient } from "aws4fetch";
+import { afterEach, describe, expect, it, vi } from "vitest";
+
+import { preflightResponse, withCors } from "../src/cors";
+import worker from "../src/index";
+import type { Env } from "../src/types";
+
+import { env } from "cloudflare:test";
+
+const ENV = env as unknown as Env;
+const ENDPOINT = "https://s3-api.example.com";
+const ORIGIN = "http://localhost:5173";
+const CTX = { waitUntil: vi.fn(), passThroughOnException: vi.fn() } as unknown as ExecutionContext;
+
+async function signed(path: string, init: RequestInit): Promise {
+ const aws = new AwsClient({ accessKeyId: ENV.ACCESS_KEY, secretAccessKey: ENV.SECRET_KEY, region: ENV.REGION, service: "s3" });
+ const bodyLength = typeof init.body === "string" ? new TextEncoder().encode(init.body).byteLength : init.body instanceof Uint8Array ? init.body.byteLength : undefined;
+ return aws.sign(`${ENDPOINT}${path}`, {
+ ...init,
+ headers: { "x-amz-content-sha256": "UNSIGNED-PAYLOAD", ...(bodyLength === undefined ? {} : { "x-amz-decoded-content-length": String(bodyLength) }), ...init.headers },
+ });
+}
+
+function fakeGoogleFetch(input: string | URL | Request, init?: RequestInit): Promise {
+ const request = input instanceof Request ? input : new Request(input, init);
+ const url = new URL(request.url);
+ if (url.hostname === "oauth2.googleapis.com") return Promise.resolve(Response.json({ access_token: "token", expires_in: 3600 }));
+ if (url.pathname === "/drive/v3/files" && request.method === "GET") return Promise.resolve(Response.json({ files: [] }));
+ if (url.pathname === "/drive/v3/files" && request.method === "POST") return Promise.resolve(Response.json({ id: "folder-1" }));
+ if (url.pathname.startsWith("/upload/drive/v3/files")) return Promise.resolve(new Response(null, { headers: { Location: "https://www.googleapis.com/upload/session/test" } }));
+ if (url.pathname === "/upload/session/test") return Promise.resolve(Response.json({ id: "file-1", name: "file.txt", md5Checksum: "d41d8cd98f00b204e9800998ecf8427e" }));
+ return Promise.resolve(new Response("Not Found", { status: 404 }));
+}
+
+afterEach(() => vi.unstubAllGlobals());
+
+describe("CORS", () => {
+ it("returns allow-list preflight headers", async () => {
+ const response = preflightResponse(
+ new Request(`${ENDPOINT}/test-bucket/file.txt`, {
+ method: "OPTIONS",
+ headers: { Origin: ORIGIN, "Access-Control-Request-Method": "PUT", "Access-Control-Request-Headers": "content-type,x-amz-date" },
+ }),
+ ENV,
+ );
+
+ expect(response.status).toBe(204);
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBe(ORIGIN);
+ expect(response.headers.get("Access-Control-Allow-Methods")).toBe("GET, HEAD, PUT, POST, DELETE, OPTIONS");
+ expect(response.headers.get("Access-Control-Allow-Headers")).toBe("content-type,x-amz-date");
+ expect(response.headers.get("Access-Control-Max-Age")).toBe("86400");
+ expect(response.headers.get("Vary")).toBe("Origin");
+ });
+
+ it("returns a bare 204 for a disallowed origin", async () => {
+ const response = preflightResponse(new Request(`${ENDPOINT}/test-bucket/file.txt`, { method: "OPTIONS", headers: { Origin: "https://not-allowed.example", "Access-Control-Request-Method": "PUT" } }), ENV);
+
+ expect(response.status).toBe(204);
+ expect([...response.headers]).toEqual([]);
+ });
+
+ it("keeps OPTIONS requests without an Origin byte-compatible with the prior bare 204", () => {
+ const response = preflightResponse(new Request(`${ENDPOINT}/test-bucket/file.txt`, { method: "OPTIONS" }), ENV);
+
+ expect(response.status).toBe(204);
+ expect([...response.headers]).toEqual([]);
+ });
+
+ it("exposes ETag for successful PUT responses", async () => {
+ vi.stubGlobal("fetch", vi.fn(fakeGoogleFetch));
+ const request = await signed("/test-bucket/file.txt", { method: "PUT", body: "hello", headers: { Origin: ORIGIN, "Content-Type": "text/plain" } });
+ const response = await worker.fetch(request, ENV, CTX);
+
+ expect(response.status).toBe(200);
+ expect(response.headers.get("ETag")).toBe('"d41d8cd98f00b204e9800998ecf8427e"');
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBe(ORIGIN);
+ expect(response.headers.get("Access-Control-Expose-Headers")).toContain("ETag");
+ });
+
+ it("adds CORS headers to access-denied and missing-key errors", async () => {
+ const denied = await worker.fetch(new Request(`${ENDPOINT}/not-a-bucket`, { headers: { Origin: ORIGIN } }), ENV, CTX);
+ expect(denied.status).toBe(403);
+ expect(denied.headers.get("Access-Control-Allow-Origin")).toBe(ORIGIN);
+
+ vi.stubGlobal("fetch", vi.fn(fakeGoogleFetch));
+ const missing = await worker.fetch(await signed("/test-bucket/missing.txt", { method: "GET", headers: { Origin: ORIGIN } }), ENV, CTX);
+ expect(missing.status).toBe(404);
+ expect(await missing.text()).toContain("NoSuchKey");
+ expect(missing.headers.get("Access-Control-Allow-Origin")).toBe(ORIGIN);
+ });
+
+ it("does not alter the S3 response for a disallowed Origin", async () => {
+ const response = await worker.fetch(new Request(`${ENDPOINT}/not-a-bucket`, { headers: { Origin: "https://not-allowed.example" } }), ENV, CTX);
+
+ expect(response.status).toBe(403);
+ expect(await response.text()).toContain("AccessDenied");
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBeNull();
+ expect(response.headers.get("Access-Control-Expose-Headers")).toBeNull();
+ expect(response.headers.get("Vary")).toBe("Origin");
+ });
+
+ it("does not attach Access-Control headers to server-to-server responses without Origin", () => {
+ const response = withCors(new Response("S3 response", { status: 200, headers: { ETag: '"etag"' } }), new Request(`${ENDPOINT}/test-bucket/file.txt`), ENV);
+
+ expect(response.status).toBe(200);
+ expect(response.headers.get("ETag")).toBe('"etag"');
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBeNull();
+ expect(response.headers.get("Access-Control-Expose-Headers")).toBeNull();
+ expect(response.headers.get("Vary")).toBe("Origin");
+ });
+
+ it("emits no CORS headers when CORS_ALLOWED_ORIGINS is unset", () => {
+ const request = new Request(`${ENDPOINT}/test-bucket/file.txt`, { headers: { Origin: ORIGIN } });
+ const response = withCors(new Response(null, { headers: { ETag: '"etag"' } }), request, { ...ENV, CORS_ALLOWED_ORIGINS: undefined });
+ expect(response.headers.get("Access-Control-Allow-Origin")).toBeNull();
+ expect(response.headers.get("Access-Control-Expose-Headers")).toBeNull();
+ expect(response.headers.get("Vary")).toBeNull();
+ });
+});
diff --git a/test/docs.test.ts b/test/docs.test.ts
new file mode 100644
index 0000000..d6a20e7
--- /dev/null
+++ b/test/docs.test.ts
@@ -0,0 +1,39 @@
+import { describe, expect, it, vi } from "vitest";
+
+import worker from "../src/index";
+import type { Env } from "../src/types";
+
+import { env } from "cloudflare:test";
+
+const ENV = env as unknown as Env;
+const ENDPOINT = "https://s3-api.example.com";
+const CTX = { waitUntil: vi.fn(), passThroughOnException: vi.fn() } as unknown as ExecutionContext;
+
+describe("API documentation routes", () => {
+ it("serves the Scalar shell and raw OpenAPI document", async () => {
+ const docs = await worker.fetch(new Request(`${ENDPOINT}/docs`), ENV, CTX);
+ expect(docs.status).toBe(200);
+ expect(docs.headers.get("Content-Type")).toContain("text/html");
+ expect(await docs.text()).toContain("@scalar/api-reference");
+
+ const spec = await worker.fetch(new Request(`${ENDPOINT}/openapi.yaml`), ENV, CTX);
+ expect(spec.status).toBe(200);
+ expect(spec.headers.get("Content-Type")).toContain("application/yaml");
+ expect(new TextDecoder().decode(await spec.arrayBuffer())).toContain("openapi: 3.1.0");
+ });
+
+ it("bypasses documentation routes when disabled", async () => {
+ const disabled = { ...ENV, ENABLE_DOCS: "false" };
+ for (const path of ["/docs", "/openapi.yaml"]) {
+ const response = await worker.fetch(new Request(`${ENDPOINT}${path}`), disabled, CTX);
+ expect(response.status).toBe(403);
+ }
+ });
+
+ it("does not claim /docs when docs is a configured bucket", async () => {
+ const withDocsBucket = { ...ENV, ALLOWED_BUCKETS: "test-bucket,docs" };
+ const response = await worker.fetch(new Request(`${ENDPOINT}/docs`), withDocsBucket, CTX);
+ expect(response.status).toBe(403);
+ expect(await response.text()).toContain("SignatureDoesNotMatch");
+ });
+});
diff --git a/test/s3.test.ts b/test/s3.test.ts
index c5127b4..b8fab68 100644
--- a/test/s3.test.ts
+++ b/test/s3.test.ts
@@ -56,7 +56,9 @@ class FakeDrive {
if (url.pathname === "/drive/v3/files" && request.method === "POST") return this.createMetadata(await request.json>());
if (url.pathname.startsWith("/drive/v3/files/") && url.searchParams.get("alt") === "media") return this.download(url, request);
if (url.pathname.startsWith("/drive/v3/files/") && request.method === "DELETE") {
- this.files.delete(url.pathname.split("/").at(-1)!);
+ const fileId = url.pathname.split("/").at(-1);
+ if (!fileId) return new Response("Not Found", { status: 404 });
+ this.files.delete(fileId);
return new Response(null, { status: 204 });
}
if (url.pathname.startsWith("/upload/drive/v3/files") && url.searchParams.get("uploadType") === "resumable") return this.initialize(url, request);
@@ -121,7 +123,8 @@ class FakeDrive {
}
private async upload(url: URL, request: Request): Promise {
- const id = url.pathname.split("/").at(-1)!;
+ const id = url.pathname.split("/").at(-1);
+ if (!id) return new Response(null, { status: 404 });
const session = this.sessions.get(id);
if (!session) return new Response(null, { status: 404 });
if (request.method === "DELETE") {
@@ -159,7 +162,9 @@ class FakeDrive {
}
private download(url: URL, request: Request): Response {
- const file = this.files.get(url.pathname.split("/").at(-1)!);
+ const fileId = url.pathname.split("/").at(-1);
+ if (!fileId) return new Response(null, { status: 404 });
+ const file = this.files.get(fileId);
if (!file) return new Response(null, { status: 404 });
const range = request.headers.get("Range");
if (!range) return new Response(file.data, { headers: { "Content-Length": String(file.data.byteLength) } });
@@ -204,6 +209,18 @@ async function signed(path: string, init: RequestInit): Promise {
});
}
+async function presigned(path: string, init: RequestInit, options: { datetime?: string; accessKeyId?: string; expires?: string } = {}): Promise {
+ const url = new URL(`${ENDPOINT}${path}`);
+ url.searchParams.set("X-Amz-Expires", options.expires ?? "60");
+ const aws = new AwsClient({ accessKeyId: options.accessKeyId ?? ENV.ACCESS_KEY, secretAccessKey: ENV.SECRET_KEY, region: ENV.REGION, service: "s3" });
+ const bodyLength = typeof init.body === "string" ? new TextEncoder().encode(init.body).byteLength : init.body instanceof Uint8Array ? init.body.byteLength : undefined;
+ return aws.sign(url.toString(), {
+ ...init,
+ headers: { "x-amz-content-sha256": "UNSIGNED-PAYLOAD", ...(bodyLength === undefined ? {} : { "x-amz-decoded-content-length": String(bodyLength) }), ...init.headers },
+ aws: { signQuery: true, ...(options.datetime ? { datetime: options.datetime } : {}) },
+ });
+}
+
let drive: FakeDrive;
beforeEach(async () => {
@@ -226,7 +243,10 @@ describe("S3 compatibility", () => {
const second = await worker.fetch(await signed("/test-bucket/file.txt", { method: "PUT", body: "second" }), ENV, CTX);
expect(second.status).toBe(200);
expect([...drive.files.values()].filter((file) => file.name === "file.txt")).toHaveLength(1);
- expect(new TextDecoder().decode([...drive.files.values()].find((file) => file.name === "file.txt")!.data)).toBe("second");
+ const stored = [...drive.files.values()].find((file) => file.name === "file.txt");
+ expect(stored).toBeDefined();
+ if (!stored) throw new Error("Overwritten object was not stored");
+ expect(new TextDecoder().decode(stored.data)).toBe("second");
});
it("sets Last-Modified on GET and HEAD from Drive's modifiedTime", async () => {
@@ -264,6 +284,33 @@ describe("S3 compatibility", () => {
expect(response.status).toBe(200);
});
+ it("rejects expired presigned URLs and accepts unexpired ones", async () => {
+ const expired = await worker.fetch(await presigned("/test-bucket/file.txt", { method: "GET" }, { datetime: "20200101T000000Z", expires: "60" }), ENV, CTX);
+ expect(expired.status).toBe(403);
+ expect(await expired.text()).toContain("AccessDenied");
+ });
+
+ it("accepts an unexpired presigned URL", async () => {
+ await worker.fetch(await signed("/test-bucket/presigned.txt", { method: "PUT", body: "hello" }), ENV, CTX);
+ const response = await worker.fetch(await presigned("/test-bucket/presigned.txt", { method: "GET" }), ENV, CTX);
+ expect(response.status).toBe(200);
+ expect(await response.text()).toBe("hello");
+ });
+
+ it("rejects a Credential access key that does not match ACCESS_KEY", async () => {
+ const response = await worker.fetch(await presigned("/test-bucket/file.txt", { method: "GET" }, { accessKeyId: "unexpected-access-key" }), ENV, CTX);
+ expect(response.status).toBe(403);
+ expect(await response.text()).toContain("AccessDenied");
+ });
+
+ it("rejects header-authenticated requests outside the 15-minute clock skew", async () => {
+ const aws = new AwsClient({ accessKeyId: ENV.ACCESS_KEY, secretAccessKey: ENV.SECRET_KEY, region: ENV.REGION, service: "s3" });
+ const request = await aws.sign(`${ENDPOINT}/test-bucket/file.txt`, { method: "GET", aws: { datetime: "20200101T000000Z" } });
+ const response = await worker.fetch(request, ENV, CTX);
+ expect(response.status).toBe(403);
+ expect(await response.text()).toContain("RequestTimeTooSkewed");
+ });
+
it("decodes both aws-chunked framing variants across arbitrary boundaries", async () => {
const payload = bytes(70_013);
for (const trailer of [true, false]) {
@@ -282,20 +329,28 @@ describe("S3 compatibility", () => {
const source = bytes(1_500_123);
const create = await worker.fetch(await signed("/test-bucket/big.bin?uploads", { method: "POST", headers: { "Content-Type": "application/octet-stream" } }), ENV, CTX);
expect(create.status).toBe(200);
- const uploadId = /([^<]+)<\/UploadId>/.exec(await create.text())![1];
+ const uploadId = /([^<]+)<\/UploadId>/.exec(await create.text())?.[1];
+ expect(uploadId).toBeDefined();
+ if (!uploadId) throw new Error("Multipart initiation did not return an upload ID");
const completed: Array<{ partNumber: number; etag: string }> = [];
for (let index = 0, offset = 0; offset < source.byteLength; index++) {
const end = Math.min(source.byteLength, offset + 500_000);
const part = await worker.fetch(await signed(`/test-bucket/big.bin?partNumber=${index + 1}&uploadId=${encodeURIComponent(uploadId)}`, { method: "PUT", body: source.slice(offset, end) }), ENV, CTX);
expect(part.status).toBe(200);
- completed.push({ partNumber: index + 1, etag: part.headers.get("ETag")!.replaceAll('"', "") });
+ const partEtag = part.headers.get("ETag");
+ expect(partEtag).toBeDefined();
+ if (!partEtag) throw new Error(`Multipart part ${index + 1} did not return an ETag`);
+ completed.push({ partNumber: index + 1, etag: partEtag.replaceAll('"', "") });
offset = end;
}
const xml = `${completed.map((part) => `${part.partNumber}"${part.etag}"`).join("")}`;
const result = await worker.fetch(await signed(`/test-bucket/big.bin?uploadId=${encodeURIComponent(uploadId)}`, { method: "POST", body: xml }), ENV, CTX);
expect(result.status).toBe(200);
expect(await result.text()).toMatch(/"[0-9a-f]{32}"<\/ETag>/);
- const stored = [...drive.files.values()].find((file) => file.name === "big.bin")!.data;
+ const storedFile = [...drive.files.values()].find((file) => file.name === "big.bin");
+ expect(storedFile).toBeDefined();
+ if (!storedFile) throw new Error("Completed multipart object was not stored");
+ const stored = storedFile.data;
expect(stored.byteLength).toBe(source.byteLength);
expect(fakeMd5(stored)).toBe(fakeMd5(source));
});
@@ -317,13 +372,13 @@ describe("S3 compatibility", () => {
CTX,
);
expect(response.status).toBe(200);
- expect([...drive.files.values()].find((file) => file.name === "chunked.bin")!.data).toEqual(source);
+ expect([...drive.files.values()].find((file) => file.name === "chunked.bin")?.data).toEqual(source);
});
it("supports an empty PutObject", async () => {
const response = await worker.fetch(await signed("/test-bucket/empty", { method: "PUT", body: new Uint8Array() }), ENV, CTX);
expect(response.status).toBe(200);
- expect([...drive.files.values()].find((file) => file.name === "empty")!.data.byteLength).toBe(0);
+ expect([...drive.files.values()].find((file) => file.name === "empty")?.data.byteLength).toBe(0);
});
it("lists nested keys under a prefix, as CommonPrefixes with a delimiter and recursively without one", async () => {
diff --git a/vitest.config.mts b/vitest.config.mts
index bc13d45..0ff7d65 100644
--- a/vitest.config.mts
+++ b/vitest.config.mts
@@ -16,6 +16,8 @@ export default defineConfig({
ALLOWED_BUCKETS: "test-bucket,empty-bucket,my-bucket",
ALLOW_MULTIPART: "true",
ETAG_STYLE: "md5",
+ CORS_ALLOWED_ORIGINS: "http://localhost:5173",
+ ENABLE_DOCS: "true",
},
},
}),
diff --git a/worker-configuration.d.ts b/worker-configuration.d.ts
index 8b68580..0c1063b 100644
--- a/worker-configuration.d.ts
+++ b/worker-configuration.d.ts
@@ -1,22 +1,27 @@
/* eslint-disable */
-// Generated by Wrangler by running `wrangler types` (hash: f1715efaed926a6241da69b62ea21ad8)
-// Runtime types generated with workerd@1.20260804.1 2025-09-27
+// Generated by Wrangler by running `wrangler types` (hash: 292aeeaad14a692de2ecd641e28c3a4c)
+// Runtime types generated with workerd@1.20260811.1 2025-09-27
interface __BaseEnv_Env {
- AUTH_KV: KVNamespace;
- FOLDER_CACHE: KVNamespace;
- ALLOW_MULTIPART: "false";
- ETAG_STYLE: "md5";
- MPU: DurableObjectNamespace;
+ AUTH_KV: KVNamespace;
+ FOLDER_CACHE: KVNamespace;
+ ALLOW_MULTIPART: "true";
+ ETAG_STYLE: "md5";
+ CORS_ALLOWED_ORIGINS: "";
+ ENABLE_DOCS: "true";
+ MPU: DurableObjectNamespace;
}
declare namespace Cloudflare {
- interface GlobalProps {
- mainModule: typeof import("./src/index");
- durableNamespaces: "MultipartUploadDO";
- }
- interface Env extends __BaseEnv_Env {}
+ interface GlobalProps {
+ mainModule: typeof import("./src/index");
+ durableNamespaces: "MultipartUploadDO";
+ }
+ interface Env extends __BaseEnv_Env {}
}
interface Env extends __BaseEnv_Env {}
-
+declare module "docs/openapi.yaml" {
+ const value: string;
+ export default value;
+}
// Begin runtime types
/*! *****************************************************************************
Copyright (c) Cloudflare. All rights reserved.
@@ -105,7 +110,7 @@ declare abstract class WorkerGlobalScope extends EventTarget): Promise;
declare const self: ServiceWorkerGlobalScope;
/**
- * The Web Crypto API provides a set of low-level functions for common cryptographic tasks.
- * The Workers runtime implements the full surface of this API, but with some differences in
- * the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms)
- * compared to those implemented in most browsers.
- *
- * [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/)
- */
+* The Web Crypto API provides a set of low-level functions for common cryptographic tasks.
+* The Workers runtime implements the full surface of this API, but with some differences in
+* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms)
+* compared to those implemented in most browsers.
+*
+* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/)
+*/
declare const crypto: Crypto;
/**
- * The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.
- *
- * [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)
- */
+* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.
+*
+* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)
+*/
declare const caches: CacheStorage;
declare const scheduler: Scheduler;
/**
- * The Workers runtime supports a subset of the Performance API, used to measure timing and performance,
- * as well as timing of subrequests and other operations.
- *
- * [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/)
- */
+* The Workers runtime supports a subset of the Performance API, used to measure timing and performance,
+* as well as timing of subrequests and other operations.
+*
+* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/)
+*/
declare const performance: Performance;
declare const Cloudflare: Cloudflare;
declare const origin: string;
declare const navigator: Navigator;
-type TestController = {};
+interface TestController {
+}
interface ExecutionContext {
waitUntil(promise: Promise): void;
passThroughOnException(): void;
@@ -440,6 +446,7 @@ interface ExecutionContext {
cache?: CacheContext;
readonly access?: CloudflareAccessContext;
tracing: Tracing;
+ abort(reason?: any): void;
}
type ExportedHandlerFetchHandler = (request: Request>, env: Env, ctx: ExecutionContext) => Response | Promise;
type ExportedHandlerConnectHandler = (socket: Socket, env: Env, ctx: ExecutionContext) => void | Promise;
@@ -538,7 +545,8 @@ interface DurableObjectNamespaceGetDurableObjectOptions {
locationHint?: DurableObjectLocationHint;
routingMode?: DurableObjectRoutingMode;
}
-type DurableObjectClass<_T extends Rpc.DurableObjectBranded | undefined = undefined> = {};
+interface DurableObjectClass<_T extends Rpc.DurableObjectBranded | undefined = undefined> {
+}
interface DurableObjectState {
waitUntil(promise: Promise): void;
readonly props: Props;
@@ -996,10 +1004,10 @@ interface FileOptions {
lastModified?: number;
}
/**
- * The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.
- *
- * [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)
- */
+* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.
+*
+* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)
+*/
declare abstract class CacheStorage {
/**
* The **`open()`** method of the the Cache object matching the `cacheName`.
@@ -1010,10 +1018,10 @@ declare abstract class CacheStorage {
readonly default: Cache;
}
/**
- * The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.
- *
- * [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)
- */
+* The Cache API allows fine grained control of reading and writing from the Cloudflare global network cache.
+*
+* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/)
+*/
declare abstract class Cache {
/* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/cache/#delete) */
delete(request: RequestInfo | URL, options?: CacheQueryOptions): Promise;
@@ -1026,13 +1034,13 @@ interface CacheQueryOptions {
ignoreMethod?: boolean;
}
/**
- * The Web Crypto API provides a set of low-level functions for common cryptographic tasks.
- * The Workers runtime implements the full surface of this API, but with some differences in
- * the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms)
- * compared to those implemented in most browsers.
- *
- * [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/)
- */
+* The Web Crypto API provides a set of low-level functions for common cryptographic tasks.
+* The Workers runtime implements the full surface of this API, but with some differences in
+* the [supported algorithms](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/#supported-algorithms)
+* compared to those implemented in most browsers.
+*
+* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/web-crypto/)
+*/
declare abstract class Crypto {
/**
* The **`Crypto.subtle`** read-only property returns a cryptographic operations.
@@ -1200,26 +1208,26 @@ interface RsaOtherPrimesInfo {
}
interface SubtleCryptoDeriveKeyAlgorithm {
name: string;
- salt?: ArrayBuffer | ArrayBufferView;
+ salt?: (ArrayBuffer | ArrayBufferView);
iterations?: number;
- hash?: string | SubtleCryptoHashAlgorithm;
+ hash?: (string | SubtleCryptoHashAlgorithm);
$public?: CryptoKey;
- info?: ArrayBuffer | ArrayBufferView;
+ info?: (ArrayBuffer | ArrayBufferView);
}
interface SubtleCryptoEncryptAlgorithm {
name: string;
- iv?: ArrayBuffer | ArrayBufferView;
- additionalData?: ArrayBuffer | ArrayBufferView;
+ iv?: (ArrayBuffer | ArrayBufferView);
+ additionalData?: (ArrayBuffer | ArrayBufferView);
tagLength?: number;
- counter?: ArrayBuffer | ArrayBufferView;
+ counter?: (ArrayBuffer | ArrayBufferView);
length?: number;
- label?: ArrayBuffer | ArrayBufferView;
+ label?: (ArrayBuffer | ArrayBufferView);
}
interface SubtleCryptoGenerateKeyAlgorithm {
name: string;
- hash?: string | SubtleCryptoHashAlgorithm;
+ hash?: (string | SubtleCryptoHashAlgorithm);
modulusLength?: number;
- publicExponent?: ArrayBuffer | ArrayBufferView;
+ publicExponent?: (ArrayBuffer | ArrayBufferView);
length?: number;
namedCurve?: string;
}
@@ -1228,14 +1236,14 @@ interface SubtleCryptoHashAlgorithm {
}
interface SubtleCryptoImportKeyAlgorithm {
name: string;
- hash?: string | SubtleCryptoHashAlgorithm;
+ hash?: (string | SubtleCryptoHashAlgorithm);
length?: number;
namedCurve?: string;
compressed?: boolean;
}
interface SubtleCryptoSignAlgorithm {
name: string;
- hash?: string | SubtleCryptoHashAlgorithm;
+ hash?: (string | SubtleCryptoHashAlgorithm);
dataLength?: number;
saltLength?: number;
}
@@ -1284,7 +1292,7 @@ declare class TextDecoder {
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/TextDecoder/decode)
*/
- decode(input?: ArrayBuffer | ArrayBufferView, options?: TextDecoderDecodeOptions): string;
+ decode(input?: (ArrayBuffer | ArrayBufferView), options?: TextDecoderDecodeOptions): string;
get encoding(): string;
get fatal(): boolean;
get ignoreBOM(): boolean;
@@ -1494,13 +1502,19 @@ declare class FormData {
*/
set(name: string, value: Blob, filename?: string): void;
/* Returns an array of key, value pairs for every entry in the list. */
- entries(): IterableIterator<[key: string, value: File | string]>;
+ entries(): IterableIterator<[
+ key: string,
+ value: File | string
+ ]>;
/* Returns a list of keys in the list. */
keys(): IterableIterator;
/* Returns a list of values in the list. */
- values(): IterableIterator;
+ values(): IterableIterator<(File | string)>;
forEach(callback: (this: This, value: File | string, key: string, parent: FormData) => void, thisArg?: This): void;
- [Symbol.iterator](): IterableIterator<[key: string, value: File | string]>;
+ [Symbol.iterator](): IterableIterator<[
+ key: string,
+ value: File | string
+ ]>;
}
interface ContentOptions {
html?: boolean;
@@ -1639,12 +1653,18 @@ declare class Headers {
delete(name: string): void;
forEach(callback: (this: This, value: string, key: string, parent: Headers) => void, thisArg?: This): void;
/* Returns an iterator allowing to go through all key/value pairs contained in this object. */
- entries(): IterableIterator<[key: string, value: string]>;
+ entries(): IterableIterator<[
+ key: string,
+ value: string
+ ]>;
/* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object. */
keys(): IterableIterator;
/* Returns an iterator allowing to go through all values of the key/value pairs contained in this object. */
values(): IterableIterator;
- [Symbol.iterator](): IterableIterator<[key: string, value: string]>;
+ [Symbol.iterator](): IterableIterator<[
+ key: string,
+ value: string
+ ]>;
}
type BodyInit = ReadableStream | string | ArrayBuffer | ArrayBufferView | Blob | URLSearchParams | FormData;
declare abstract class Body {
@@ -1675,7 +1695,7 @@ declare var Response: {
new (body?: BodyInit | null, init?: ResponseInit): Response;
error(): Response;
redirect(url: string, status?: number): Response;
- json(any: any, maybeInit?: ResponseInit | Response): Response;
+ json(any: any, maybeInit?: (ResponseInit | Response)): Response;
};
/**
* The **`Response`** interface of the Fetch API represents the response to a request.
@@ -1739,7 +1759,7 @@ interface ResponseInit {
statusText?: string;
headers?: HeadersInit;
cf?: any;
- webSocket?: WebSocket | null;
+ webSocket?: (WebSocket | null);
encodeBody?: "automatic" | "manual";
}
type RequestInfo> = Request | string;
@@ -1824,25 +1844,17 @@ interface RequestInit {
body?: BodyInit | null;
/* A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */
redirect?: string;
- fetcher?: Fetcher | null;
+ fetcher?: (Fetcher | null);
cf?: Cf;
/* A string indicating how the request will interact with the browser's cache to set request's cache. */
cache?: "no-store" | "no-cache";
/* A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */
integrity?: string;
/* An AbortSignal to set request's signal. */
- signal?: AbortSignal | null;
+ signal?: (AbortSignal | null);
encodeResponseBody?: "automatic" | "manual";
}
-type Service Rpc.WorkerEntrypointBranded) | Rpc.WorkerEntrypointBranded | ExportedHandler | undefined = undefined> = T extends new (
- ...args: any[]
-) => Rpc.WorkerEntrypointBranded
- ? Fetcher>
- : T extends Rpc.WorkerEntrypointBranded
- ? Fetcher
- : T extends Exclude
- ? never
- : Fetcher;
+type Service Rpc.WorkerEntrypointBranded) | Rpc.WorkerEntrypointBranded | ExportedHandler | undefined = undefined> = T extends new (...args: any[]) => Rpc.WorkerEntrypointBranded ? Fetcher> : T extends Rpc.WorkerEntrypointBranded ? Fetcher : T extends Exclude ? never : Fetcher;
type Fetcher = (T extends Rpc.EntrypointBranded ? Rpc.Provider : unknown) & {
fetch(input: RequestInfo | URL, init?: RequestInit): Promise;
connect(address: SocketAddress | string, options?: SocketOptions): Socket;
@@ -1852,18 +1864,16 @@ interface KVNamespaceListKey {
expiration?: number;
metadata?: Metadata;
}
-type KVNamespaceListResult =
- | {
- list_complete: false;
- keys: KVNamespaceListKey[];
- cursor: string;
- cacheStatus: string | null;
- }
- | {
- list_complete: true;
- keys: KVNamespaceListKey[];
- cacheStatus: string | null;
- };
+type KVNamespaceListResult = {
+ list_complete: false;
+ keys: KVNamespaceListKey[];
+ cursor: string;
+ cacheStatus: string | null;
+} | {
+ list_complete: true;
+ keys: KVNamespaceListKey[];
+ cacheStatus: string | null;
+};
interface KVNamespace {
get(key: Key, options?: Partial>): Promise;
get(key: Key, type: "text"): Promise;
@@ -1899,8 +1909,8 @@ interface KVNamespace {
}
interface KVNamespaceListOptions {
limit?: number;
- prefix?: string | null;
- cursor?: string | null;
+ prefix?: (string | null);
+ cursor?: (string | null);
}
interface KVNamespaceGetOptions {
type: Type;
@@ -1909,7 +1919,7 @@ interface KVNamespaceGetOptions {
interface KVNamespacePutOptions {
expiration?: number;
expirationTtl?: number;
- metadata?: any | null;
+ metadata?: (any | null);
}
interface KVNamespaceGetWithMetadataResult {
value: Value | null;
@@ -2011,20 +2021,13 @@ interface R2ListOptions {
}
interface R2Bucket {
head(key: string): Promise;
- get(
- key: string,
- options: R2GetOptions & {
- onlyIf: R2Conditional | Headers;
- },
- ): Promise;
+ get(key: string, options: R2GetOptions & {
+ onlyIf: R2Conditional | Headers;
+ }): Promise;
get(key: string, options?: R2GetOptions): Promise;
- put(
- key: string,
- value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob,
- options?: R2PutOptions & {
- onlyIf: R2Conditional | Headers;
- },
- ): Promise;
+ put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions & {
+ onlyIf: R2Conditional | Headers;
+ }): Promise;
put(key: string, value: ReadableStream | ArrayBuffer | ArrayBufferView | string | null | Blob, options?: R2PutOptions): Promise;
createMultipartUpload(key: string, options?: R2MultipartOptions): Promise;
resumeMultipartUpload(key: string, uploadId: string): R2MultipartUpload;
@@ -2066,18 +2069,15 @@ interface R2ObjectBody extends R2Object {
json(): Promise;
blob(): Promise;
}
-type R2Range =
- | {
- offset: number;
- length?: number;
- }
- | {
- offset?: number;
- length: number;
- }
- | {
- suffix: number;
- };
+type R2Range = {
+ offset: number;
+ length?: number;
+} | {
+ offset?: number;
+ length: number;
+} | {
+ suffix: number;
+};
interface R2Conditional {
etagMatches?: string;
etagDoesNotMatch?: string;
@@ -2086,27 +2086,27 @@ interface R2Conditional {
secondsGranularity?: boolean;
}
interface R2GetOptions {
- onlyIf?: R2Conditional | Headers;
- range?: R2Range | Headers;
- ssecKey?: ArrayBuffer | string;
+ onlyIf?: (R2Conditional | Headers);
+ range?: (R2Range | Headers);
+ ssecKey?: (ArrayBuffer | string);
}
interface R2PutOptions {
- onlyIf?: R2Conditional | Headers;
- httpMetadata?: R2HTTPMetadata | Headers;
+ onlyIf?: (R2Conditional | Headers);
+ httpMetadata?: (R2HTTPMetadata | Headers);
customMetadata?: Record;
- md5?: (ArrayBuffer | ArrayBufferView) | string;
- sha1?: (ArrayBuffer | ArrayBufferView) | string;
- sha256?: (ArrayBuffer | ArrayBufferView) | string;
- sha384?: (ArrayBuffer | ArrayBufferView) | string;
- sha512?: (ArrayBuffer | ArrayBufferView) | string;
+ md5?: ((ArrayBuffer | ArrayBufferView) | string);
+ sha1?: ((ArrayBuffer | ArrayBufferView) | string);
+ sha256?: ((ArrayBuffer | ArrayBufferView) | string);
+ sha384?: ((ArrayBuffer | ArrayBufferView) | string);
+ sha512?: ((ArrayBuffer | ArrayBufferView) | string);
storageClass?: string;
- ssecKey?: ArrayBuffer | string;
+ ssecKey?: (ArrayBuffer | string);
}
interface R2MultipartOptions {
- httpMetadata?: R2HTTPMetadata | Headers;
+ httpMetadata?: (R2HTTPMetadata | Headers);
customMetadata?: Record;
storageClass?: string;
- ssecKey?: ArrayBuffer | string;
+ ssecKey?: (ArrayBuffer | string);
}
interface R2Checksums {
readonly md5?: ArrayBuffer;
@@ -2134,17 +2134,14 @@ interface R2HTTPMetadata {
type R2Objects = {
objects: R2Object[];
delimitedPrefixes: string[];
-} & (
- | {
- truncated: true;
- cursor: string;
- }
- | {
- truncated: false;
- }
-);
+} & ({
+ truncated: true;
+ cursor: string;
+} | {
+ truncated: false;
+});
interface R2UploadPartOptions {
- ssecKey?: ArrayBuffer | string;
+ ssecKey?: (ArrayBuffer | string);
}
declare abstract class ScheduledEvent extends ExtendableEvent {
readonly scheduledTime: number;
@@ -2157,7 +2154,7 @@ interface ScheduledController {
noRetry(): void;
}
interface QueuingStrategy {
- highWaterMark?: number | bigint;
+ highWaterMark?: (number | bigint);
size?: (chunk: T) => number | bigint;
}
interface UnderlyingSink {
@@ -2179,7 +2176,7 @@ interface UnderlyingSource {
start?: (controller: ReadableStreamDefaultController) => void | Promise;
pull?: (controller: ReadableStreamDefaultController) => void | Promise;
cancel?: (reason: any) => void | Promise;
- expectedLength?: number | bigint;
+ expectedLength?: (number | bigint);
}
interface Transformer {
readableType?: string;
@@ -2213,15 +2210,13 @@ interface StreamPipeOptions {
preventClose?: boolean;
signal?: AbortSignal;
}
-type ReadableStreamReadResult =
- | {
- done: false;
- value: R;
- }
- | {
- done: true;
- value?: undefined;
- };
+type ReadableStreamReadResult = {
+ done: false;
+ value: R;
+} | {
+ done: true;
+ value?: undefined;
+};
/**
* The `ReadableStream` interface of the Streams API represents a readable stream of byte data.
*
@@ -2269,7 +2264,10 @@ interface ReadableStream {
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStream/tee)
*/
- tee(): [ReadableStream, ReadableStream];
+ tee(): [
+ ReadableStream,
+ ReadableStream
+ ];
values(options?: ReadableStreamValuesOptions): AsyncIterableIterator;
[Symbol.asyncIterator](options?: ReadableStreamValuesOptions): AsyncIterableIterator;
}
@@ -2601,7 +2599,7 @@ declare class IdentityTransformStream extends TransformStream> | Record | string);
+ constructor(init?: (Iterable> | Record | string));
/**
* The **`size`** read-only property of the URLSearchParams interface indicates the total number of search parameter entries.
*
@@ -3026,7 +3032,10 @@ declare class URLSearchParams {
*/
sort(): void;
/* Returns an array of key, value pairs for every entry in the search params. */
- entries(): IterableIterator<[key: string, value: string]>;
+ entries(): IterableIterator<[
+ key: string,
+ value: string
+ ]>;
/* Returns a list of keys in the search params. */
keys(): IterableIterator;
/* Returns a list of values in the search params. */
@@ -3034,10 +3043,13 @@ declare class URLSearchParams {
forEach(callback: (this: This, value: string, key: string, parent: URLSearchParams) => void, thisArg?: This): void;
/*function toString() { [native code] }*/
toString(): string;
- [Symbol.iterator](): IterableIterator<[key: string, value: string]>;
+ [Symbol.iterator](): IterableIterator<[
+ key: string,
+ value: string
+ ]>;
}
declare class URLPattern {
- constructor(input?: string | URLPatternInit, baseURL?: string | URLPatternOptions, patternOptions?: URLPatternOptions);
+ constructor(input?: (string | URLPatternInit), baseURL?: (string | URLPatternOptions), patternOptions?: URLPatternOptions);
get protocol(): string;
get username(): string;
get password(): string;
@@ -3047,8 +3059,8 @@ declare class URLPattern {
get search(): string;
get hash(): string;
get hasRegExpGroups(): boolean;
- test(input?: string | URLPatternInit, baseURL?: string): boolean;
- exec(input?: string | URLPatternInit, baseURL?: string): URLPatternResult | null;
+ test(input?: (string | URLPatternInit), baseURL?: string): boolean;
+ exec(input?: (string | URLPatternInit), baseURL?: string): URLPatternResult | null;
}
interface URLPatternInit {
protocol?: string;
@@ -3123,7 +3135,7 @@ type WebSocketEventMap = {
*/
declare var WebSocket: {
prototype: WebSocket;
- new (url: string, protocols?: string[] | string): WebSocket;
+ new (url: string, protocols?: (string[] | string)): WebSocket;
readonly READY_STATE_CONNECTING: number;
readonly CONNECTING: number;
readonly READY_STATE_OPEN: number;
@@ -3207,18 +3219,17 @@ interface SqlStorage {
Cursor: typeof SqlStorageCursor;
Statement: typeof SqlStorageStatement;
}
-declare abstract class SqlStorageStatement {}
+declare abstract class SqlStorageStatement {
+}
type SqlStorageValue = ArrayBuffer | string | number | null;
declare abstract class SqlStorageCursor> {
- next():
- | {
- done?: false;
- value: T;
- }
- | {
- done: true;
- value?: never;
- };
+ next(): {
+ done?: false;
+ value: T;
+ } | {
+ done: true;
+ value?: never;
+ };
toArray(): T[];
one(): T;
raw(): IterableIterator;
@@ -3240,7 +3251,7 @@ interface Socket {
interface SocketOptions {
secureTransport?: string;
allowHalfOpen: boolean;
- highWaterMark?: number | bigint;
+ highWaterMark?: (number | bigint);
}
interface SocketAddress {
hostname: string;
@@ -3314,18 +3325,26 @@ interface ContainerExecOptions {
cwd?: string;
env?: Record;
user?: string;
+ signal?: AbortSignal;
+ pty?: boolean | ContainerExecPtyOptions;
stdin?: ReadableStream | "pipe";
stdout?: "pipe" | "ignore";
stderr?: "pipe" | "ignore" | "combined";
}
+interface ContainerExecPtyOptions {
+ cols?: number;
+ rows?: number;
+}
interface ExecProcess {
readonly stdin: WritableStream | null;
readonly stdout: ReadableStream | null;
readonly stderr: ReadableStream | null;
readonly pid: number;
+ readonly isPty: boolean;
readonly exitCode: Promise;
output(): Promise;
kill(signal?: number): void;
+ resize(cols: number, rows: number): void;
}
interface Container {
get running(): boolean;
@@ -3372,6 +3391,11 @@ interface ContainerStartupOptions {
directorySnapshots?: ContainerDirectorySnapshotRestoreParams[];
containerSnapshot?: ContainerSnapshot;
}
+interface ContainerStartResources {
+ vcpu: number;
+ memoryMib: number;
+ diskMb: number;
+}
/**
* The **`MessagePort`** interface of the Channel Messaging API represents one of the two ports of a MessageChannel, allowing messages to be sent from one port and listening out for them arriving at the other.
*
@@ -3383,7 +3407,7 @@ declare abstract class MessagePort extends EventTarget {
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/MessagePort/postMessage)
*/
- postMessage(data?: any, options?: any[] | MessagePortPostMessageOptions): void;
+ postMessage(data?: any, options?: (any[] | MessagePortPostMessageOptions)): void;
/**
* The **`close()`** method of the MessagePort interface disconnects the port, so it is no longer active.
*
@@ -3422,24 +3446,27 @@ declare class MessageChannel {
interface MessagePortPostMessageOptions {
transfer?: any[];
}
-type LoopbackForExport Rpc.EntrypointBranded) | ExportedHandler | undefined = undefined> = T extends new (
- ...args: any[]
-) => Rpc.WorkerEntrypointBranded
- ? LoopbackServiceStub>
- : T extends new (
- ...args: any[]
- ) => Rpc.DurableObjectBranded
- ? LoopbackDurableObjectClass>
- : T extends ExportedHandler
- ? LoopbackServiceStub
- : undefined;
-type LoopbackServiceStub = Fetcher & (T extends CloudflareWorkersModule.WorkerEntrypoint ? (opts: { props?: Props }) => Fetcher : (opts: { props?: any }) => Fetcher);
-type LoopbackDurableObjectClass = DurableObjectClass & (T extends CloudflareWorkersModule.DurableObject ? (opts: { props?: Props }) => DurableObjectClass : (opts: { props?: any }) => DurableObjectClass);
-interface LoopbackDurableObjectNamespace extends DurableObjectNamespace {}
-interface LoopbackColoLocalActorNamespace extends ColoLocalActorNamespace {}
+type LoopbackForExport Rpc.EntrypointBranded) | ExportedHandler | undefined = undefined> = T extends new (...args: any[]) => Rpc.WorkerEntrypointBranded ? LoopbackServiceStub> : T extends new (...args: any[]) => Rpc.DurableObjectBranded ? LoopbackDurableObjectClass> : T extends ExportedHandler ? LoopbackServiceStub : undefined;
+type LoopbackServiceStub = Fetcher & (T extends CloudflareWorkersModule.WorkerEntrypoint ? (opts: {
+ props?: Props;
+}) => Fetcher : (opts: {
+ props?: any;
+}) => Fetcher);
+type LoopbackDurableObjectClass = DurableObjectClass & (T extends CloudflareWorkersModule.DurableObject ? (opts: {
+ props?: Props;
+}) => DurableObjectClass : (opts: {
+ props?: any;
+}) => DurableObjectClass);
+interface LoopbackDurableObjectNamespace extends DurableObjectNamespace {
+}
+interface LoopbackColoLocalActorNamespace extends ColoLocalActorNamespace {
+}
interface SyncKvStorage {
get(key: string): T | undefined;
- list(options?: SyncKvListOptions): Iterable<[string, T]>;
+ list(options?: SyncKvListOptions): Iterable<[
+ string,
+ T
+ ]>;
put(key: string, value: T): void;
delete(key: string): boolean;
}
@@ -3480,7 +3507,7 @@ interface WorkerLoaderWorkerCode {
mainModule: string;
modules: Record;
env?: any;
- globalOutbound?: Fetcher | null;
+ globalOutbound?: (Fetcher | null);
tails?: Fetcher[];
streamingTails?: Fetcher[];
}
@@ -3489,11 +3516,11 @@ interface workerdResourceLimits {
subRequests?: number;
}
/**
- * The Workers runtime supports a subset of the Performance API, used to measure timing and performance,
- * as well as timing of subrequests and other operations.
- *
- * [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/)
- */
+* The Workers runtime supports a subset of the Performance API, used to measure timing and performance,
+* as well as timing of subrequests and other operations.
+*
+* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/)
+*/
declare abstract class Performance {
/* [Cloudflare Docs Reference](https://developers.cloudflare.com/workers/runtime-apis/performance/#performancetimeorigin) */
get timeOrigin(): number;
@@ -3509,11 +3536,13 @@ declare abstract class Performance {
interface Tracing {
enterSpan(name: string, callback: (span: Span, ...args: A) => T, ...args: A): T;
startActiveSpan(name: string, callback: (span: Span, ...args: A) => T, ...args: A): T;
+ startSpan(name: string): Span;
Span: typeof Span;
}
declare abstract class Span {
get isTraced(): boolean;
- setAttribute(key: string, value?: boolean | number | string): void;
+ setAttribute(key: string, value: boolean | number | string): this;
+ setAttributes(attributes: Record): this;
end(): void;
}
/**
@@ -3785,12 +3814,14 @@ declare abstract class AgentMemoryNamespace {
deleteProfile(profileName: string): Promise;
}
// ============ AI Search Error Interfaces ============
-interface AiSearchInternalError extends Error {}
-interface AiSearchNotFoundError extends Error {}
+interface AiSearchInternalError extends Error {
+}
+interface AiSearchNotFoundError extends Error {
+}
// ============ AI Search Common Types ============
/** A single message in a conversation-style search or chat request. */
type AiSearchMessage = {
- role: "system" | "developer" | "user" | "assistant" | "tool";
+ role: 'system' | 'developer' | 'user' | 'assistant' | 'tool';
content: string | null;
};
/**
@@ -3800,11 +3831,11 @@ type AiSearchMessage = {
type AiSearchOptions = {
retrieval?: {
/** Which retrieval backend to use. Defaults to the instance's configured index_method. */
- retrieval_type?: "vector" | "keyword" | "hybrid";
+ retrieval_type?: 'vector' | 'keyword' | 'hybrid';
/** Fusion method for combining vector + keyword results. */
- fusion_method?: "max" | "rrf";
+ fusion_method?: 'max' | 'rrf';
/** How keyword terms are combined: "and" = all terms must match, "or" = any term matches. */
- keyword_match_mode?: "and" | "or";
+ keyword_match_mode?: 'and' | 'or';
/** Minimum similarity score (0-1) for a result to be included. Default 0.4. */
match_threshold?: number;
/** Maximum number of results to return (1-50). Default 10. */
@@ -3820,7 +3851,7 @@ type AiSearchOptions = {
/** Boost results by metadata field values. Max 3 entries. */
boost_by?: Array<{
field: string;
- direction?: "asc" | "desc" | "exists" | "not_exists";
+ direction?: 'asc' | 'desc' | 'exists' | 'not_exists';
}>;
[key: string]: unknown;
};
@@ -3839,7 +3870,7 @@ type AiSearchOptions = {
};
cache?: {
enabled?: boolean;
- cache_threshold?: "super_strict_match" | "close_enough" | "flexible_friend" | "anything_goes";
+ cache_threshold?: 'super_strict_match' | 'close_enough' | 'flexible_friend' | 'anything_goes';
};
[key: string]: unknown;
};
@@ -3848,19 +3879,17 @@ type AiSearchOptions = {
* Request body for single-instance search.
* Exactly one of `query` or `messages` must be provided.
*/
-type AiSearchSearchRequest =
- | {
- /** Simple query string. */
- query: string;
- messages?: never;
- ai_search_options?: AiSearchOptions;
- }
- | {
- query?: never;
- /** Conversation-style input. At least one user message with non-empty content is required. */
- messages: AiSearchMessage[];
- ai_search_options?: AiSearchOptions;
- };
+type AiSearchSearchRequest = {
+ /** Simple query string. */
+ query: string;
+ messages?: never;
+ ai_search_options?: AiSearchOptions;
+} | {
+ query?: never;
+ /** Conversation-style input. At least one user message with non-empty content is required. */
+ messages: AiSearchMessage[];
+ ai_search_options?: AiSearchOptions;
+};
type AiSearchChatCompletionsRequest = {
messages: AiSearchMessage[];
model?: string;
@@ -3879,21 +3908,19 @@ type AiSearchMultiSearchOptions = AiSearchOptions & {
* `ai_search_options` is required and must include `instance_ids`.
* Exactly one of `query` or `messages` must be provided.
*/
-type AiSearchMultiSearchRequest =
- | {
- /** Simple query string. */
- query: string;
- messages?: never;
- ai_search_options: AiSearchMultiSearchOptions;
- }
- | {
- query?: never;
- /** Conversation-style input. */
- messages: AiSearchMessage[];
- ai_search_options: AiSearchMultiSearchOptions;
- };
+type AiSearchMultiSearchRequest = {
+ /** Simple query string. */
+ query: string;
+ messages?: never;
+ ai_search_options: AiSearchMultiSearchOptions;
+} | {
+ query?: never;
+ /** Conversation-style input. */
+ messages: AiSearchMessage[];
+ ai_search_options: AiSearchMultiSearchOptions;
+};
/** A search result chunk tagged with the instance it originated from. */
-type AiSearchMultiSearchChunk = AiSearchSearchResponse["chunks"][number] & {
+type AiSearchMultiSearchChunk = AiSearchSearchResponse['chunks'][number] & {
instance_id: string;
};
/** Describes a per-instance error during a multi-instance operation. */
@@ -3908,11 +3935,11 @@ type AiSearchMultiSearchResponse = {
errors?: AiSearchMultiSearchError[];
};
/** Request for chat completions across multiple instances within a namespace. `ai_search_options` is required and must include `instance_ids`. */
-type AiSearchMultiChatCompletionsRequest = Omit & {
+type AiSearchMultiChatCompletionsRequest = Omit & {
ai_search_options: AiSearchMultiSearchOptions;
};
/** Response from multi-instance chat completions, with chunks tagged by instance and optional partial-failure errors. */
-type AiSearchMultiChatCompletionsResponse = Omit & {
+type AiSearchMultiChatCompletionsResponse = Omit & {
chunks: AiSearchMultiSearchChunk[];
errors?: AiSearchMultiSearchError[];
};
@@ -3942,7 +3969,7 @@ type AiSearchSearchResponse = {
/** Reranking model score */
reranking_score?: number;
/** Fusion method used to combine results */
- fusion_method?: "rrf" | "max";
+ fusion_method?: 'rrf' | 'max';
[key: string]: unknown;
};
}>;
@@ -3954,13 +3981,13 @@ type AiSearchChatCompletionsResponse = {
choices: Array<{
index?: number;
message: {
- role: "system" | "developer" | "user" | "assistant" | "tool";
+ role: 'system' | 'developer' | 'user' | 'assistant' | 'tool';
content: string | null;
[key: string]: unknown;
};
[key: string]: unknown;
}>;
- chunks: AiSearchSearchResponse["chunks"];
+ chunks: AiSearchSearchResponse['chunks'];
[key: string]: unknown;
};
type AiSearchStatsResponse = {
@@ -3987,7 +4014,7 @@ type AiSearchStatsResponse = {
// ============ AI Search Instance Info Types ============
type AiSearchInstanceInfo = {
id: string;
- type?: "r2" | "web-crawler" | string;
+ type?: 'r2' | 'web-crawler' | string;
source?: string;
source_params?: unknown;
paused?: boolean;
@@ -4011,15 +4038,15 @@ type AiSearchInstanceInfo = {
keyword?: boolean;
};
/** Fusion method for combining vector and keyword results. */
- fusion_method?: "max" | "rrf";
+ fusion_method?: 'max' | 'rrf';
indexing_options?: {
- keyword_tokenizer?: "porter" | "trigram";
+ keyword_tokenizer?: 'porter' | 'trigram';
} | null;
retrieval_options?: {
- keyword_match_mode?: "and" | "or";
+ keyword_match_mode?: 'and' | 'or';
boost_by?: Array<{
field: string;
- direction?: "asc" | "desc" | "exists" | "not_exists";
+ direction?: 'asc' | 'desc' | 'exists' | 'not_exists';
}>;
} | null;
chunk?: boolean;
@@ -4028,10 +4055,10 @@ type AiSearchInstanceInfo = {
score_threshold?: number;
max_num_results?: number;
cache?: boolean;
- cache_threshold?: "super_strict_match" | "close_enough" | "flexible_friend" | "anything_goes";
+ cache_threshold?: 'super_strict_match' | 'close_enough' | 'flexible_friend' | 'anything_goes';
custom_metadata?: Array<{
field_name: string;
- data_type: "text" | "number" | "boolean" | "datetime";
+ data_type: 'text' | 'number' | 'boolean' | 'datetime';
}>;
/** Sync interval in seconds. */
sync_interval?: 3600 | 7200 | 14400 | 21600 | 43200 | 86400;
@@ -4045,9 +4072,9 @@ type AiSearchListInstancesParams = {
/** Search instances by ID. */
search?: string;
/** Field to sort by. */
- order_by?: "created_at";
+ order_by?: 'created_at';
/** Sort direction. */
- order_by_direction?: "asc" | "desc";
+ order_by_direction?: 'asc' | 'desc';
};
type AiSearchListResponse = {
result: AiSearchInstanceInfo[];
@@ -4063,7 +4090,7 @@ type AiSearchConfig = {
/** Instance ID (1-32 chars, pattern: ^[a-z0-9_]+(?:-[a-z0-9_]+)*$) */
id: string;
/** Instance type. Omit to create with built-in storage. */
- type?: "r2" | "web-crawler" | string;
+ type?: 'r2' | 'web-crawler' | string;
/** Source URL (required for web-crawler type). */
source?: string;
source_params?: unknown;
@@ -4086,15 +4113,15 @@ type AiSearchConfig = {
keyword?: boolean;
};
/** Fusion method for combining vector and keyword results. "rrf" = reciprocal rank fusion (default), "max" = maximum score. */
- fusion_method?: "max" | "rrf";
+ fusion_method?: 'max' | 'rrf';
indexing_options?: {
- keyword_tokenizer?: "porter" | "trigram";
+ keyword_tokenizer?: 'porter' | 'trigram';
} | null;
retrieval_options?: {
- keyword_match_mode?: "and" | "or";
+ keyword_match_mode?: 'and' | 'or';
boost_by?: Array<{
field: string;
- direction?: "asc" | "desc" | "exists" | "not_exists";
+ direction?: 'asc' | 'desc' | 'exists' | 'not_exists';
}>;
} | null;
chunk?: boolean;
@@ -4105,10 +4132,10 @@ type AiSearchConfig = {
max_num_results?: number;
cache?: boolean;
/** Similarity threshold for cache hits. Stricter = fewer cache hits but higher relevance. */
- cache_threshold?: "super_strict_match" | "close_enough" | "flexible_friend" | "anything_goes";
+ cache_threshold?: 'super_strict_match' | 'close_enough' | 'flexible_friend' | 'anything_goes';
custom_metadata?: Array<{
field_name: string;
- data_type: "text" | "number" | "boolean" | "datetime";
+ data_type: 'text' | 'number' | 'boolean' | 'datetime';
}>;
namespace?: string;
/** Sync interval in seconds. 3600=1h, 7200=2h, 14400=4h, 21600=6h, 43200=12h, 86400=24h. */
@@ -4120,8 +4147,8 @@ type AiSearchConfig = {
type AiSearchItemInfo = {
id: string;
key: string;
- status: "completed" | "error" | "skipped" | "queued" | "running" | "outdated";
- next_action?: "INDEX" | "DELETE" | null;
+ status: 'completed' | 'error' | 'skipped' | 'queued' | 'running' | 'outdated';
+ next_action?: 'INDEX' | 'DELETE' | null;
error?: string;
checksum?: string;
namespace?: string;
@@ -4148,13 +4175,20 @@ type AiSearchListItemsParams = {
/** Search items by key name. */
search?: string;
/** Sort order for results. */
- sort_by?: "status" | "modified_at";
+ sort_by?: 'status' | 'modified_at';
/** Filter items by processing status. */
- status?: "queued" | "running" | "completed" | "error" | "skipped" | "outdated";
+ status?: 'queued' | 'running' | 'completed' | 'error' | 'skipped' | 'outdated';
/** Filter items by source (e.g. "builtin" or "web-crawler:https://example.com"). */
source?: string;
/** JSON-encoded Vectorize filter for metadata filtering. */
metadata_filter?: string;
+ /** Filter items by their unique ID. Returns at most one item. */
+ item_id?: string;
+ /**
+ * Filter items by their exact key (object key / filename). Keys are unique
+ * per source, so combine with `source` to disambiguate across data sources.
+ */
+ key?: string;
};
type AiSearchListItemsResponse = {
result: AiSearchItemInfo[];
@@ -4223,7 +4257,7 @@ type AiSearchItemChunksResponse = {
// ============ AI Search Job Types ============
type AiSearchJobInfo = {
id: string;
- source: "user" | "schedule";
+ source: 'user' | 'schedule';
description?: string;
last_seen_at?: string;
started_at?: string;
@@ -4321,16 +4355,12 @@ declare abstract class AiSearchItems {
* @param options Optional metadata and polling configuration.
* @returns The item info after processing completes (or timeout).
*/
- uploadAndPoll(
- name: string,
- content: ReadableStream | Blob | string,
- options?: AiSearchUploadItemOptions & {
- /** Polling interval in milliseconds (default 1000). */
- pollIntervalMs?: number;
- /** Maximum time to wait in milliseconds (default 30000). */
- timeoutMs?: number;
- },
- ): Promise;
+ uploadAndPoll(name: string, content: ReadableStream | Blob | string, options?: AiSearchUploadItemOptions & {
+ /** Polling interval in milliseconds (default 1000). */
+ pollIntervalMs?: number;
+ /** Maximum time to wait in milliseconds (default 30000). */
+ timeoutMs?: number;
+ }): Promise;
/**
* Get an item by ID.
* @param itemId The item identifier.
@@ -4415,11 +4445,9 @@ declare abstract class AiSearchInstance {
* @param params Chat completions request with stream: true.
* @returns ReadableStream of server-sent events.
*/
- chatCompletions(
- params: AiSearchChatCompletionsRequest & {
- stream: true;
- },
- ): Promise;
+ chatCompletions(params: AiSearchChatCompletionsRequest & {
+ stream: true;
+ }): Promise;
/**
* Generate chat completions with AI Search context.
* @param params Chat completions request.
@@ -4526,11 +4554,9 @@ declare abstract class AiSearchNamespace {
* @param params Chat completions request with stream: true and required `ai_search_options.instance_ids`.
* @returns ReadableStream of server-sent events.
*/
- chatCompletions(
- params: AiSearchMultiChatCompletionsRequest & {
- stream: true;
- },
- ): Promise;
+ chatCompletions(params: AiSearchMultiChatCompletionsRequest & {
+ stream: true;
+ }): Promise;
/**
* Generate chat completions across multiple instances within the bound namespace.
* Fans out to the specified instance_ids, merges context, and generates a response.
@@ -4765,11 +4791,9 @@ type AiTextToSpeechInput = {
prompt: string;
lang?: string;
};
-type AiTextToSpeechOutput =
- | Uint8Array
- | {
- audio: string;
- };
+type AiTextToSpeechOutput = Uint8Array | {
+ audio: string;
+};
declare abstract class BaseAiTextToSpeech {
inputs: AiTextToSpeechInput;
postProcessedOutputs: AiTextToSpeechOutput;
@@ -4911,22 +4935,18 @@ type ChatCompletionToolChoiceAllowedTools = {
type ChatCompletionToolChoiceOption = "none" | "auto" | "required" | ChatCompletionToolChoiceFunction | ChatCompletionToolChoiceCustom | ChatCompletionToolChoiceAllowedTools;
type DeveloperMessage = {
role: "developer";
- content:
- | string
- | Array<{
- type: "text";
- text: string;
- }>;
+ content: string | Array<{
+ type: "text";
+ text: string;
+ }>;
name?: string;
};
type SystemMessage = {
role: "system";
- content:
- | string
- | Array<{
- type: "text";
- text: string;
- }>;
+ content: string | Array<{
+ type: "text";
+ text: string;
+ }>;
name?: string;
};
/**
@@ -4979,12 +4999,10 @@ type AssistantMessage = {
};
type ToolMessage = {
role: "tool";
- content:
- | string
- | Array<{
- type: "text";
- text: string;
- }>;
+ content: string | Array<{
+ type: "text";
+ text: string;
+ }>;
tool_call_id: string;
};
type FunctionMessage = {
@@ -5015,19 +5033,15 @@ type ChatCompletionsStreamOptions = {
};
type PredictionContent = {
type: "content";
- content:
- | string
- | Array<{
- type: "text";
- text: string;
- }>;
+ content: string | Array<{
+ type: "text";
+ text: string;
+ }>;
};
type AudioParams = {
- voice:
- | string
- | {
- id: string;
- };
+ voice: string | {
+ id: string;
+ };
format: "wav" | "aac" | "mp3" | "flac" | "opus" | "pcm16";
};
type WebSearchUserLocation = {
@@ -5080,12 +5094,9 @@ type ChatCompletionsCommonOptions = {
top_p?: number | null;
user?: string;
web_search_options?: WebSearchOptions;
- function_call?:
- | "none"
- | "auto"
- | {
- name: string;
- };
+ function_call?: "none" | "auto" | {
+ name: string;
+ };
functions?: Array;
};
type PromptTokensDetails = {
@@ -5273,25 +5284,7 @@ type ResponseCustomToolCallOutput = {
id?: string;
};
type ResponseError = {
- code:
- | "server_error"
- | "rate_limit_exceeded"
- | "invalid_prompt"
- | "vector_store_timeout"
- | "invalid_image"
- | "invalid_image_format"
- | "invalid_base64_image"
- | "invalid_image_url"
- | "image_too_large"
- | "image_too_small"
- | "image_parse_error"
- | "image_content_policy_violation"
- | "invalid_image_mode"
- | "image_file_too_large"
- | "unsupported_image_media_type"
- | "empty_image_file"
- | "failed_to_download_image"
- | "image_file_not_found";
+ code: "server_error" | "rate_limit_exceeded" | "invalid_prompt" | "vector_store_timeout" | "invalid_image" | "invalid_image_format" | "invalid_base64_image" | "invalid_image_url" | "image_too_large" | "image_too_small" | "image_parse_error" | "image_content_policy_violation" | "invalid_image_mode" | "image_file_too_large" | "unsupported_image_media_type" | "empty_image_file" | "failed_to_download_image" | "image_file_not_found";
message: string;
};
type ResponseErrorEvent = {
@@ -5490,22 +5483,7 @@ type ResponseRefusalDoneEvent = {
type: "response.refusal.done";
};
type ResponseStatus = "completed" | "failed" | "in_progress" | "cancelled" | "queued" | "incomplete";
-type ResponseStreamEvent =
- | ResponseCompletedEvent
- | ResponseCreatedEvent
- | ResponseErrorEvent
- | ResponseFunctionCallArgumentsDeltaEvent
- | ResponseFunctionCallArgumentsDoneEvent
- | ResponseFailedEvent
- | ResponseIncompleteEvent
- | ResponseOutputItemAddedEvent
- | ResponseOutputItemDoneEvent
- | ResponseReasoningTextDeltaEvent
- | ResponseReasoningTextDoneEvent
- | ResponseRefusalDeltaEvent
- | ResponseRefusalDoneEvent
- | ResponseTextDeltaEvent
- | ResponseTextDoneEvent;
+type ResponseStreamEvent = ResponseCompletedEvent | ResponseCreatedEvent | ResponseErrorEvent | ResponseFunctionCallArgumentsDeltaEvent | ResponseFunctionCallArgumentsDoneEvent | ResponseFailedEvent | ResponseIncompleteEvent | ResponseOutputItemAddedEvent | ResponseOutputItemDoneEvent | ResponseReasoningTextDeltaEvent | ResponseReasoningTextDoneEvent | ResponseRefusalDeltaEvent | ResponseRefusalDoneEvent | ResponseTextDeltaEvent | ResponseTextDoneEvent;
type ResponseCompletedEvent = {
response: Response;
sequence_number: number;
@@ -5563,39 +5541,35 @@ type Without = {
};
/** Either T or U, but not both (mutually exclusive) */
type XOR = (T & Without) | (U & Without);
-type Ai_Cf_Baai_Bge_Base_En_V1_5_Input =
- | {
- text: string | string[];
- /**
- * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
- */
- pooling?: "mean" | "cls";
- }
- | {
- /**
- * Batch of the embeddings requests to run using async-queue
- */
- requests: {
- text: string | string[];
- /**
- * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
- */
- pooling?: "mean" | "cls";
- }[];
- };
-type Ai_Cf_Baai_Bge_Base_En_V1_5_Output =
- | {
- shape?: number[];
- /**
- * Embeddings of the requested text values
- */
- data?: number[][];
- /**
- * The pooling method used in the embedding process.
- */
- pooling?: "mean" | "cls";
- }
- | Ai_Cf_Baai_Bge_Base_En_V1_5_AsyncResponse;
+type Ai_Cf_Baai_Bge_Base_En_V1_5_Input = {
+ text: string | string[];
+ /**
+ * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
+ */
+ pooling?: "mean" | "cls";
+} | {
+ /**
+ * Batch of the embeddings requests to run using async-queue
+ */
+ requests: {
+ text: string | string[];
+ /**
+ * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
+ */
+ pooling?: "mean" | "cls";
+ }[];
+};
+type Ai_Cf_Baai_Bge_Base_En_V1_5_Output = {
+ shape?: number[];
+ /**
+ * Embeddings of the requested text values
+ */
+ data?: number[][];
+ /**
+ * The pooling method used in the embedding process.
+ */
+ pooling?: "mean" | "cls";
+} | Ai_Cf_Baai_Bge_Base_En_V1_5_AsyncResponse;
interface Ai_Cf_Baai_Bge_Base_En_V1_5_AsyncResponse {
/**
* The async request id that can be used to obtain the results.
@@ -5606,14 +5580,12 @@ declare abstract class Base_Ai_Cf_Baai_Bge_Base_En_V1_5 {
inputs: Ai_Cf_Baai_Bge_Base_En_V1_5_Input;
postProcessedOutputs: Ai_Cf_Baai_Bge_Base_En_V1_5_Output;
}
-type Ai_Cf_Openai_Whisper_Input =
- | string
- | {
- /**
- * An array of integers that represent the audio data constrained to 8-bit unsigned integer values
- */
- audio: number[];
- };
+type Ai_Cf_Openai_Whisper_Input = string | {
+ /**
+ * An array of integers that represent the audio data constrained to 8-bit unsigned integer values
+ */
+ audio: number[];
+};
interface Ai_Cf_Openai_Whisper_Output {
/**
* The transcription
@@ -5637,48 +5609,44 @@ declare abstract class Base_Ai_Cf_Openai_Whisper {
inputs: Ai_Cf_Openai_Whisper_Input;
postProcessedOutputs: Ai_Cf_Openai_Whisper_Output;
}
-type Ai_Cf_Meta_M2M100_1_2B_Input =
- | {
- /**
- * The text to be translated
- */
- text: string;
- /**
- * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified
- */
- source_lang?: string;
- /**
- * The language code to translate the text into (e.g., 'es' for Spanish)
- */
- target_lang: string;
- }
- | {
- /**
- * Batch of the embeddings requests to run using async-queue
- */
- requests: {
- /**
- * The text to be translated
- */
- text: string;
- /**
- * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified
- */
- source_lang?: string;
- /**
- * The language code to translate the text into (e.g., 'es' for Spanish)
- */
- target_lang: string;
- }[];
- };
-type Ai_Cf_Meta_M2M100_1_2B_Output =
- | {
- /**
- * The translated text in the target language
- */
- translated_text?: string;
- }
- | Ai_Cf_Meta_M2M100_1_2B_AsyncResponse;
+type Ai_Cf_Meta_M2M100_1_2B_Input = {
+ /**
+ * The text to be translated
+ */
+ text: string;
+ /**
+ * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified
+ */
+ source_lang?: string;
+ /**
+ * The language code to translate the text into (e.g., 'es' for Spanish)
+ */
+ target_lang: string;
+} | {
+ /**
+ * Batch of the embeddings requests to run using async-queue
+ */
+ requests: {
+ /**
+ * The text to be translated
+ */
+ text: string;
+ /**
+ * The language code of the source text (e.g., 'en' for English). Defaults to 'en' if not specified
+ */
+ source_lang?: string;
+ /**
+ * The language code to translate the text into (e.g., 'es' for Spanish)
+ */
+ target_lang: string;
+ }[];
+};
+type Ai_Cf_Meta_M2M100_1_2B_Output = {
+ /**
+ * The translated text in the target language
+ */
+ translated_text?: string;
+} | Ai_Cf_Meta_M2M100_1_2B_AsyncResponse;
interface Ai_Cf_Meta_M2M100_1_2B_AsyncResponse {
/**
* The async request id that can be used to obtain the results.
@@ -5689,39 +5657,35 @@ declare abstract class Base_Ai_Cf_Meta_M2M100_1_2B {
inputs: Ai_Cf_Meta_M2M100_1_2B_Input;
postProcessedOutputs: Ai_Cf_Meta_M2M100_1_2B_Output;
}
-type Ai_Cf_Baai_Bge_Small_En_V1_5_Input =
- | {
- text: string | string[];
- /**
- * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
- */
- pooling?: "mean" | "cls";
- }
- | {
- /**
- * Batch of the embeddings requests to run using async-queue
- */
- requests: {
- text: string | string[];
- /**
- * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
- */
- pooling?: "mean" | "cls";
- }[];
- };
-type Ai_Cf_Baai_Bge_Small_En_V1_5_Output =
- | {
- shape?: number[];
- /**
- * Embeddings of the requested text values
- */
- data?: number[][];
- /**
- * The pooling method used in the embedding process.
- */
- pooling?: "mean" | "cls";
- }
- | Ai_Cf_Baai_Bge_Small_En_V1_5_AsyncResponse;
+type Ai_Cf_Baai_Bge_Small_En_V1_5_Input = {
+ text: string | string[];
+ /**
+ * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
+ */
+ pooling?: "mean" | "cls";
+} | {
+ /**
+ * Batch of the embeddings requests to run using async-queue
+ */
+ requests: {
+ text: string | string[];
+ /**
+ * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
+ */
+ pooling?: "mean" | "cls";
+ }[];
+};
+type Ai_Cf_Baai_Bge_Small_En_V1_5_Output = {
+ shape?: number[];
+ /**
+ * Embeddings of the requested text values
+ */
+ data?: number[][];
+ /**
+ * The pooling method used in the embedding process.
+ */
+ pooling?: "mean" | "cls";
+} | Ai_Cf_Baai_Bge_Small_En_V1_5_AsyncResponse;
interface Ai_Cf_Baai_Bge_Small_En_V1_5_AsyncResponse {
/**
* The async request id that can be used to obtain the results.
@@ -5732,39 +5696,35 @@ declare abstract class Base_Ai_Cf_Baai_Bge_Small_En_V1_5 {
inputs: Ai_Cf_Baai_Bge_Small_En_V1_5_Input;
postProcessedOutputs: Ai_Cf_Baai_Bge_Small_En_V1_5_Output;
}
-type Ai_Cf_Baai_Bge_Large_En_V1_5_Input =
- | {
- text: string | string[];
- /**
- * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
- */
- pooling?: "mean" | "cls";
- }
- | {
- /**
- * Batch of the embeddings requests to run using async-queue
- */
- requests: {
- text: string | string[];
- /**
- * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
- */
- pooling?: "mean" | "cls";
- }[];
- };
-type Ai_Cf_Baai_Bge_Large_En_V1_5_Output =
- | {
- shape?: number[];
- /**
- * Embeddings of the requested text values
- */
- data?: number[][];
- /**
- * The pooling method used in the embedding process.
- */
- pooling?: "mean" | "cls";
- }
- | Ai_Cf_Baai_Bge_Large_En_V1_5_AsyncResponse;
+type Ai_Cf_Baai_Bge_Large_En_V1_5_Input = {
+ text: string | string[];
+ /**
+ * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
+ */
+ pooling?: "mean" | "cls";
+} | {
+ /**
+ * Batch of the embeddings requests to run using async-queue
+ */
+ requests: {
+ text: string | string[];
+ /**
+ * The pooling method used in the embedding process. `cls` pooling will generate more accurate embeddings on larger inputs - however, embeddings created with cls pooling are not compatible with embeddings generated with mean pooling. The default pooling method is `mean` in order for this to not be a breaking change, but we highly suggest using the new `cls` pooling for better accuracy.
+ */
+ pooling?: "mean" | "cls";
+ }[];
+};
+type Ai_Cf_Baai_Bge_Large_En_V1_5_Output = {
+ shape?: number[];
+ /**
+ * Embeddings of the requested text values
+ */
+ data?: number[][];
+ /**
+ * The pooling method used in the embedding process.
+ */
+ pooling?: "mean" | "cls";
+} | Ai_Cf_Baai_Bge_Large_En_V1_5_AsyncResponse;
interface Ai_Cf_Baai_Bge_Large_En_V1_5_AsyncResponse {
/**
* The async request id that can be used to obtain the results.
@@ -5775,47 +5735,45 @@ declare abstract class Base_Ai_Cf_Baai_Bge_Large_En_V1_5 {
inputs: Ai_Cf_Baai_Bge_Large_En_V1_5_Input;
postProcessedOutputs: Ai_Cf_Baai_Bge_Large_En_V1_5_Output;
}
-type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input =
- | string
- | {
- /**
- * The input text prompt for the model to generate a response.
- */
- prompt?: string;
- /**
- * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
- */
- raw?: boolean;
- /**
- * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.
- */
- top_p?: number;
- /**
- * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.
- */
- top_k?: number;
- /**
- * Random seed for reproducibility of the generation.
- */
- seed?: number;
- /**
- * Penalty for repeated tokens; higher values discourage repetition.
- */
- repetition_penalty?: number;
- /**
- * Decreases the likelihood of the model repeating the same lines verbatim.
- */
- frequency_penalty?: number;
- /**
- * Increases the likelihood of the model introducing new topics.
- */
- presence_penalty?: number;
- image: number[] | (string & NonNullable);
- /**
- * The maximum number of tokens to generate in the response.
- */
- max_tokens?: number;
- };
+type Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input = string | {
+ /**
+ * The input text prompt for the model to generate a response.
+ */
+ prompt?: string;
+ /**
+ * If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
+ */
+ raw?: boolean;
+ /**
+ * Controls the creativity of the AI's responses by adjusting how many possible words it considers. Lower values make outputs more predictable; higher values allow for more varied and creative responses.
+ */
+ top_p?: number;
+ /**
+ * Limits the AI to choose from the top 'k' most probable words. Lower values make responses more focused; higher values introduce more variety and potential surprises.
+ */
+ top_k?: number;
+ /**
+ * Random seed for reproducibility of the generation.
+ */
+ seed?: number;
+ /**
+ * Penalty for repeated tokens; higher values discourage repetition.
+ */
+ repetition_penalty?: number;
+ /**
+ * Decreases the likelihood of the model repeating the same lines verbatim.
+ */
+ frequency_penalty?: number;
+ /**
+ * Increases the likelihood of the model introducing new topics.
+ */
+ presence_penalty?: number;
+ image: number[] | (string & NonNullable);
+ /**
+ * The maximum number of tokens to generate in the response.
+ */
+ max_tokens?: number;
+};
interface Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output {
description?: string;
}
@@ -5823,14 +5781,12 @@ declare abstract class Base_Ai_Cf_Unum_Uform_Gen2_Qwen_500M {
inputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Input;
postProcessedOutputs: Ai_Cf_Unum_Uform_Gen2_Qwen_500M_Output;
}
-type Ai_Cf_Openai_Whisper_Tiny_En_Input =
- | string
- | {
- /**
- * An array of integers that represent the audio data constrained to 8-bit unsigned integer values
- */
- audio: number[];
- };
+type Ai_Cf_Openai_Whisper_Tiny_En_Input = string | {
+ /**
+ * An array of integers that represent the audio data constrained to 8-bit unsigned integer values
+ */
+ audio: number[];
+};
interface Ai_Cf_Openai_Whisper_Tiny_En_Output {
/**
* The transcription
@@ -5855,12 +5811,10 @@ declare abstract class Base_Ai_Cf_Openai_Whisper_Tiny_En {
postProcessedOutputs: Ai_Cf_Openai_Whisper_Tiny_En_Output;
}
interface Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input {
- audio:
- | string
- | {
- body?: object;
- contentType?: string;
- };
+ audio: string | {
+ body?: object;
+ contentType?: string;
+ };
/**
* Supported tasks are 'translate' or 'transcribe'.
*/
@@ -5986,15 +5940,12 @@ declare abstract class Base_Ai_Cf_Openai_Whisper_Large_V3_Turbo {
inputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Input;
postProcessedOutputs: Ai_Cf_Openai_Whisper_Large_V3_Turbo_Output;
}
-type Ai_Cf_Baai_Bge_M3_Input =
- | Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts
- | Ai_Cf_Baai_Bge_M3_Input_Embedding
- | {
- /**
- * Batch of the embeddings requests to run using async-queue
- */
- requests: (Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts_1 | Ai_Cf_Baai_Bge_M3_Input_Embedding_1)[];
- };
+type Ai_Cf_Baai_Bge_M3_Input = Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts | Ai_Cf_Baai_Bge_M3_Input_Embedding | {
+ /**
+ * Batch of the embeddings requests to run using async-queue
+ */
+ requests: (Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts_1 | Ai_Cf_Baai_Bge_M3_Input_Embedding_1)[];
+};
interface Ai_Cf_Baai_Bge_M3_Input_QueryAnd_Contexts {
/**
* A query you wish to perform against the provided contexts. If no query is provided the model with respond with embeddings for contexts
@@ -6174,34 +6125,31 @@ interface Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Messages {
* The tool call id. If you don't know what to put here you can fall back to 000000001
*/
tool_call_id?: string;
- content?:
- | string
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- }[]
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- };
+ content?: string | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ }[] | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ };
}[];
image?: number[] | (string & NonNullable);
functions?: {
@@ -6211,93 +6159,90 @@ interface Ai_Cf_Meta_Llama_3_2_11B_Vision_Instruct_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
/**
* If true, the response will be streamed back incrementally.
*/
@@ -6423,18 +6368,16 @@ interface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Messages {
* The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').
*/
role: string;
- content:
- | string
- | {
- /**
- * Type of the content (text)
- */
- type?: string;
- /**
- * Text content
- */
- text?: string;
- }[];
+ content: string | {
+ /**
+ * Type of the content (text)
+ */
+ type?: string;
+ /**
+ * Text content
+ */
+ text?: string;
+ }[];
}[];
functions?: {
name: string;
@@ -6443,93 +6386,90 @@ interface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode_1;
/**
* If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
@@ -6625,45 +6565,42 @@ interface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_JSON_Mode_2 {
type?: "json_object" | "json_schema";
json_schema?: unknown;
}
-type Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Output =
- | {
- /**
- * The generated text response from the model
- */
- response: string;
- /**
- * Usage statistics for the inference request
- */
- usage?: {
- /**
- * Total number of tokens in input
- */
- prompt_tokens?: number;
- /**
- * Total number of tokens in output
- */
- completion_tokens?: number;
- /**
- * Total number of input and output tokens
- */
- total_tokens?: number;
- };
- /**
- * An array of tool calls requests made during the response generation
- */
- tool_calls?: {
- /**
- * The arguments passed to be passed to the tool call request
- */
- arguments?: object;
- /**
- * The name of the tool to be called
- */
- name?: string;
- }[];
- }
- | string
- | Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_AsyncResponse;
+type Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_Output = {
+ /**
+ * The generated text response from the model
+ */
+ response: string;
+ /**
+ * Usage statistics for the inference request
+ */
+ usage?: {
+ /**
+ * Total number of tokens in input
+ */
+ prompt_tokens?: number;
+ /**
+ * Total number of tokens in output
+ */
+ completion_tokens?: number;
+ /**
+ * Total number of input and output tokens
+ */
+ total_tokens?: number;
+ };
+ /**
+ * An array of tool calls requests made during the response generation
+ */
+ tool_calls?: {
+ /**
+ * The arguments passed to be passed to the tool call request
+ */
+ arguments?: object;
+ /**
+ * The name of the tool to be called
+ */
+ name?: string;
+ }[];
+} | string | Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_AsyncResponse;
interface Ai_Cf_Meta_Llama_3_3_70B_Instruct_Fp8_Fast_AsyncResponse {
/**
* The async request id that can be used to obtain the results.
@@ -6707,18 +6644,16 @@ interface Ai_Cf_Meta_Llama_Guard_3_8B_Input {
};
}
interface Ai_Cf_Meta_Llama_Guard_3_8B_Output {
- response?:
- | string
- | {
- /**
- * Whether the conversation is safe or not.
- */
- safe?: boolean;
- /**
- * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe.
- */
- categories?: string[];
- };
+ response?: string | {
+ /**
+ * Whether the conversation is safe or not.
+ */
+ safe?: boolean;
+ /**
+ * A list of what hazard categories predicted for the conversation, if the conversation is deemed unsafe.
+ */
+ categories?: string[];
+ };
/**
* Usage statistics for the inference request
*/
@@ -6852,93 +6787,90 @@ interface Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Qwen_Qwen2_5_Coder_32B_Instruct_JSON_Mode_1;
/**
* If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
@@ -7089,34 +7021,31 @@ interface Ai_Cf_Qwen_Qwq_32B_Messages {
* The tool call id. If you don't know what to put here you can fall back to 000000001
*/
tool_call_id?: string;
- content?:
- | string
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- }[]
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- };
+ content?: string | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ }[] | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ };
}[];
functions?: {
name: string;
@@ -7125,93 +7054,90 @@ interface Ai_Cf_Qwen_Qwq_32B_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
/**
* JSON schema that should be fufilled for the response.
*/
@@ -7361,34 +7287,31 @@ interface Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Messages {
* The tool call id. Must be supplied for tool calls for Mistral-3. If you don't know what to put here you can fall back to 000000001
*/
tool_call_id?: string;
- content?:
- | string
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- }[]
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- };
+ content?: string | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ }[] | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ };
}[];
functions?: {
name: string;
@@ -7397,93 +7320,90 @@ interface Ai_Cf_Mistralai_Mistral_Small_3_1_24B_Instruct_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
/**
* JSON schema that should be fufilled for the response.
*/
@@ -7629,21 +7549,19 @@ interface Ai_Cf_Google_Gemma_3_12B_It_Messages {
* The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').
*/
role?: string;
- content?:
- | string
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- }[];
+ content?: string | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ }[];
}[];
functions?: {
name: string;
@@ -7652,93 +7570,90 @@ interface Ai_Cf_Google_Gemma_3_12B_It_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
/**
* JSON schema that should be fufilled for the response.
*/
@@ -7893,34 +7808,31 @@ interface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages {
* The tool call id. If you don't know what to put here you can fall back to 000000001
*/
tool_call_id?: string;
- content?:
- | string
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- }[]
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- };
+ content?: string | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ }[] | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ };
}[];
functions?: {
name: string;
@@ -7929,93 +7841,90 @@ interface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_JSON_Mode;
/**
* JSON schema that should be fufilled for the response.
@@ -8129,34 +8038,31 @@ interface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages_Inner {
* The tool call id. If you don't know what to put here you can fall back to 000000001
*/
tool_call_id?: string;
- content?:
- | string
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- }[]
- | {
- /**
- * Type of the content provided
- */
- type?: string;
- text?: string;
- image_url?: {
- /**
- * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
- */
- url?: string;
- };
- };
+ content?: string | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ }[] | {
+ /**
+ * Type of the content provided
+ */
+ type?: string;
+ text?: string;
+ image_url?: {
+ /**
+ * image uri with data (e.g. data:image/jpeg;base64,/9j/...). HTTP URL will not be accepted
+ */
+ url?: string;
+ };
+ };
}[];
functions?: {
name: string;
@@ -8165,93 +8071,90 @@ interface Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_Messages_Inner {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Meta_Llama_4_Scout_17B_16E_Instruct_JSON_Mode;
/**
* JSON schema that should be fufilled for the response.
@@ -8416,18 +8319,16 @@ interface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages {
* The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').
*/
role: string;
- content:
- | string
- | {
- /**
- * Type of the content (text)
- */
- type?: string;
- /**
- * Text content
- */
- text?: string;
- }[];
+ content: string | {
+ /**
+ * Type of the content (text)
+ */
+ type?: string;
+ /**
+ * Text content
+ */
+ text?: string;
+ }[];
}[];
functions?: {
name: string;
@@ -8436,93 +8337,90 @@ interface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_1;
/**
* If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
@@ -8636,18 +8534,16 @@ interface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages_1 {
* The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').
*/
role: string;
- content:
- | string
- | {
- /**
- * Type of the content (text)
- */
- type?: string;
- /**
- * Text content
- */
- text?: string;
- }[];
+ content: string | {
+ /**
+ * Type of the content (text)
+ */
+ type?: string;
+ /**
+ * Text content
+ */
+ text?: string;
+ }[];
}[];
functions?: {
name: string;
@@ -8656,93 +8552,90 @@ interface Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_Messages_1 {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Qwen_Qwen3_30B_A3B_Fp8_JSON_Mode_3;
/**
* If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
@@ -9164,30 +9057,28 @@ declare abstract class Base_Ai_Cf_Qwen_Qwen3_Embedding_0_6B {
inputs: Ai_Cf_Qwen_Qwen3_Embedding_0_6B_Input;
postProcessedOutputs: Ai_Cf_Qwen_Qwen3_Embedding_0_6B_Output;
}
-type Ai_Cf_Pipecat_Ai_Smart_Turn_V2_Input =
- | {
- /**
- * readable stream with audio data and content-type specified for that data
- */
- audio: {
- body: object;
- contentType: string;
- };
- /**
- * type of data PCM data that's sent to the inference server as raw array
- */
- dtype?: "uint8" | "float32" | "float64";
- }
- | {
- /**
- * base64 encoded audio data
- */
- audio: string;
- /**
- * type of data PCM data that's sent to the inference server as raw array
- */
- dtype?: "uint8" | "float32" | "float64";
- };
+type Ai_Cf_Pipecat_Ai_Smart_Turn_V2_Input = {
+ /**
+ * readable stream with audio data and content-type specified for that data
+ */
+ audio: {
+ body: object;
+ contentType: string;
+ };
+ /**
+ * type of data PCM data that's sent to the inference server as raw array
+ */
+ dtype?: "uint8" | "float32" | "float64";
+} | {
+ /**
+ * base64 encoded audio data
+ */
+ audio: string;
+ /**
+ * type of data PCM data that's sent to the inference server as raw array
+ */
+ dtype?: "uint8" | "float32" | "float64";
+};
interface Ai_Cf_Pipecat_Ai_Smart_Turn_V2_Output {
/**
* if true, end-of-turn was detected
@@ -9330,41 +9221,7 @@ interface Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B_Input {
/**
* Target langauge to translate to
*/
- target_language:
- | "asm_Beng"
- | "awa_Deva"
- | "ben_Beng"
- | "bho_Deva"
- | "brx_Deva"
- | "doi_Deva"
- | "eng_Latn"
- | "gom_Deva"
- | "gon_Deva"
- | "guj_Gujr"
- | "hin_Deva"
- | "hne_Deva"
- | "kan_Knda"
- | "kas_Arab"
- | "kas_Deva"
- | "kha_Latn"
- | "lus_Latn"
- | "mag_Deva"
- | "mai_Deva"
- | "mal_Mlym"
- | "mar_Deva"
- | "mni_Beng"
- | "mni_Mtei"
- | "npi_Deva"
- | "ory_Orya"
- | "pan_Guru"
- | "san_Deva"
- | "sat_Olck"
- | "snd_Arab"
- | "snd_Deva"
- | "tam_Taml"
- | "tel_Telu"
- | "urd_Arab"
- | "unr_Deva";
+ target_language: "asm_Beng" | "awa_Deva" | "ben_Beng" | "bho_Deva" | "brx_Deva" | "doi_Deva" | "eng_Latn" | "gom_Deva" | "gon_Deva" | "guj_Gujr" | "hin_Deva" | "hne_Deva" | "kan_Knda" | "kas_Arab" | "kas_Deva" | "kha_Latn" | "lus_Latn" | "mag_Deva" | "mai_Deva" | "mal_Mlym" | "mar_Deva" | "mni_Beng" | "mni_Mtei" | "npi_Deva" | "ory_Orya" | "pan_Guru" | "san_Deva" | "sat_Olck" | "snd_Arab" | "snd_Deva" | "tam_Taml" | "tel_Telu" | "urd_Arab" | "unr_Deva";
}
interface Ai_Cf_Ai4Bharat_Indictrans2_En_Indic_1B_Output {
/**
@@ -9441,18 +9298,16 @@ interface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages {
* The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').
*/
role: string;
- content:
- | string
- | {
- /**
- * Type of the content (text)
- */
- type?: string;
- /**
- * Text content
- */
- text?: string;
- }[];
+ content: string | {
+ /**
+ * Type of the content (text)
+ */
+ type?: string;
+ /**
+ * Text content
+ */
+ text?: string;
+ }[];
}[];
functions?: {
name: string;
@@ -9461,93 +9316,90 @@ interface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_1;
/**
* If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
@@ -9661,18 +9513,16 @@ interface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages_1 {
* The role of the message sender (e.g., 'user', 'assistant', 'system', 'tool').
*/
role: string;
- content:
- | string
- | {
- /**
- * Type of the content (text)
- */
- type?: string;
- /**
- * Text content
- */
- text?: string;
- }[];
+ content: string | {
+ /**
+ * Type of the content (text)
+ */
+ type?: string;
+ /**
+ * Text content
+ */
+ text?: string;
+ }[];
}[];
functions?: {
name: string;
@@ -9681,93 +9531,90 @@ interface Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_Messages_1 {
/**
* A list of tools available for the assistant to use.
*/
- tools?: (
- | {
- /**
- * The name of the tool. More descriptive the better.
- */
- name: string;
- /**
- * A brief description of what the tool does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the tool.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- }
- | {
- /**
- * Specifies the type of tool (e.g., 'function').
- */
- type: string;
- /**
- * Details of the function tool.
- */
- function: {
- /**
- * The name of the function.
- */
- name: string;
- /**
- * A brief description of what the function does.
- */
- description: string;
- /**
- * Schema defining the parameters accepted by the function.
- */
- parameters: {
- /**
- * The type of the parameters object (usually 'object').
- */
- type: string;
- /**
- * List of required parameter names.
- */
- required?: string[];
- /**
- * Definitions of each parameter.
- */
- properties: {
- [k: string]: {
- /**
- * The data type of the parameter.
- */
- type: string;
- /**
- * A description of the expected parameter.
- */
- description: string;
- };
- };
- };
- };
- }
- )[];
+ tools?: ({
+ /**
+ * The name of the tool. More descriptive the better.
+ */
+ name: string;
+ /**
+ * A brief description of what the tool does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the tool.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ } | {
+ /**
+ * Specifies the type of tool (e.g., 'function').
+ */
+ type: string;
+ /**
+ * Details of the function tool.
+ */
+ function: {
+ /**
+ * The name of the function.
+ */
+ name: string;
+ /**
+ * A brief description of what the function does.
+ */
+ description: string;
+ /**
+ * Schema defining the parameters accepted by the function.
+ */
+ parameters: {
+ /**
+ * The type of the parameters object (usually 'object').
+ */
+ type: string;
+ /**
+ * List of required parameter names.
+ */
+ required?: string[];
+ /**
+ * Definitions of each parameter.
+ */
+ properties: {
+ [k: string]: {
+ /**
+ * The data type of the parameter.
+ */
+ type: string;
+ /**
+ * A description of the expected parameter.
+ */
+ description: string;
+ };
+ };
+ };
+ };
+ })[];
response_format?: Ai_Cf_Aisingapore_Gemma_Sea_Lion_V4_27B_It_JSON_Mode_3;
/**
* If true, a chat template is not applied and you must adhere to the specific model's expected formatting.
@@ -10006,7 +9853,10 @@ interface Ai_Cf_Pfnet_Plamo_Embedding_1B_Output {
* @minItems 2
* @maxItems 2
*/
- shape: [number, number];
+ shape: [
+ number,
+ number
+ ];
}
declare abstract class Base_Ai_Cf_Pfnet_Plamo_Embedding_1B {
inputs: Ai_Cf_Pfnet_Plamo_Embedding_1B_Input;
@@ -10104,47 +9954,7 @@ interface Ai_Cf_Deepgram_Aura_2_En_Input {
/**
* Speaker used to produce the audio.
*/
- speaker?:
- | "amalthea"
- | "andromeda"
- | "apollo"
- | "arcas"
- | "aries"
- | "asteria"
- | "athena"
- | "atlas"
- | "aurora"
- | "callista"
- | "cora"
- | "cordelia"
- | "delia"
- | "draco"
- | "electra"
- | "harmonia"
- | "helena"
- | "hera"
- | "hermes"
- | "hyperion"
- | "iris"
- | "janus"
- | "juno"
- | "jupiter"
- | "luna"
- | "mars"
- | "minerva"
- | "neptune"
- | "odysseus"
- | "ophelia"
- | "orion"
- | "orpheus"
- | "pandora"
- | "phoebe"
- | "pluto"
- | "saturn"
- | "thalia"
- | "theia"
- | "vesta"
- | "zeus";
+ speaker?: "amalthea" | "andromeda" | "apollo" | "arcas" | "aries" | "asteria" | "athena" | "atlas" | "aurora" | "callista" | "cora" | "cordelia" | "delia" | "draco" | "electra" | "harmonia" | "helena" | "hera" | "hermes" | "hyperion" | "iris" | "janus" | "juno" | "jupiter" | "luna" | "mars" | "minerva" | "neptune" | "odysseus" | "ophelia" | "orion" | "orpheus" | "pandora" | "phoebe" | "pluto" | "saturn" | "thalia" | "theia" | "vesta" | "zeus";
/**
* Encoding of the output audio.
*/
@@ -10422,8 +10232,10 @@ type AiModelsSearchObject = {
};
type ChatCompletionsBase = ChatCompletionsMessagesInput;
type ChatCompletionsInput = ChatCompletionsMessagesInput;
-interface InferenceUpstreamError extends Error {}
-interface AiInternalError extends Error {}
+interface InferenceUpstreamError extends Error {
+}
+interface AiInternalError extends Error {
+}
type AiModelListType = Record;
type AiAsyncBatchResponse = {
request_id: string;
@@ -10445,41 +10257,25 @@ declare abstract class Ai {
*/
autorag(autoragId: string): AutoRAG;
// Batch request
- run(
- model: Name,
- inputs: {
- requests: AiModelList[Name]["inputs"][];
- },
- options: AiOptions & {
- queueRequest: true;
- },
- ): Promise;
+ run(model: Name, inputs: {
+ requests: AiModelList[Name]['inputs'][];
+ }, options: AiOptions & {
+ queueRequest: true;
+ }): Promise;
// Raw response
- run(
- model: Name,
- inputs: AiModelList[Name]["inputs"],
- options: AiOptions & {
- returnRawResponse: true;
- },
- ): Promise;
+ run(model: Name, inputs: AiModelList[Name]['inputs'], options: AiOptions & {
+ returnRawResponse: true;
+ }): Promise;
// WebSocket
- run(
- model: Name,
- inputs: AiModelList[Name]["inputs"],
- options: AiOptions & {
- websocket: true;
- },
- ): Promise;
+ run(model: Name, inputs: AiModelList[Name]['inputs'], options: AiOptions & {
+ websocket: true;
+ }): Promise;
// Streaming
- run(
- model: Name,
- inputs: AiModelList[Name]["inputs"] & {
- stream: true;
- },
- options?: AiOptions,
- ): Promise;
+ run(model: Name, inputs: AiModelList[Name]['inputs'] & {
+ stream: true;
+ }, options?: AiOptions): Promise;
// Normal (default) - known model
- run(model: Name, inputs: AiModelList[Name]["inputs"], options?: AiOptions): Promise;
+ run(model: Name, inputs: AiModelList[Name]['inputs'], options?: AiOptions): Promise;
// Unknown model (fallback).
//
// The `Exclude<..., keyof AiModelList>` constraint forces TypeScript to
@@ -10497,7 +10293,7 @@ declare abstract class Ai {
type GatewayRetries = {
maxAttempts?: 1 | 2 | 3 | 4 | 5;
retryDelayMs?: number;
- backoff?: "constant" | "linear" | "exponential";
+ backoff?: 'constant' | 'linear' | 'exponential';
};
type GatewayOptions = {
id: string;
@@ -10510,7 +10306,7 @@ type GatewayOptions = {
requestTimeoutMs?: number;
retries?: GatewayRetries;
};
-type UniversalGatewayOptions = Exclude & {
+type UniversalGatewayOptions = Exclude & {
/**
** @deprecated
*/
@@ -10548,29 +10344,26 @@ type AiGatewayLog = {
response_head_complete: boolean;
created_at: Date;
};
-type AIGatewayProviders = "workers-ai" | "anthropic" | "aws-bedrock" | "azure-openai" | "google-vertex-ai" | "huggingface" | "openai" | "perplexity-ai" | "replicate" | "groq" | "cohere" | "google-ai-studio" | "mistral" | "grok" | "openrouter" | "deepseek" | "cerebras" | "cartesia" | "elevenlabs" | "adobe-firefly";
+type AIGatewayProviders = 'workers-ai' | 'anthropic' | 'aws-bedrock' | 'azure-openai' | 'google-vertex-ai' | 'huggingface' | 'openai' | 'perplexity-ai' | 'replicate' | 'groq' | 'cohere' | 'google-ai-studio' | 'mistral' | 'grok' | 'openrouter' | 'deepseek' | 'cerebras' | 'cartesia' | 'elevenlabs' | 'adobe-firefly';
type AIGatewayHeaders = {
- "cf-aig-metadata": Record | string;
- "cf-aig-custom-cost":
- | {
- per_token_in?: number;
- per_token_out?: number;
- }
- | {
- total_cost?: number;
- }
- | string;
- "cf-aig-cache-ttl": number | string;
- "cf-aig-skip-cache": boolean | string;
- "cf-aig-cache-key": string;
- "cf-aig-event-id": string;
- "cf-aig-request-timeout": number | string;
- "cf-aig-max-attempts": number | string;
- "cf-aig-retry-delay": number | string;
- "cf-aig-backoff": string;
- "cf-aig-collect-log": boolean | string;
+ 'cf-aig-metadata': Record | string;
+ 'cf-aig-custom-cost': {
+ per_token_in?: number;
+ per_token_out?: number;
+ } | {
+ total_cost?: number;
+ } | string;
+ 'cf-aig-cache-ttl': number | string;
+ 'cf-aig-skip-cache': boolean | string;
+ 'cf-aig-cache-key': string;
+ 'cf-aig-event-id': string;
+ 'cf-aig-request-timeout': number | string;
+ 'cf-aig-max-attempts': number | string;
+ 'cf-aig-retry-delay': number | string;
+ 'cf-aig-backoff': string;
+ 'cf-aig-collect-log': boolean | string;
Authorization: string;
- "Content-Type": string;
+ 'Content-Type': string;
[key: string]: string | number | boolean | object;
};
type AIGatewayUniversalRequest = {
@@ -10579,19 +10372,18 @@ type AIGatewayUniversalRequest = {
headers: Partial;
query: unknown;
};
-interface AiGatewayInternalError extends Error {}
-interface AiGatewayLogNotFound extends Error {}
+interface AiGatewayInternalError extends Error {
+}
+interface AiGatewayLogNotFound extends Error {
+}
declare abstract class AiGateway {
patchLog(logId: string, data: AiGatewayPatchLog): Promise;
getLog(logId: string): Promise;
- run(
- data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[],
- options?: {
- gateway?: UniversalGatewayOptions;
- extraHeaders?: object;
- signal?: AbortSignal;
- },
- ): Promise;
+ run(data: AIGatewayUniversalRequest | AIGatewayUniversalRequest[], options?: {
+ gateway?: UniversalGatewayOptions;
+ extraHeaders?: object;
+ signal?: AbortSignal;
+ }): Promise;
getUrl(provider?: AIGatewayProviders | string): Promise; // eslint-disable-line
}
// Copyright (c) 2022-2025 Cloudflare, Inc.
@@ -10646,7 +10438,7 @@ interface ArtifactsCreateRepoResult {
/** Paginated list of repositories. */
interface ArtifactsRepoListResult {
/** Repositories in this page (without the `remote` field). */
- repos: Omit[];
+ repos: Omit[];
/** Total number of repositories in the namespace. */
total: number;
/** Cursor for the next page, if there are more results. */
@@ -10659,7 +10451,7 @@ interface ArtifactsCreateTokenResult {
/** Plaintext token (only returned at creation time). */
plaintext: string;
/** Token scope: "read" or "write". */
- scope: "read" | "write";
+ scope: 'read' | 'write';
/** ISO 8601 token expiry timestamp. */
expiresAt: string;
}
@@ -10668,9 +10460,9 @@ interface ArtifactsTokenInfo {
/** Unique token ID. */
id: string;
/** Token scope: "read" or "write". */
- scope: "read" | "write";
+ scope: 'read' | 'write';
/** Token state: "active", "expired", or "revoked". */
- state: "active" | "expired" | "revoked";
+ state: 'active' | 'expired' | 'revoked';
/** ISO 8601 creation timestamp. */
createdAt: string;
/** ISO 8601 expiry timestamp. */
@@ -10695,7 +10487,7 @@ interface ArtifactsRepo extends ArtifactsRepoInfo {
* @param ttl Time-to-live in seconds (default 86400, min 60, max 31536000).
* @throws {ArtifactsError} with code `INVALID_TTL` if ttl is out of range.
*/
- createToken(scope?: "write" | "read", ttl?: number): Promise;
+ createToken(scope?: 'write' | 'read', ttl?: number): Promise;
/** List tokens for this repo (metadata only, no plaintext). */
listTokens(): Promise;
/**
@@ -10714,14 +10506,11 @@ interface ArtifactsRepo extends ArtifactsRepoInfo {
* @throws {ArtifactsError} with code `ALREADY_EXISTS` if the target repo already exists.
* @throws {ArtifactsError} with code `FORK_IN_PROGRESS` if a fork is already running.
*/
- fork(
- name: string,
- opts?: {
- description?: string;
- readOnly?: boolean;
- defaultBranchOnly?: boolean;
- },
- ): Promise;
+ fork(name: string, opts?: {
+ description?: string;
+ readOnly?: boolean;
+ defaultBranchOnly?: boolean;
+ }): Promise