alchemy 0.84.0 → 0.85.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/bin/alchemy.js +32 -23
  2. package/lib/cloudflare/account-api-token.d.ts +5 -0
  3. package/lib/cloudflare/account-api-token.d.ts.map +1 -1
  4. package/lib/cloudflare/account-api-token.js +13 -2
  5. package/lib/cloudflare/account-api-token.js.map +1 -1
  6. package/lib/cloudflare/ai-crawler.d.ts +27 -0
  7. package/lib/cloudflare/ai-crawler.d.ts.map +1 -0
  8. package/lib/cloudflare/ai-crawler.js +75 -0
  9. package/lib/cloudflare/ai-crawler.js.map +1 -0
  10. package/lib/cloudflare/ai-search-token.d.ts +150 -0
  11. package/lib/cloudflare/ai-search-token.d.ts.map +1 -0
  12. package/lib/cloudflare/ai-search-token.js +171 -0
  13. package/lib/cloudflare/ai-search-token.js.map +1 -0
  14. package/lib/cloudflare/ai-search.d.ts +397 -0
  15. package/lib/cloudflare/ai-search.d.ts.map +1 -0
  16. package/lib/cloudflare/ai-search.js +307 -0
  17. package/lib/cloudflare/ai-search.js.map +1 -0
  18. package/lib/cloudflare/api-error.d.ts +11 -0
  19. package/lib/cloudflare/api-error.d.ts.map +1 -1
  20. package/lib/cloudflare/api-error.js +11 -0
  21. package/lib/cloudflare/api-error.js.map +1 -1
  22. package/lib/cloudflare/api.js +1 -1
  23. package/lib/cloudflare/api.js.map +1 -1
  24. package/lib/cloudflare/auth.d.ts +3 -2
  25. package/lib/cloudflare/auth.d.ts.map +1 -1
  26. package/lib/cloudflare/auth.js +17 -14
  27. package/lib/cloudflare/auth.js.map +1 -1
  28. package/lib/cloudflare/bindings.d.ts +12 -2
  29. package/lib/cloudflare/bindings.d.ts.map +1 -1
  30. package/lib/cloudflare/bindings.js.map +1 -1
  31. package/lib/cloudflare/bound.d.ts +2 -1
  32. package/lib/cloudflare/bound.d.ts.map +1 -1
  33. package/lib/cloudflare/bucket-custom-domain.d.ts.map +1 -1
  34. package/lib/cloudflare/bucket-custom-domain.js +4 -2
  35. package/lib/cloudflare/bucket-custom-domain.js.map +1 -1
  36. package/lib/cloudflare/bucket.d.ts.map +1 -1
  37. package/lib/cloudflare/bucket.js +8 -2
  38. package/lib/cloudflare/bucket.js.map +1 -1
  39. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  40. package/lib/cloudflare/d1-database.js +9 -1
  41. package/lib/cloudflare/d1-database.js.map +1 -1
  42. package/lib/cloudflare/index.d.ts +4 -0
  43. package/lib/cloudflare/index.d.ts.map +1 -1
  44. package/lib/cloudflare/index.js +4 -0
  45. package/lib/cloudflare/index.js.map +1 -1
  46. package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
  47. package/lib/cloudflare/miniflare/build-worker-options.js +15 -0
  48. package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
  49. package/lib/cloudflare/miniflare/miniflare-controller.d.ts +1 -1
  50. package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
  51. package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
  52. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +41 -3
  53. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
  54. package/lib/cloudflare/permission-groups.d.ts +5 -0
  55. package/lib/cloudflare/permission-groups.d.ts.map +1 -1
  56. package/lib/cloudflare/permission-groups.js +6 -0
  57. package/lib/cloudflare/permission-groups.js.map +1 -1
  58. package/lib/cloudflare/vpc-service.d.ts +288 -0
  59. package/lib/cloudflare/vpc-service.d.ts.map +1 -0
  60. package/lib/cloudflare/vpc-service.js +238 -0
  61. package/lib/cloudflare/vpc-service.js.map +1 -0
  62. package/lib/cloudflare/worker-metadata.d.ts +1 -0
  63. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  64. package/lib/cloudflare/worker-metadata.js +8 -0
  65. package/lib/cloudflare/worker-metadata.js.map +1 -1
  66. package/lib/cloudflare/worker.d.ts +9 -0
  67. package/lib/cloudflare/worker.d.ts.map +1 -1
  68. package/lib/cloudflare/worker.js.map +1 -1
  69. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  70. package/lib/cloudflare/wrangler.json.js +9 -0
  71. package/lib/cloudflare/wrangler.json.js.map +1 -1
  72. package/lib/planetscale/api/sdk.gen.d.ts +874 -190
  73. package/lib/planetscale/api/sdk.gen.d.ts.map +1 -1
  74. package/lib/planetscale/api/sdk.gen.js +1250 -306
  75. package/lib/planetscale/api/sdk.gen.js.map +1 -1
  76. package/lib/planetscale/api/types.gen.d.ts +8241 -4128
  77. package/lib/planetscale/api/types.gen.d.ts.map +1 -1
  78. package/lib/planetscale/branch.js +4 -4
  79. package/lib/planetscale/branch.js.map +1 -1
  80. package/lib/planetscale/database.d.ts +43 -24
  81. package/lib/planetscale/database.d.ts.map +1 -1
  82. package/lib/planetscale/database.js +39 -18
  83. package/lib/planetscale/database.js.map +1 -1
  84. package/lib/planetscale/default-role.d.ts +51 -0
  85. package/lib/planetscale/default-role.d.ts.map +1 -0
  86. package/lib/planetscale/default-role.js +100 -0
  87. package/lib/planetscale/default-role.js.map +1 -0
  88. package/lib/planetscale/index.d.ts +1 -0
  89. package/lib/planetscale/index.d.ts.map +1 -1
  90. package/lib/planetscale/index.js +1 -0
  91. package/lib/planetscale/index.js.map +1 -1
  92. package/lib/planetscale/organization.js +1 -1
  93. package/lib/planetscale/organization.js.map +1 -1
  94. package/lib/planetscale/role.d.ts +1 -1
  95. package/lib/planetscale/role.d.ts.map +1 -1
  96. package/lib/planetscale/role.js +1 -1
  97. package/lib/planetscale/role.js.map +1 -1
  98. package/lib/planetscale/utils.d.ts +1 -1
  99. package/lib/planetscale/utils.d.ts.map +1 -1
  100. package/lib/planetscale/utils.js +9 -10
  101. package/lib/planetscale/utils.js.map +1 -1
  102. package/lib/scope.d.ts +1 -1
  103. package/lib/scope.d.ts.map +1 -1
  104. package/lib/scope.js +10 -5
  105. package/lib/scope.js.map +1 -1
  106. package/lib/state/sqlite-state-store.js +2 -0
  107. package/lib/state/sqlite-state-store.js.map +1 -1
  108. package/lib/test/bun.d.ts +17 -0
  109. package/lib/test/bun.d.ts.map +1 -1
  110. package/lib/test/bun.js +1 -0
  111. package/lib/test/bun.js.map +1 -1
  112. package/lib/test/vitest.d.ts +17 -0
  113. package/lib/test/vitest.d.ts.map +1 -1
  114. package/lib/test/vitest.js +3 -0
  115. package/lib/test/vitest.js.map +1 -1
  116. package/lib/util/snake-to-camel.d.ts +21 -0
  117. package/lib/util/snake-to-camel.d.ts.map +1 -0
  118. package/lib/util/snake-to-camel.js +34 -0
  119. package/lib/util/snake-to-camel.js.map +1 -0
  120. package/package.json +2 -2
  121. package/src/cloudflare/account-api-token.ts +23 -6
  122. package/src/cloudflare/ai-crawler.ts +89 -0
  123. package/src/cloudflare/ai-search-token.ts +324 -0
  124. package/src/cloudflare/ai-search.ts +952 -0
  125. package/src/cloudflare/api-error.ts +19 -0
  126. package/src/cloudflare/api.ts +1 -1
  127. package/src/cloudflare/auth.ts +17 -14
  128. package/src/cloudflare/bindings.ts +15 -2
  129. package/src/cloudflare/bound.ts +6 -3
  130. package/src/cloudflare/bucket-custom-domain.ts +4 -1
  131. package/src/cloudflare/bucket.ts +9 -2
  132. package/src/cloudflare/d1-database.ts +11 -4
  133. package/src/cloudflare/index.ts +4 -0
  134. package/src/cloudflare/miniflare/build-worker-options.ts +30 -18
  135. package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +47 -2
  136. package/src/cloudflare/permission-groups.ts +6 -0
  137. package/src/cloudflare/vpc-service.ts +466 -0
  138. package/src/cloudflare/worker-metadata.ts +8 -0
  139. package/src/cloudflare/worker.ts +9 -0
  140. package/src/cloudflare/wrangler.json.ts +8 -0
  141. package/src/planetscale/api/sdk.gen.ts +2331 -987
  142. package/src/planetscale/api/types.gen.ts +8980 -4574
  143. package/src/planetscale/branch.ts +4 -4
  144. package/src/planetscale/database.ts +87 -46
  145. package/src/planetscale/default-role.ts +162 -0
  146. package/src/planetscale/index.ts +1 -0
  147. package/src/planetscale/organization.ts +1 -1
  148. package/src/planetscale/role.ts +3 -2
  149. package/src/planetscale/utils.ts +10 -10
  150. package/src/scope.ts +14 -5
  151. package/src/state/sqlite-state-store.ts +2 -0
  152. package/src/test/bun.ts +25 -0
  153. package/src/test/vitest.ts +33 -0
  154. package/src/util/snake-to-camel.ts +59 -0
  155. package/workers/tunnel-proxy.js +1 -1
@@ -1,3 +1,5 @@
1
+ import type { CloudflareApiErrorPayload } from "./api-response";
2
+
1
3
  /**
2
4
  * Custom error class for Cloudflare API errors
3
5
  * Includes HTTP status information from the Response
@@ -66,3 +68,20 @@ export async function handleApiError(
66
68
  }: ${errors.map((error) => `${error.code}: ${error.message}`).join(", ")}`;
67
69
  throw new CloudflareApiError(errorMessage, response, errors);
68
70
  }
71
+
72
+ /**
73
+ * Helper function to check if an error is a Cloudflare API error.
74
+ * Optional match criteria can be provided to check for a specific HTTP status code or error code.
75
+ */
76
+ export function isCloudflareApiError(
77
+ error: unknown,
78
+ match: { status?: number; code?: number } = {},
79
+ ): error is CloudflareApiError & { errorData: CloudflareApiErrorPayload[] } {
80
+ return (
81
+ error instanceof CloudflareApiError &&
82
+ (match.status === undefined || error.status === match.status) &&
83
+ (match.code === undefined ||
84
+ (Array.isArray(error.errorData) &&
85
+ error.errorData.some((e) => "code" in e && e.code === match.code)))
86
+ );
87
+ }
@@ -113,7 +113,7 @@ export const createCloudflareApi = memoize(
113
113
  baseUrl,
114
114
  profile,
115
115
  credentials,
116
- accountId: provider.metadata.id,
116
+ accountId: accountId ?? provider.metadata.id,
117
117
  });
118
118
  } catch (error) {
119
119
  throw new Error(
@@ -75,8 +75,9 @@ export namespace CloudflareAuth {
75
75
  "queues:write": "See and change Cloudflare Queues settings and data",
76
76
  "r2_catalog:write": "Grants write level access to R2 Data Catalog",
77
77
  "radar:read": "Grants access to read Cloudflare Radar data",
78
- "rag:read": "Grants read level access to Auto Rag",
79
- "rag:write": "Grants write level access to Auto Rag",
78
+ "ai-search:read": "Grants read level access to AI Search",
79
+ "ai-search:write": "Grants write level access to AI Search",
80
+ "ai-search:run": "Grants run level access to AI Search",
80
81
  "secrets_store:read": "Grants read level access to Secrets Store",
81
82
  "secrets_store:write": "Grants write level access to Secrets Store",
82
83
  "ssl_certs:write":
@@ -120,24 +121,26 @@ export namespace CloudflareAuth {
120
121
  export type Scope = keyof typeof ALL_SCOPES;
121
122
  export const DEFAULT_SCOPES = [
122
123
  "account:read",
124
+ "ai-search:write",
125
+ "ai-search:run",
126
+ "ai:write",
127
+ "cloudchamber:write",
128
+ "connectivity:admin",
129
+ "containers:write",
130
+ "d1:write",
131
+ "pages:write",
132
+ "pipelines:write",
133
+ "queues:write",
134
+ "secrets_store:write",
135
+ "ssl_certs:write",
123
136
  "user:read",
124
- "workers:write",
137
+ "vectorize:write",
125
138
  "workers_kv:write",
126
139
  "workers_routes:write",
127
140
  "workers_scripts:write",
128
141
  "workers_tail:read",
129
- "d1:write",
130
- "pages:write",
142
+ "workers:write",
131
143
  "zone:read",
132
- "ssl_certs:write",
133
- "ai:write",
134
- "queues:write",
135
- "pipelines:write",
136
- "secrets_store:write",
137
- "containers:write",
138
- "cloudchamber:write",
139
- "vectorize:write",
140
- "connectivity:admin",
141
144
  ];
142
145
 
143
146
  /**
@@ -26,6 +26,7 @@ import type { SecretRef as CloudflareSecretRef } from "./secret-ref.ts";
26
26
  import type { Secret as CloudflareSecret } from "./secret.ts";
27
27
  import type { VectorizeIndex } from "./vectorize-index.ts";
28
28
  import type { VersionMetadata } from "./version-metadata.ts";
29
+ import type { VpcService } from "./vpc-service.ts";
29
30
  import type { WorkerLoader } from "./worker-loader.ts";
30
31
  import type { WorkerRef } from "./worker-ref.ts";
31
32
  import type { WorkerStub } from "./worker-stub.ts";
@@ -82,7 +83,8 @@ export type Binding =
82
83
  | BrowserRendering
83
84
  | VersionMetadata
84
85
  | Self
85
- | Json;
86
+ | Json
87
+ | VpcService;
86
88
 
87
89
  export type Self<
88
90
  RPC extends Rpc.WorkerEntrypointBranded = Rpc.WorkerEntrypointBranded,
@@ -143,7 +145,8 @@ export type WorkerBindingSpec =
143
145
  | WorkerBindingVersionMetadata
144
146
  | WorkerBindingWasmModule
145
147
  | WorkerBindingWorkerLoader
146
- | WorkerBindingWorkflow;
148
+ | WorkerBindingWorkflow
149
+ | WorkerBindingVpcService;
147
150
 
148
151
  /**
149
152
  * AI binding type
@@ -499,6 +502,16 @@ export interface WorkerBindingWorkflow {
499
502
  script_name?: string;
500
503
  }
501
504
 
505
+ export interface WorkerBindingVpcService {
506
+ /** The name of the binding */
507
+ name: string;
508
+ /** Type identifier for VPC Service binding */
509
+ type: "vpc_service";
510
+ /** VPC Service name */
511
+ service_name: string;
512
+ service_id: string;
513
+ }
514
+
502
515
  /**
503
516
  * Images binding type
504
517
  */
@@ -20,6 +20,7 @@ import type { SecretRef as CloudflareSecretRef } from "./secret-ref.ts";
20
20
  import type { Secret as CloudflareSecret } from "./secret.ts";
21
21
  import type { VectorizeIndex as _VectorizeIndex } from "./vectorize-index.ts";
22
22
  import type { VersionMetadata as _VersionMetadata } from "./version-metadata.ts";
23
+ import type { VpcService as _VpcService } from "./vpc-service.ts";
23
24
  import type { WorkerLoader as _WorkerLoader } from "./worker-loader.ts";
24
25
  import type { WorkerRef } from "./worker-ref.ts";
25
26
  import type { WorkerStub } from "./worker-stub.ts";
@@ -105,6 +106,8 @@ export type Bound<T extends Binding> =
105
106
  Obj &
106
107
  Rpc.DurableObjectBranded
107
108
  >
108
- : T extends undefined
109
- ? undefined
110
- : Service;
109
+ : T extends _VpcService
110
+ ? Fetcher
111
+ : T extends undefined
112
+ ? undefined
113
+ : Service;
@@ -100,6 +100,8 @@ export type R2BucketCustomDomain = {
100
100
  minTLS?: "1.0" | "1.1" | "1.2" | "1.3" | (string & {});
101
101
  };
102
102
 
103
+ const DEV_NOOP_ZONE = "noop-zone";
104
+
103
105
  export const R2BucketCustomDomain = Resource(
104
106
  "cloudflare::R2BucketCustomDomain",
105
107
  async function (
@@ -114,7 +116,7 @@ export const R2BucketCustomDomain = Resource(
114
116
  zoneId:
115
117
  typeof props.zone === "string"
116
118
  ? props.zone
117
- : (props.zone?.id ?? "noop-zone"),
119
+ : (props.zone?.id ?? DEV_NOOP_ZONE),
118
120
  ciphers: props.ciphers,
119
121
  minTLS: props.minTLS,
120
122
  };
@@ -158,6 +160,7 @@ export const R2BucketCustomDomain = Resource(
158
160
  }
159
161
  case "update": {
160
162
  if (
163
+ this.output.zoneId !== DEV_NOOP_ZONE && // dev mode placeholder should not trigger replacement
161
164
  diff(this.output, payload).some(
162
165
  (property) => property === "domain" || property === "zoneId",
163
166
  )
@@ -689,6 +689,13 @@ const _R2Bucket = Resource(
689
689
 
690
690
  if (this.phase === "delete") {
691
691
  if (props.delete !== false) {
692
+ // Use bucket's actual jurisdiction from output, falling back to props
693
+ // This ensures EU/FedRAMP buckets are correctly addressed during cleanup
694
+ const deleteProps: BucketProps = {
695
+ ...props,
696
+ jurisdiction: this.output?.jurisdiction ?? props.jurisdiction,
697
+ };
698
+
692
699
  if (this.output?.catalog) {
693
700
  await disableDataCatalog(api, bucketName);
694
701
  }
@@ -696,9 +703,9 @@ const _R2Bucket = Resource(
696
703
  await deleteMiniflareBinding(this.scope, "r2", this.output.dev.id);
697
704
  }
698
705
  if (props.empty) {
699
- await emptyBucket(api, bucketName, props);
706
+ await emptyBucket(api, bucketName, deleteProps);
700
707
  }
701
- await deleteBucket(api, bucketName, props);
708
+ await deleteBucket(api, bucketName, deleteProps);
702
709
  }
703
710
  return this.destroy();
704
711
  }
@@ -540,10 +540,17 @@ export async function deleteDatabase(
540
540
  api: CloudflareApi,
541
541
  databaseId: string,
542
542
  ): Promise<void> {
543
- await extractCloudflareResult(
544
- `delete D1 database "${databaseId}"`,
545
- api.delete(`/accounts/${api.accountId}/d1/database/${databaseId}`),
546
- );
543
+ try {
544
+ await extractCloudflareResult(
545
+ `delete D1 database "${databaseId}"`,
546
+ api.delete(`/accounts/${api.accountId}/d1/database/${databaseId}`),
547
+ );
548
+ } catch (error) {
549
+ if (error instanceof CloudflareApiError && error.status === 404) {
550
+ return;
551
+ }
552
+ throw error;
553
+ }
547
554
  }
548
555
 
549
556
  /**
@@ -2,7 +2,10 @@
2
2
 
3
3
  export * from "./account-api-token.ts";
4
4
  export * from "./account-id.ts";
5
+ export * from "./ai-crawler.ts";
5
6
  export * from "./ai-gateway.ts";
7
+ export * from "./ai-search-token.ts";
8
+ export * from "./ai-search.ts";
6
9
  export * from "./ai.ts";
7
10
  export * from "./analytics-engine.ts";
8
11
  export * from "./api-error.ts";
@@ -70,6 +73,7 @@ export * from "./vectorize-index.ts";
70
73
  export * from "./vectorize-metadata-index.ts";
71
74
  export * from "./version-metadata.ts";
72
75
  export * from "./vite/vite.ts";
76
+ export * from "./vpc-service.ts";
73
77
  export * from "./warp-default-profile.ts";
74
78
  export * from "./warp-device-profile.ts";
75
79
  export * from "./website.ts";
@@ -31,29 +31,26 @@ export interface MiniflareWorkerInput {
31
31
  cwd: string;
32
32
  }
33
33
 
34
- type RemoteOnlyBindingType =
35
- | "ai"
36
- | "browser"
37
- | "dispatch_namespace"
38
- | "mtls_certificate"
39
- | "vectorize";
40
- type RemoteOptionalBindingType =
41
- | "d1"
42
- | "images"
43
- | "kv_namespace"
44
- | "queue"
45
- | "r2_bucket";
46
-
47
34
  type RemoteBinding =
35
+ // Supported remote bindings that are NOT a fetcher require the `raw` flag.
48
36
  | (Extract<
49
37
  WorkerBindingSpec,
50
38
  {
51
- type: RemoteOnlyBindingType | RemoteOptionalBindingType;
39
+ type:
40
+ | "ai"
41
+ | "browser"
42
+ | "dispatch_namespace"
43
+ | "mtls_certificate"
44
+ | "vectorize"
45
+ | "d1"
46
+ | "images"
47
+ | "kv_namespace"
48
+ | "queue"
49
+ | "r2_bucket";
52
50
  }
53
- > & {
54
- raw: true;
55
- })
56
- | WorkerBindingService;
51
+ > & { raw: true })
52
+ // Fetcher type bindings do not require the `raw` flag and will throw an error if it is present.
53
+ | Extract<WorkerBindingSpec, { type: "service" | "vpc_service" }>;
57
54
 
58
55
  type BaseWorkerOptions = {
59
56
  [K in keyof miniflare.WorkerOptions]: K extends
@@ -324,6 +321,15 @@ export const buildWorkerOptions = async (
324
321
  };
325
322
  break;
326
323
  }
324
+ case "vpc_service": {
325
+ remoteBindings.push({
326
+ type: "vpc_service",
327
+ name: key,
328
+ service_name: binding.name,
329
+ service_id: binding.serviceId,
330
+ });
331
+ break;
332
+ }
327
333
  case "worker_loader": {
328
334
  (options.workerLoaders ??= {})[key] = {};
329
335
  break;
@@ -447,6 +453,12 @@ export const buildWorkerOptions = async (
447
453
  remoteProxyConnectionString: remoteProxy.connectionString,
448
454
  };
449
455
  break;
456
+ case "vpc_service":
457
+ (options.vpcServices ??= {})[binding.name] = {
458
+ service_id: binding.service_id,
459
+ remoteProxyConnectionString: remoteProxy.connectionString,
460
+ };
461
+ break;
450
462
  default: {
451
463
  assertNever(binding);
452
464
  }
@@ -32,10 +32,12 @@ export async function createMiniflareWorkerProxy(options: {
32
32
 
33
33
  server.on("request", async (req, res) => {
34
34
  try {
35
- const response = await handleFetch(req);
35
+ const response = await handleFetch(req, res);
36
36
  writeMiniflareResponseToNode(response, res);
37
37
  } catch (error) {
38
- console.error(error);
38
+ if (!(error instanceof DOMException && error.name === "AbortError")) {
39
+ console.error(error);
40
+ }
39
41
  const response = MiniflareWorkerProxyError.fromUnknown(error).toResponse(
40
42
  options.mode,
41
43
  );
@@ -45,6 +47,7 @@ export async function createMiniflareWorkerProxy(options: {
45
47
 
46
48
  const handleFetch = async (
47
49
  req: http.IncomingMessage,
50
+ res: http.ServerResponse,
48
51
  ): Promise<miniflare.Response> => {
49
52
  const info = parseIncomingMessage(req);
50
53
  options.transformRequest?.(info);
@@ -60,12 +63,14 @@ export async function createMiniflareWorkerProxy(options: {
60
63
  info.url.pathname = "/cdn-cgi/handler/scheduled";
61
64
  }
62
65
 
66
+ const signal = getDisconnectSignal(req, res);
63
67
  const request = new miniflare.Request(info.url, {
64
68
  method: info.method,
65
69
  headers: info.headers,
66
70
  body: info.body,
67
71
  redirect: info.redirect,
68
72
  duplex: info.duplex,
73
+ signal,
69
74
  });
70
75
  return await options.miniflare.dispatchFetch(request);
71
76
  };
@@ -114,6 +119,46 @@ export async function createMiniflareWorkerProxy(options: {
114
119
  };
115
120
  }
116
121
 
122
+ /**
123
+ * Creates an abort signal that is triggered when the downstream client
124
+ * disconnects before the response is fully written.
125
+ *
126
+ * This signal should be forwarded to Miniflare dispatch so Worker `req.signal`
127
+ * listeners can observe local client cancellation.
128
+ */
129
+ function getDisconnectSignal(
130
+ req: http.IncomingMessage,
131
+ res: http.ServerResponse,
132
+ ): AbortSignal {
133
+ const controller = new AbortController();
134
+ const onAbort = () => {
135
+ if (!controller.signal.aborted) {
136
+ controller.abort();
137
+ }
138
+ cleanup();
139
+ };
140
+ const onClose = () => {
141
+ if (!res.writableEnded) {
142
+ onAbort();
143
+ return;
144
+ }
145
+ cleanup();
146
+ };
147
+ const cleanup = () => {
148
+ req.off("aborted", onAbort);
149
+ req.socket?.off("close", onClose);
150
+ res.off("close", onClose);
151
+ res.off("finish", cleanup);
152
+ };
153
+
154
+ req.on("aborted", onAbort);
155
+ req.socket?.on("close", onClose);
156
+ res.on("close", onClose);
157
+ res.on("finish", cleanup);
158
+
159
+ return controller.signal;
160
+ }
161
+
117
162
  /**
118
163
  * Listens on the given port, retrying on the next port if the port is already in use.
119
164
  */
@@ -98,6 +98,12 @@ export const PERMISSION_GROUPS = [
98
98
  description: "Grants access to reading AI Gateways",
99
99
  scopes: ["com.cloudflare.api.account"],
100
100
  },
101
+ {
102
+ id: "9e9b428a0bcd46fd80e580b46a69963c",
103
+ name: "AI Search Index Engine",
104
+ description: "Grants access to run AI Search Index Engine",
105
+ scopes: ["com.cloudflare.api.account"],
106
+ },
101
107
  {
102
108
  id: "644535f4ed854494a59cb289d634b257",
103
109
  name: "AI Gateway Run",