alchemy 0.83.3 → 0.85.0

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 (111) hide show
  1. package/bin/alchemy.js +1 -1
  2. package/lib/cloudflare/api-error.d.ts +11 -0
  3. package/lib/cloudflare/api-error.d.ts.map +1 -1
  4. package/lib/cloudflare/api-error.js +11 -0
  5. package/lib/cloudflare/api-error.js.map +1 -1
  6. package/lib/cloudflare/api.js +1 -1
  7. package/lib/cloudflare/api.js.map +1 -1
  8. package/lib/cloudflare/bindings.d.ts +12 -2
  9. package/lib/cloudflare/bindings.d.ts.map +1 -1
  10. package/lib/cloudflare/bindings.js.map +1 -1
  11. package/lib/cloudflare/bound.d.ts +2 -1
  12. package/lib/cloudflare/bound.d.ts.map +1 -1
  13. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  14. package/lib/cloudflare/d1-database.js +9 -1
  15. package/lib/cloudflare/d1-database.js.map +1 -1
  16. package/lib/cloudflare/index.d.ts +1 -0
  17. package/lib/cloudflare/index.d.ts.map +1 -1
  18. package/lib/cloudflare/index.js +1 -0
  19. package/lib/cloudflare/index.js.map +1 -1
  20. package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
  21. package/lib/cloudflare/kv-namespace.js +2 -2
  22. package/lib/cloudflare/kv-namespace.js.map +1 -1
  23. package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
  24. package/lib/cloudflare/miniflare/build-worker-options.js +15 -0
  25. package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
  26. package/lib/cloudflare/miniflare/miniflare-controller.d.ts +1 -1
  27. package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
  28. package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
  29. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +41 -3
  30. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
  31. package/lib/cloudflare/vpc-service.d.ts +288 -0
  32. package/lib/cloudflare/vpc-service.d.ts.map +1 -0
  33. package/lib/cloudflare/vpc-service.js +238 -0
  34. package/lib/cloudflare/vpc-service.js.map +1 -0
  35. package/lib/cloudflare/worker-metadata.d.ts +1 -0
  36. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  37. package/lib/cloudflare/worker-metadata.js +8 -0
  38. package/lib/cloudflare/worker-metadata.js.map +1 -1
  39. package/lib/cloudflare/worker-stub.d.ts.map +1 -1
  40. package/lib/cloudflare/worker-stub.js +8 -10
  41. package/lib/cloudflare/worker-stub.js.map +1 -1
  42. package/lib/cloudflare/worker-subdomain.d.ts +11 -8
  43. package/lib/cloudflare/worker-subdomain.d.ts.map +1 -1
  44. package/lib/cloudflare/worker-subdomain.js +16 -25
  45. package/lib/cloudflare/worker-subdomain.js.map +1 -1
  46. package/lib/cloudflare/worker.d.ts +16 -0
  47. package/lib/cloudflare/worker.d.ts.map +1 -1
  48. package/lib/cloudflare/worker.js +50 -24
  49. package/lib/cloudflare/worker.js.map +1 -1
  50. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  51. package/lib/cloudflare/wrangler.json.js +9 -0
  52. package/lib/cloudflare/wrangler.json.js.map +1 -1
  53. package/lib/planetscale/api/sdk.gen.d.ts +874 -190
  54. package/lib/planetscale/api/sdk.gen.d.ts.map +1 -1
  55. package/lib/planetscale/api/sdk.gen.js +1250 -306
  56. package/lib/planetscale/api/sdk.gen.js.map +1 -1
  57. package/lib/planetscale/api/types.gen.d.ts +8241 -4128
  58. package/lib/planetscale/api/types.gen.d.ts.map +1 -1
  59. package/lib/planetscale/branch.js +4 -4
  60. package/lib/planetscale/branch.js.map +1 -1
  61. package/lib/planetscale/database.d.ts +43 -24
  62. package/lib/planetscale/database.d.ts.map +1 -1
  63. package/lib/planetscale/database.js +39 -18
  64. package/lib/planetscale/database.js.map +1 -1
  65. package/lib/planetscale/default-role.d.ts +51 -0
  66. package/lib/planetscale/default-role.d.ts.map +1 -0
  67. package/lib/planetscale/default-role.js +100 -0
  68. package/lib/planetscale/default-role.js.map +1 -0
  69. package/lib/planetscale/index.d.ts +1 -0
  70. package/lib/planetscale/index.d.ts.map +1 -1
  71. package/lib/planetscale/index.js +1 -0
  72. package/lib/planetscale/index.js.map +1 -1
  73. package/lib/planetscale/organization.js +1 -1
  74. package/lib/planetscale/organization.js.map +1 -1
  75. package/lib/planetscale/role.d.ts +1 -1
  76. package/lib/planetscale/role.d.ts.map +1 -1
  77. package/lib/planetscale/role.js +1 -1
  78. package/lib/planetscale/role.js.map +1 -1
  79. package/lib/planetscale/utils.d.ts +1 -1
  80. package/lib/planetscale/utils.d.ts.map +1 -1
  81. package/lib/planetscale/utils.js +9 -10
  82. package/lib/planetscale/utils.js.map +1 -1
  83. package/lib/state/sqlite-state-store.js +2 -0
  84. package/lib/state/sqlite-state-store.js.map +1 -1
  85. package/package.json +2 -2
  86. package/src/cloudflare/api-error.ts +19 -0
  87. package/src/cloudflare/api.ts +1 -1
  88. package/src/cloudflare/bindings.ts +15 -2
  89. package/src/cloudflare/bound.ts +6 -3
  90. package/src/cloudflare/d1-database.ts +11 -4
  91. package/src/cloudflare/index.ts +1 -0
  92. package/src/cloudflare/kv-namespace.ts +6 -2
  93. package/src/cloudflare/miniflare/build-worker-options.ts +30 -18
  94. package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +47 -2
  95. package/src/cloudflare/vpc-service.ts +466 -0
  96. package/src/cloudflare/worker-metadata.ts +8 -0
  97. package/src/cloudflare/worker-stub.ts +9 -11
  98. package/src/cloudflare/worker-subdomain.ts +29 -34
  99. package/src/cloudflare/worker.ts +170 -98
  100. package/src/cloudflare/wrangler.json.ts +8 -0
  101. package/src/planetscale/api/sdk.gen.ts +2331 -987
  102. package/src/planetscale/api/types.gen.ts +8980 -4574
  103. package/src/planetscale/branch.ts +4 -4
  104. package/src/planetscale/database.ts +87 -46
  105. package/src/planetscale/default-role.ts +162 -0
  106. package/src/planetscale/index.ts +1 -0
  107. package/src/planetscale/organization.ts +1 -1
  108. package/src/planetscale/role.ts +3 -2
  109. package/src/planetscale/utils.ts +10 -10
  110. package/src/state/sqlite-state-store.ts +2 -0
  111. package/workers/tunnel-proxy.js +1 -1
@@ -0,0 +1,466 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource.ts";
3
+ import { isCloudflareApiError } from "./api-error.ts";
4
+ import { extractCloudflareResult } from "./api-response.ts";
5
+ import {
6
+ createCloudflareApi,
7
+ type CloudflareApi,
8
+ type CloudflareApiOptions,
9
+ } from "./api.ts";
10
+ import type { Tunnel } from "./tunnel.ts";
11
+
12
+ /**
13
+ * Properties for creating or updating a VPC service.
14
+ */
15
+ export interface VpcServiceProps extends CloudflareApiOptions {
16
+ /**
17
+ * The name of the VPC service to create.
18
+ *
19
+ * @default ${app}-${stage}-${id}
20
+ */
21
+ name?: string;
22
+ /**
23
+ * The type of the VPC service. Currently only "http" is supported, but tcp will be supported in the future.
24
+ *
25
+ * @default "http"
26
+ */
27
+ serviceType?: "http";
28
+ /**
29
+ * The TCP port for the VPC service.
30
+ */
31
+ tcpPort?: number;
32
+ /**
33
+ * The application protocol for the VPC service.
34
+ */
35
+ appProtocol?: string;
36
+ /**
37
+ * The HTTP port for the VPC service.
38
+ *
39
+ * @default 80
40
+ */
41
+ httpPort?: number;
42
+ /**
43
+ * The HTTPS port for the VPC service.
44
+ *
45
+ * @default 443
46
+ */
47
+ httpsPort?: number;
48
+ /**
49
+ * The host for the VPC service.
50
+ */
51
+ host:
52
+ | VpcService.IPv4Host
53
+ | VpcService.IPv6Host
54
+ | VpcService.DualStackHost
55
+ | VpcService.HostnameHost;
56
+ /**
57
+ * Whether to adopt the VPC service if it already exists.
58
+ *
59
+ * @default false
60
+ */
61
+ adopt?: boolean;
62
+ }
63
+
64
+ export declare namespace VpcService {
65
+ /** Host definition: hostname (with resolver network) or IPv4/IPv6/dual-stack with network. */
66
+ export type Host = IPv4Host | IPv6Host | DualStackHost | HostnameHost;
67
+
68
+ /**
69
+ * Represents a VPC service that is accessible via an IPv4 address.
70
+ */
71
+ export interface IPv4Host {
72
+ ipv4: string;
73
+ network: Network;
74
+ }
75
+
76
+ /**
77
+ * Represents a VPC service that is accessible via an IPv6 address.
78
+ */
79
+ export interface IPv6Host {
80
+ ipv6: string;
81
+ network: Network;
82
+ }
83
+
84
+ /**
85
+ * Represents a VPC service that is accessible via both IPv4 and IPv6 addresses.
86
+ */
87
+ export interface DualStackHost {
88
+ ipv4: string;
89
+ ipv6: string;
90
+ network: Network;
91
+ }
92
+
93
+ /**
94
+ * Network the VPC service is reached through. Use a `Tunnel` resource or an existing `tunnelId`.
95
+ */
96
+ export type Network = { tunnelId: string } | { tunnel: Tunnel };
97
+
98
+ /**
99
+ * Hostname-based host. DNS is resolved over the resolver network (tunnel).
100
+ * Optionally specify `resolverIps` for explicit DNS resolver IPs.
101
+ */
102
+ export interface HostnameHost {
103
+ hostname: string;
104
+ resolverNetwork: Network & { resolverIps?: string[] };
105
+ }
106
+ }
107
+
108
+ /**
109
+ * A VPC service instance. Bind to a Worker to allow the Worker to fetch from
110
+ * the private host (hostname or IP) over the configured tunnel.
111
+ */
112
+ export type VpcService = Omit<VpcServiceProps, "name" | "adopt"> & {
113
+ /** Display name of the service. */
114
+ name: string;
115
+ /** Cloudflare-assigned service ID. */
116
+ serviceId: string;
117
+ /** Creation time (Unix ms). */
118
+ createdAt: number;
119
+ /** Last update time (Unix ms). */
120
+ updatedAt: number;
121
+ /** Resource kind for bindings. */
122
+ type: "vpc_service";
123
+ };
124
+
125
+ /**
126
+ * Create or update a VPC service that routes Worker traffic to a private host
127
+ * (hostname or IP) through a Cloudflare Tunnel.
128
+ *
129
+ * [VPC Services](https://developers.cloudflare.com/workers-vpc/configuration/vpc-services/)
130
+ * enable Workers to securely access private network resources through Cloudflare Tunnel.
131
+ * Configure a host (hostname or IP) and optional ports, then bind the service to a Worker
132
+ * to reach private backends.
133
+ *
134
+ * @example
135
+ * // Minimal: hostname through a tunnel
136
+ * ```ts
137
+ * const tunnel = await Tunnel("my-tunnel", {
138
+ * ingress: [{ service: "http://localhost:3000" }],
139
+ * });
140
+ * const vpcService = await VpcService("my-service", {
141
+ * host: {
142
+ * hostname: "localhost",
143
+ * resolverNetwork: { tunnel, resolverIps: ["127.0.0.1"] },
144
+ * },
145
+ * });
146
+ * ```
147
+ *
148
+ * @example
149
+ * // IPv4 address
150
+ * ```ts
151
+ * const vpcService = await VpcService("internal-api", {
152
+ * host: {
153
+ * ipv4: "192.168.1.100",
154
+ * network: { tunnel },
155
+ * },
156
+ * });
157
+ * ```
158
+ *
159
+ * @example
160
+ * // IPv6 address
161
+ * ```ts
162
+ * const vpcService = await VpcService("ipv6-service", {
163
+ * host: { ipv6: "::1", network: { tunnel } },
164
+ * });
165
+ * ```
166
+ *
167
+ * @example
168
+ * // Dual stack (IPv4 + IPv6)
169
+ * ```ts
170
+ * const vpcService = await VpcService("dual-stack-service", {
171
+ * host: {
172
+ * ipv4: "192.168.1.100",
173
+ * ipv6: "::1",
174
+ * network: { tunnel },
175
+ * },
176
+ * });
177
+ * ```
178
+ *
179
+ * @example
180
+ * // Custom HTTP/HTTPS ports
181
+ * ```ts
182
+ * const vpcService = await VpcService("dev-server", {
183
+ * httpPort: 5173,
184
+ * httpsPort: 5174,
185
+ * host: {
186
+ * hostname: "localhost",
187
+ * resolverNetwork: { tunnel, resolverIps: ["127.0.0.1"] },
188
+ * },
189
+ * });
190
+ * ```
191
+ *
192
+ * @example
193
+ * // Bind to a Worker
194
+ * ```ts
195
+ * const vpcService = await VpcService("private-api", {
196
+ * httpPort: 8080,
197
+ * host: {
198
+ * hostname: "internal-api",
199
+ * resolverNetwork: { tunnel, resolverIps: ["10.0.0.1"] },
200
+ * },
201
+ * });
202
+ * const worker = await Worker("api-gateway", {
203
+ * entrypoint: "./src/worker.ts",
204
+ * bindings: { PRIVATE_API: vpcService },
205
+ * });
206
+ * ```
207
+ *
208
+ * @example
209
+ * // Existing tunnel by ID
210
+ * ```ts
211
+ * const vpcService = await VpcService("existing-tunnel-service", {
212
+ * host: {
213
+ * hostname: "internal.example.com",
214
+ * resolverNetwork: {
215
+ * tunnelId: "e6a0817c-79c5-40ca-9776-a1c019defe70",
216
+ * resolverIps: ["10.0.0.53"],
217
+ * },
218
+ * },
219
+ * });
220
+ * ```
221
+ *
222
+ * @example
223
+ * // Adopt an existing VPC service
224
+ * ```ts
225
+ * const vpcService = await VpcService("adopted-service", {
226
+ * name: "existing-service-name",
227
+ * adopt: true,
228
+ * host: {
229
+ * hostname: "localhost",
230
+ * resolverNetwork: { tunnel },
231
+ * },
232
+ * });
233
+ * ```
234
+ */
235
+ export const VpcService = Resource(
236
+ "cloudflare::VpcService",
237
+ async function (
238
+ this: Context<VpcService>,
239
+ id: string,
240
+ props: VpcServiceProps,
241
+ ): Promise<VpcService> {
242
+ const api = await createCloudflareApi(props);
243
+ if (this.phase === "delete") {
244
+ if (this.output?.serviceId) {
245
+ await deleteService(api, this.output.serviceId);
246
+ }
247
+ return this.destroy();
248
+ }
249
+ const input: ConnectivityService.Input = {
250
+ name: props.name ?? this.scope.createPhysicalName(id),
251
+ type: props.serviceType ?? "http",
252
+ tcp_port: props.tcpPort,
253
+ app_protocol: props.appProtocol,
254
+ http_port: props.httpPort,
255
+ https_port: props.httpsPort,
256
+ host: normalizeHost(props.host),
257
+ };
258
+ switch (this.phase) {
259
+ case "create": {
260
+ const adopt = props.adopt ?? this.scope.adopt;
261
+ const service = await createService(api, input).catch(async (err) => {
262
+ if (isCloudflareApiError(err, { code: 5101 }) && adopt) {
263
+ const services = await listServices(api);
264
+ const service = services.find((s) => s.name === input.name);
265
+ if (service) {
266
+ return await updateService(api, service.service_id, input);
267
+ }
268
+ }
269
+ throw err;
270
+ });
271
+ return formatOutput(service);
272
+ }
273
+ case "update": {
274
+ const service = await updateService(api, this.output.serviceId, input);
275
+ return formatOutput(service);
276
+ }
277
+ }
278
+
279
+ function normalizeHost(host: VpcService.Host): ConnectivityService.Host {
280
+ if ("hostname" in host) {
281
+ return {
282
+ hostname: host.hostname,
283
+ resolver_network: normalizeNetwork(host.resolverNetwork),
284
+ };
285
+ }
286
+ return {
287
+ ...host,
288
+ network: normalizeNetwork(host.network),
289
+ };
290
+ }
291
+
292
+ function normalizeNetwork<T extends VpcService.Network>(
293
+ network: T,
294
+ ): ConnectivityService.Network {
295
+ if ("tunnelId" in network) {
296
+ const { tunnelId, ...rest } = network;
297
+ return { tunnel_id: network.tunnelId, ...rest };
298
+ }
299
+ const { tunnel, ...rest } = network;
300
+ return { tunnel_id: tunnel.tunnelId, ...rest };
301
+ }
302
+
303
+ function formatOutput(service: ConnectivityService): VpcService {
304
+ return {
305
+ name: service.name,
306
+ serviceId: service.service_id,
307
+ serviceType: service.type,
308
+ tcpPort: service.tcp_port,
309
+ appProtocol: service.app_protocol,
310
+ httpPort: service.http_port,
311
+ httpsPort: service.https_port,
312
+ host:
313
+ "hostname" in service.host
314
+ ? {
315
+ hostname: service.host.hostname,
316
+ resolverNetwork: {
317
+ tunnelId: service.host.resolver_network.tunnel_id,
318
+ resolverIps: service.host.resolver_network.resolver_ips,
319
+ },
320
+ }
321
+ : {
322
+ ...service.host,
323
+ network: { tunnelId: service.host.network.tunnel_id },
324
+ },
325
+ createdAt: new Date(service.created_at).getTime(),
326
+ updatedAt: new Date(service.updated_at).getTime(),
327
+ type: "vpc_service",
328
+ };
329
+ }
330
+ },
331
+ );
332
+
333
+ /**
334
+ * Create a connectivity (VPC) service via the Cloudflare API.
335
+ * @internal
336
+ */
337
+ export async function createService(
338
+ api: CloudflareApi,
339
+ body: ConnectivityService.Input,
340
+ ): Promise<ConnectivityService> {
341
+ return await extractCloudflareResult<ConnectivityService>(
342
+ `create connectivity service`,
343
+ api.post(
344
+ `/accounts/${api.accountId}/connectivity/directory/services`,
345
+ body,
346
+ ),
347
+ );
348
+ }
349
+
350
+ /**
351
+ * Delete a connectivity (VPC) service. No-op if the service is already gone (404).
352
+ * @internal
353
+ */
354
+ export async function deleteService(
355
+ api: CloudflareApi,
356
+ serviceId: string,
357
+ ): Promise<void> {
358
+ await extractCloudflareResult(
359
+ `delete connectivity service "${serviceId}"`,
360
+ api.delete(
361
+ `/accounts/${api.accountId}/connectivity/directory/services/${serviceId}`,
362
+ ),
363
+ ).catch((err) => {
364
+ if (!isCloudflareApiError(err, { status: 404 })) {
365
+ throw err;
366
+ }
367
+ });
368
+ }
369
+
370
+ /**
371
+ * Fetch a single connectivity (VPC) service by ID.
372
+ * @internal
373
+ */
374
+ export async function getService(
375
+ api: CloudflareApi,
376
+ serviceId: string,
377
+ ): Promise<ConnectivityService> {
378
+ return await extractCloudflareResult<ConnectivityService>(
379
+ `get connectivity service "${serviceId}"`,
380
+ api.get(
381
+ `/accounts/${api.accountId}/connectivity/directory/services/${serviceId}`,
382
+ ),
383
+ );
384
+ }
385
+
386
+ /**
387
+ * List connectivity (VPC) services for the account.
388
+ * @internal
389
+ */
390
+ export async function listServices(
391
+ api: CloudflareApi,
392
+ ): Promise<ConnectivityService[]> {
393
+ return await extractCloudflareResult<ConnectivityService[]>(
394
+ `list connectivity services`,
395
+ api.get(
396
+ `/accounts/${api.accountId}/connectivity/directory/services?per_page=1000`,
397
+ ),
398
+ );
399
+ }
400
+
401
+ /**
402
+ * Update an existing connectivity (VPC) service.
403
+ * @internal
404
+ */
405
+ export async function updateService(
406
+ api: CloudflareApi,
407
+ serviceId: string,
408
+ body: ConnectivityService.Input,
409
+ ): Promise<ConnectivityService> {
410
+ return await extractCloudflareResult<ConnectivityService>(
411
+ `update connectivity service "${serviceId}"`,
412
+ api.put(
413
+ `/accounts/${api.accountId}/connectivity/directory/services/${serviceId}`,
414
+ body,
415
+ ),
416
+ );
417
+ }
418
+
419
+ interface ConnectivityService extends ConnectivityService.Input {
420
+ service_id: string;
421
+ created_at: string;
422
+ updated_at: string;
423
+ }
424
+
425
+ declare namespace ConnectivityService {
426
+ export interface Input {
427
+ name: string;
428
+ type: "http";
429
+ tcp_port?: number;
430
+ app_protocol?: string;
431
+ http_port?: number;
432
+ https_port?: number;
433
+ host: Host;
434
+ }
435
+
436
+ export type Host = IPv4Host | IPv6Host | DualStackHost | HostnameHost;
437
+
438
+ export interface IPv4Host {
439
+ ipv4: string;
440
+ network: Network;
441
+ }
442
+
443
+ export interface IPv6Host {
444
+ ipv6: string;
445
+ network: Network;
446
+ }
447
+
448
+ export interface DualStackHost {
449
+ ipv4: string;
450
+ ipv6: string;
451
+ network: Network;
452
+ }
453
+
454
+ export interface Network {
455
+ tunnel_id: string;
456
+ }
457
+
458
+ export interface HostnameHost {
459
+ hostname: string;
460
+ resolver_network: ResolverNetwork;
461
+ }
462
+
463
+ export interface ResolverNetwork extends Network {
464
+ resolver_ips?: string[];
465
+ }
466
+ }
@@ -221,6 +221,7 @@ export interface WorkerMetadata {
221
221
  placement?: WorkerPlacement;
222
222
  limits?: {
223
223
  cpu_ms?: number;
224
+ subrequests?: number;
224
225
  };
225
226
  tail_consumers?: Array<Worker | { service: string }>;
226
227
  }
@@ -625,6 +626,13 @@ export async function prepareWorkerMetadata(
625
626
  namespace_id: binding.namespace_id.toString(),
626
627
  simple: binding.simple,
627
628
  });
629
+ } else if (binding.type === "vpc_service") {
630
+ meta.bindings.push({
631
+ type: "vpc_service",
632
+ name: bindingName,
633
+ service_name: binding.name,
634
+ service_id: binding.serviceId,
635
+ });
628
636
  } else {
629
637
  assertNever(
630
638
  binding,
@@ -9,7 +9,7 @@ import {
9
9
  type CloudflareApi,
10
10
  type CloudflareApiOptions,
11
11
  } from "./api.ts";
12
- import { WorkerSubdomain } from "./worker-subdomain.ts";
12
+ import { createWorkerUrl, enableWorkerSubdomain } from "./worker-subdomain.ts";
13
13
 
14
14
  /**
15
15
  * Properties for creating a Worker stub
@@ -105,26 +105,24 @@ export const WorkerStub = Resource("cloudflare::WorkerStub", async function <
105
105
  return this.destroy();
106
106
  }
107
107
 
108
+ const scriptName = props.name;
109
+
108
110
  // If worker doesn't exist and we're in create phase, create an empty one
109
- if (!(await exists(api, props.name)) && this.phase === "create") {
110
- await createEmptyWorker(api, props.name);
111
+ if (!(await exists(api, scriptName)) && this.phase === "create") {
112
+ await createEmptyWorker(api, scriptName);
111
113
  }
112
114
 
113
115
  // Configure URL if requested (defaults to true)
114
- const subdomain =
115
- props.url !== false
116
- ? await WorkerSubdomain("url", {
117
- ...props,
118
- scriptName: props.name,
119
- })
120
- : undefined;
116
+ if (props.url !== false) {
117
+ await enableWorkerSubdomain(api, scriptName);
118
+ }
121
119
 
122
120
  // Return the worker stub info
123
121
  return {
124
122
  type: "service",
125
123
  __rpc__: props.rpc as unknown as RPC,
126
124
  ...props,
127
- url: subdomain?.url,
125
+ url: await createWorkerUrl(api, scriptName),
128
126
  } as WorkerStub<RPC>;
129
127
  });
130
128
 
@@ -4,11 +4,7 @@ import { memoize } from "../util/memoize.ts";
4
4
  import { withExponentialBackoff } from "../util/retry.ts";
5
5
  import { CloudflareApiError } from "./api-error.ts";
6
6
  import { extractCloudflareResult } from "./api-response.ts";
7
- import {
8
- createCloudflareApi,
9
- type CloudflareApi,
10
- type CloudflareApiOptions,
11
- } from "./api.ts";
7
+ import { type CloudflareApi, type CloudflareApiOptions } from "./api.ts";
12
8
 
13
9
  interface WorkerSubdomainProps extends CloudflareApiOptions {
14
10
  /**
@@ -21,6 +17,14 @@ interface WorkerSubdomainProps extends CloudflareApiOptions {
21
17
  * @default undefined
22
18
  */
23
19
  previewVersionId?: string;
20
+ /**
21
+ * Whether to enable previews for the subdomain.
22
+ *
23
+ * Must be `false` when the worker has Durable Object bindings.
24
+ *
25
+ * @default true
26
+ */
27
+ previewsEnabled?: boolean;
24
28
  /**
25
29
  * Prevents the subdomain from being deleted when the worker is deleted.
26
30
  *
@@ -37,12 +41,7 @@ interface WorkerSubdomainProps extends CloudflareApiOptions {
37
41
  dev?: boolean;
38
42
  }
39
43
 
40
- export interface WorkerSubdomain {
41
- /**
42
- * The `workers.dev` URL for the worker or preview version.
43
- */
44
- url: string;
45
- }
44
+ export interface WorkerSubdomain {}
46
45
 
47
46
  export const WorkerSubdomain = Resource(
48
47
  "cloudflare::WorkerSubdomain",
@@ -51,31 +50,10 @@ export const WorkerSubdomain = Resource(
51
50
  _id: string,
52
51
  props: WorkerSubdomainProps,
53
52
  ) {
54
- if (this.scope.local && props.dev) {
55
- return {
56
- url: this.output?.url ?? "https://unavailable.alchemy.run",
57
- };
58
- }
59
-
60
- const api = await createCloudflareApi(props);
61
53
  if (this.phase === "delete") {
62
- if (!props.retain) {
63
- await disableWorkerSubdomain(api, props.scriptName);
64
- }
65
54
  return this.destroy();
66
55
  }
67
- await enableWorkerSubdomain(api, props.scriptName);
68
- const subdomain = await getAccountSubdomain(api);
69
- const base = `${subdomain}.workers.dev`;
70
- let url: string;
71
- if (props.previewVersionId) {
72
- url = `https://${props.previewVersionId.substring(0, 8)}-${props.scriptName}.${base}`;
73
- } else {
74
- url = `https://${props.scriptName}.${base}`;
75
- }
76
- return {
77
- url,
78
- };
56
+ return {};
79
57
  },
80
58
  );
81
59
 
@@ -102,6 +80,7 @@ export async function disableWorkerSubdomain(
102
80
  export async function enableWorkerSubdomain(
103
81
  api: CloudflareApi,
104
82
  scriptName: string,
83
+ previewsEnabled: boolean = true,
105
84
  ) {
106
85
  await withExponentialBackoff(
107
86
  () =>
@@ -111,7 +90,7 @@ export async function enableWorkerSubdomain(
111
90
  `/accounts/${api.accountId}/workers/scripts/${scriptName}/subdomain`,
112
91
  {
113
92
  enabled: true,
114
- previews_enabled: true,
93
+ previews_enabled: previewsEnabled,
115
94
  },
116
95
  ),
117
96
  ),
@@ -160,3 +139,19 @@ export const getAccountSubdomain = memoize(
160
139
  },
161
140
  (api) => api.accountId,
162
141
  );
142
+
143
+ export async function createWorkerUrl(
144
+ api: CloudflareApi,
145
+ scriptName: string,
146
+ previewVersionId?: string,
147
+ ) {
148
+ const subdomain = await getAccountSubdomain(api);
149
+ const base = `${subdomain}.workers.dev`;
150
+ let url: string;
151
+ if (previewVersionId) {
152
+ url = `https://${previewVersionId.substring(0, 8)}-${scriptName}.${base}`;
153
+ } else {
154
+ url = `https://${scriptName}.${base}`;
155
+ }
156
+ return url;
157
+ }