alchemy 0.43.4 → 0.44.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.
- package/bin/alchemy.mjs +430 -346
- package/bin/alchemy.ts +7 -21
- package/bin/commands/create.ts +79 -23
- package/bin/services/package-manager.ts +0 -3
- package/bin/services/template-manager.ts +9 -4
- package/bin/services/vibe-rules.ts +47 -0
- package/bin/types.ts +25 -5
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +8 -11
- package/lib/alchemy.js.map +1 -1
- package/lib/apply.js +2 -0
- package/lib/apply.js.map +1 -1
- package/lib/build-date.d.ts +1 -1
- package/lib/build-date.js +1 -1
- package/lib/cloudflare/assets.d.ts +1 -1
- package/lib/cloudflare/assets.d.ts.map +1 -1
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +9 -6
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker-dev.d.ts.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker-dev.js +4 -1
- package/lib/cloudflare/bundle/bundle-worker-dev.js.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker.d.ts.map +1 -1
- package/lib/cloudflare/bundle/bundle-worker.js +4 -1
- package/lib/cloudflare/bundle/bundle-worker.js.map +1 -1
- package/lib/cloudflare/bundle/external.d.ts +1 -0
- package/lib/cloudflare/bundle/external.d.ts.map +1 -1
- package/lib/cloudflare/bundle/external.js +1 -1
- package/lib/cloudflare/bundle/external.js.map +1 -1
- package/lib/cloudflare/bundle/nodejs-import-warning-plugin.d.ts +10 -0
- package/lib/cloudflare/bundle/nodejs-import-warning-plugin.d.ts.map +1 -0
- package/lib/cloudflare/bundle/nodejs-import-warning-plugin.js +67 -0
- package/lib/cloudflare/bundle/nodejs-import-warning-plugin.js.map +1 -0
- package/lib/cloudflare/container.d.ts +27 -2
- package/lib/cloudflare/container.d.ts.map +1 -1
- package/lib/cloudflare/container.js +82 -24
- package/lib/cloudflare/container.js.map +1 -1
- package/lib/cloudflare/redirect-rule.js +1 -1
- package/lib/cloudflare/redirect-rule.js.map +1 -1
- package/lib/cloudflare/website.d.ts +17 -0
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +1 -0
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker/miniflare.d.ts +4 -1
- package/lib/cloudflare/worker/miniflare.d.ts.map +1 -1
- package/lib/cloudflare/worker/miniflare.js +22 -2
- package/lib/cloudflare/worker/miniflare.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +12 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +24 -0
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +20 -2
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/context.d.ts +6 -1
- package/lib/context.d.ts.map +1 -1
- package/lib/context.js +2 -1
- package/lib/context.js.map +1 -1
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +1 -0
- package/lib/destroy.js.map +1 -1
- package/lib/docker/api.d.ts +11 -0
- package/lib/docker/api.d.ts.map +1 -1
- package/lib/docker/api.js +41 -1
- package/lib/docker/api.js.map +1 -1
- package/lib/docker/image.d.ts +1 -1
- package/lib/docker/image.d.ts.map +1 -1
- package/lib/docker/image.js +11 -5
- package/lib/docker/image.js.map +1 -1
- package/lib/esbuild/bundle.d.ts +2 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/llms.cloudflare.txt +379 -0
- package/lib/llms.d.ts +10 -0
- package/lib/llms.d.ts.map +1 -0
- package/lib/llms.js +12 -0
- package/lib/llms.js.map +1 -0
- package/lib/planetscale/index.d.ts +3 -2
- package/lib/planetscale/index.d.ts.map +1 -1
- package/lib/planetscale/index.js +3 -2
- package/lib/planetscale/index.js.map +1 -1
- package/lib/planetscale/password.d.ts +233 -0
- package/lib/planetscale/password.d.ts.map +1 -0
- package/lib/planetscale/password.js +238 -0
- package/lib/planetscale/password.js.map +1 -0
- package/lib/scope.d.ts +6 -5
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +18 -7
- package/lib/scope.js.map +1 -1
- package/lib/test/bun.d.ts.map +1 -1
- package/lib/test/bun.js +1 -0
- package/lib/test/bun.js.map +1 -1
- package/lib/test/vitest.d.ts.map +1 -1
- package/lib/test/vitest.js +1 -0
- package/lib/test/vitest.js.map +1 -1
- package/lib/util/nanoid.d.ts +3 -0
- package/lib/util/nanoid.d.ts.map +1 -0
- package/lib/util/nanoid.js +13 -0
- package/lib/util/nanoid.js.map +1 -0
- package/lib/util/safe-fetch.d.ts.map +1 -1
- package/lib/util/safe-fetch.js +0 -1
- package/lib/util/safe-fetch.js.map +1 -1
- package/lib/util/telemetry/client.d.ts.map +1 -1
- package/lib/util/telemetry/client.js +13 -3
- package/lib/util/telemetry/client.js.map +1 -1
- package/lib/util/telemetry/constants.d.ts +2 -1
- package/lib/util/telemetry/constants.d.ts.map +1 -1
- package/lib/util/telemetry/constants.js +3 -2
- package/lib/util/telemetry/constants.js.map +1 -1
- package/package.json +10 -2
- package/src/alchemy.ts +8 -13
- package/src/apply.ts +2 -0
- package/src/build-date.ts +1 -1
- package/src/cloudflare/bucket.ts +22 -14
- package/src/cloudflare/bundle/bundle-worker-dev.ts +4 -1
- package/src/cloudflare/bundle/bundle-worker.ts +4 -1
- package/src/cloudflare/bundle/external.ts +1 -1
- package/src/cloudflare/bundle/nodejs-import-warning-plugin.ts +94 -0
- package/src/cloudflare/container.ts +113 -28
- package/src/cloudflare/redirect-rule.ts +1 -1
- package/src/cloudflare/website.ts +21 -1
- package/src/cloudflare/worker/miniflare.ts +33 -8
- package/src/cloudflare/worker-metadata.ts +15 -0
- package/src/cloudflare/wrangler.json.ts +61 -5
- package/src/context.ts +7 -0
- package/src/destroy.ts +1 -0
- package/src/docker/api.ts +57 -1
- package/src/docker/image.ts +15 -6
- package/src/index.ts +1 -0
- package/src/llms.cloudflare.txt +379 -0
- package/src/llms.ts +25 -0
- package/src/planetscale/index.ts +3 -2
- package/src/planetscale/password.ts +366 -0
- package/src/scope.ts +25 -11
- package/src/test/bun.ts +1 -0
- package/src/test/vitest.ts +1 -0
- package/src/util/nanoid.ts +13 -0
- package/src/util/safe-fetch.ts +0 -1
- package/src/util/telemetry/client.ts +17 -3
- package/src/util/telemetry/constants.ts +5 -3
|
@@ -96,6 +96,7 @@ export async function Container<T>(
|
|
|
96
96
|
const image = await Image(id, {
|
|
97
97
|
name: `${api.accountId}/${name}`,
|
|
98
98
|
tag,
|
|
99
|
+
build: props.build,
|
|
99
100
|
registry: {
|
|
100
101
|
server: "registry.cloudflare.com",
|
|
101
102
|
username: credentials.username || credentials.user!,
|
|
@@ -148,6 +149,15 @@ export interface ContainerApplicationProps extends CloudflareApiOptions {
|
|
|
148
149
|
namespaceId: string;
|
|
149
150
|
};
|
|
150
151
|
rollout?: ContainerApplicationRollout;
|
|
152
|
+
/**
|
|
153
|
+
* Whether to adopt an existing container application with the same name.
|
|
154
|
+
*
|
|
155
|
+
* If `true`, the resource will attempt to adopt an existing container application
|
|
156
|
+
* instead of failing when one already exists with the same name.
|
|
157
|
+
*
|
|
158
|
+
* @default false
|
|
159
|
+
*/
|
|
160
|
+
adopt?: boolean;
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
export type SchedulingPolicy =
|
|
@@ -155,7 +165,8 @@ export type SchedulingPolicy =
|
|
|
155
165
|
| "gpu"
|
|
156
166
|
| "regional"
|
|
157
167
|
| "fill_metals"
|
|
158
|
-
| "default"
|
|
168
|
+
| "default"
|
|
169
|
+
| (string & {});
|
|
159
170
|
|
|
160
171
|
export interface ContainerApplication
|
|
161
172
|
extends Resource<"cloudflare::ContainerApplication"> {
|
|
@@ -232,6 +243,21 @@ export interface ContainerApplication
|
|
|
232
243
|
* })
|
|
233
244
|
* }
|
|
234
245
|
* });
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* // Adopt an existing container application
|
|
249
|
+
* const existingApp = await ContainerApplication("existing-app", {
|
|
250
|
+
* name: "existing-app",
|
|
251
|
+
* adopt: true, // Will adopt instead of failing if already exists
|
|
252
|
+
* image: await Image("updated-app", {
|
|
253
|
+
* name: "updated-app",
|
|
254
|
+
* build: {
|
|
255
|
+
* context: "./docker/updated"
|
|
256
|
+
* }
|
|
257
|
+
* }),
|
|
258
|
+
* instances: 2,
|
|
259
|
+
* maxInstances: 5
|
|
260
|
+
* });
|
|
235
261
|
*/
|
|
236
262
|
export const ContainerApplication = Resource(
|
|
237
263
|
"cloudflare::ContainerApplication",
|
|
@@ -287,29 +313,78 @@ export const ContainerApplication = Resource(
|
|
|
287
313
|
name: application.name,
|
|
288
314
|
});
|
|
289
315
|
} else {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
316
|
+
let application: ContainerApplicationData;
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
application = await createContainerApplication(api, {
|
|
320
|
+
name: props.name,
|
|
321
|
+
scheduling_policy: props.schedulingPolicy ?? "default",
|
|
322
|
+
instances: props.instances ?? 1,
|
|
323
|
+
max_instances: props.maxInstances ?? 1,
|
|
324
|
+
durable_objects: props.durableObjects
|
|
325
|
+
? {
|
|
326
|
+
namespace_id: props.durableObjects.namespaceId,
|
|
327
|
+
}
|
|
328
|
+
: undefined,
|
|
329
|
+
constraints: {
|
|
330
|
+
tier: 1,
|
|
331
|
+
},
|
|
332
|
+
configuration: {
|
|
333
|
+
image: imageReference,
|
|
334
|
+
instance_type: props.instanceType ?? "dev",
|
|
335
|
+
observability: {
|
|
336
|
+
logs: {
|
|
337
|
+
enabled: true,
|
|
338
|
+
},
|
|
309
339
|
},
|
|
310
340
|
},
|
|
311
|
-
}
|
|
312
|
-
})
|
|
341
|
+
});
|
|
342
|
+
} catch (error) {
|
|
343
|
+
// Check if this is an "already exists" error and adopt is enabled
|
|
344
|
+
if (
|
|
345
|
+
props.adopt &&
|
|
346
|
+
error instanceof Error &&
|
|
347
|
+
error.message.includes("already exists")
|
|
348
|
+
) {
|
|
349
|
+
// Find the existing container application
|
|
350
|
+
const existingApplication = await findContainerApplicationByName(
|
|
351
|
+
api,
|
|
352
|
+
props.name,
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
if (!existingApplication) {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`Failed to find existing container application '${props.name}' for adoption`,
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Update the existing application with new properties
|
|
362
|
+
application = await updateContainerApplication(
|
|
363
|
+
api,
|
|
364
|
+
existingApplication.id,
|
|
365
|
+
{
|
|
366
|
+
instances: props.instances ?? existingApplication.instances,
|
|
367
|
+
max_instances:
|
|
368
|
+
props.maxInstances ?? existingApplication.max_instances,
|
|
369
|
+
scheduling_policy:
|
|
370
|
+
props.schedulingPolicy ?? existingApplication.scheduling_policy,
|
|
371
|
+
configuration,
|
|
372
|
+
},
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
// Create a rollout for the updated configuration
|
|
376
|
+
await createContainerApplicationRollout(api, application.id, {
|
|
377
|
+
description: "Update configuration on adoption",
|
|
378
|
+
strategy: "rolling",
|
|
379
|
+
kind: props.rollout?.kind ?? "full_auto",
|
|
380
|
+
step_percentage: props.rollout?.stepPercentage ?? 25,
|
|
381
|
+
target_configuration: configuration,
|
|
382
|
+
});
|
|
383
|
+
} else {
|
|
384
|
+
// Re-throw the error if adopt is false or different error
|
|
385
|
+
throw error;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
313
388
|
|
|
314
389
|
return this({
|
|
315
390
|
id: application.id,
|
|
@@ -321,7 +396,7 @@ export const ContainerApplication = Resource(
|
|
|
321
396
|
|
|
322
397
|
export interface ContainerApplicationData {
|
|
323
398
|
name: string;
|
|
324
|
-
scheduling_policy:
|
|
399
|
+
scheduling_policy: SchedulingPolicy;
|
|
325
400
|
instances: number;
|
|
326
401
|
max_instances: number;
|
|
327
402
|
constraints: {
|
|
@@ -377,6 +452,14 @@ export async function listContainerApplications(
|
|
|
377
452
|
);
|
|
378
453
|
}
|
|
379
454
|
|
|
455
|
+
export async function findContainerApplicationByName(
|
|
456
|
+
api: CloudflareApi,
|
|
457
|
+
name: string,
|
|
458
|
+
): Promise<ContainerApplicationData | undefined> {
|
|
459
|
+
const applications = await listContainerApplications(api);
|
|
460
|
+
return applications.find((app) => app.name === name);
|
|
461
|
+
}
|
|
462
|
+
|
|
380
463
|
export interface CreateContainerApplicationBody {
|
|
381
464
|
name: string;
|
|
382
465
|
max_instances: number;
|
|
@@ -492,12 +575,14 @@ export async function deleteContainerApplication(
|
|
|
492
575
|
`/accounts/${api.accountId}/containers/applications/${applicationId}`,
|
|
493
576
|
);
|
|
494
577
|
const result = (await response.json()) as any;
|
|
495
|
-
|
|
496
|
-
|
|
578
|
+
// Treat missing applications as already-deleted so that destroy() is idempotent
|
|
579
|
+
if (response.ok || response.status === 404) {
|
|
580
|
+
return result?.result;
|
|
497
581
|
}
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
582
|
+
const errorMessages = Array.isArray(result?.errors)
|
|
583
|
+
? result.errors.map((e: { message: string }) => e.message).join(", ")
|
|
584
|
+
: (result?.error ?? "Unknown error");
|
|
585
|
+
throw Error(`Failed to delete container application: ${errorMessages}`);
|
|
501
586
|
}
|
|
502
587
|
|
|
503
588
|
interface CreateRolloutApplicationRequest {
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type AssetsConfig,
|
|
11
11
|
type WorkerProps,
|
|
12
12
|
} from "./worker.ts";
|
|
13
|
-
import { WranglerJson } from "./wrangler.json.ts";
|
|
13
|
+
import { WranglerJson, type WranglerJsonSpec } from "./wrangler.json.ts";
|
|
14
14
|
|
|
15
15
|
export interface WebsiteProps<B extends Bindings>
|
|
16
16
|
extends Omit<WorkerProps<B>, "name" | "assets" | "entrypoint"> {
|
|
@@ -102,6 +102,25 @@ export interface WebsiteProps<B extends Bindings>
|
|
|
102
102
|
command: string;
|
|
103
103
|
url: string;
|
|
104
104
|
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Transform hooks to modify generated configuration files
|
|
108
|
+
*/
|
|
109
|
+
transform?: {
|
|
110
|
+
/**
|
|
111
|
+
* Hook to modify the wrangler.json object before it's written
|
|
112
|
+
*
|
|
113
|
+
* This function receives the generated wrangler.json spec and should return
|
|
114
|
+
* a modified version. It's applied as the final transformation before the
|
|
115
|
+
* file is written to disk.
|
|
116
|
+
*
|
|
117
|
+
* @param spec - The generated wrangler.json specification
|
|
118
|
+
* @returns The modified wrangler.json specification
|
|
119
|
+
*/
|
|
120
|
+
wrangler?: (
|
|
121
|
+
spec: WranglerJsonSpec,
|
|
122
|
+
) => WranglerJsonSpec | Promise<WranglerJsonSpec>;
|
|
123
|
+
};
|
|
105
124
|
}
|
|
106
125
|
|
|
107
126
|
export type Website<B extends Bindings> = B extends { ASSETS: any }
|
|
@@ -193,6 +212,7 @@ export default {
|
|
|
193
212
|
// path must be relative to the wrangler.jsonc file
|
|
194
213
|
directory: path.relative(wranglerDir, assetsDirPath),
|
|
195
214
|
},
|
|
215
|
+
transform: props.transform,
|
|
196
216
|
});
|
|
197
217
|
}
|
|
198
218
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
MiniflareCoreError,
|
|
3
|
+
type Miniflare,
|
|
4
|
+
type MiniflareOptions,
|
|
5
|
+
type RemoteProxyConnectionString,
|
|
6
|
+
type WorkerOptions,
|
|
6
7
|
} from "miniflare";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
import { findOpenPort } from "../../util/find-open-port.ts";
|
|
@@ -66,7 +67,9 @@ class MiniflareServer {
|
|
|
66
67
|
}),
|
|
67
68
|
);
|
|
68
69
|
if (this.miniflare) {
|
|
69
|
-
await
|
|
70
|
+
await withErrorRewrite(
|
|
71
|
+
this.miniflare.setOptions(await this.miniflareOptions()),
|
|
72
|
+
);
|
|
70
73
|
} else {
|
|
71
74
|
const { Miniflare } = await import("miniflare").catch(() => {
|
|
72
75
|
throw new Error(
|
|
@@ -81,9 +84,8 @@ class MiniflareServer {
|
|
|
81
84
|
process.exit(0);
|
|
82
85
|
}
|
|
83
86
|
});
|
|
84
|
-
|
|
85
87
|
this.miniflare = new Miniflare(await this.miniflareOptions());
|
|
86
|
-
await this.miniflare.ready;
|
|
88
|
+
await withErrorRewrite(this.miniflare.ready);
|
|
87
89
|
}
|
|
88
90
|
const existing = this.servers.get(worker.name);
|
|
89
91
|
if (existing) {
|
|
@@ -191,6 +193,29 @@ class MiniflareServer {
|
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
|
|
196
|
+
export class ExternalDependencyError extends Error {
|
|
197
|
+
constructor() {
|
|
198
|
+
super(
|
|
199
|
+
'Miniflare detected an external dependency that could not be resolved. This typically occurs when the "nodejs_compat" or "nodejs_als" compatibility flag is not enabled.',
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function withErrorRewrite<T>(promise: Promise<T>) {
|
|
205
|
+
try {
|
|
206
|
+
return await promise;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (
|
|
209
|
+
error instanceof MiniflareCoreError &&
|
|
210
|
+
error.code === "ERR_MODULE_STRING_SCRIPT"
|
|
211
|
+
) {
|
|
212
|
+
throw new ExternalDependencyError();
|
|
213
|
+
} else {
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
194
219
|
declare global {
|
|
195
220
|
var _ALCHEMY_MINIFLARE_SERVER: MiniflareServer | undefined;
|
|
196
221
|
}
|
|
@@ -351,6 +351,21 @@ export async function prepareWorkerMetadata(
|
|
|
351
351
|
|
|
352
352
|
const bindings = (props.bindings ?? {}) as Bindings;
|
|
353
353
|
|
|
354
|
+
// Validate that all Container and DurableObjectNamespace bindings have unique IDs
|
|
355
|
+
const bindingIds = new Map<string, string>();
|
|
356
|
+
for (const [bindingName, binding] of Object.entries(bindings)) {
|
|
357
|
+
if (typeof binding === "object" && binding !== null && "id" in binding) {
|
|
358
|
+
if (isDurableObjectNamespace(binding) || isContainer(binding)) {
|
|
359
|
+
if (bindingIds.has(binding.id)) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`Duplicate binding ID '${binding.id}' found for bindings '${bindingIds.get(binding.id)}' and '${bindingName}'. Container and DurableObjectNamespace bindings must have unique IDs.`,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
bindingIds.set(binding.id, bindingName);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
354
369
|
// Convert bindings to the format expected by the API
|
|
355
370
|
for (const [bindingName, binding] of Object.entries(bindings)) {
|
|
356
371
|
// Create a copy of the binding to avoid modifying the original
|
|
@@ -47,6 +47,25 @@ export interface WranglerJsonProps {
|
|
|
47
47
|
binding: string;
|
|
48
48
|
directory: string;
|
|
49
49
|
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Transform hooks to modify generated configuration files
|
|
53
|
+
*/
|
|
54
|
+
transform?: {
|
|
55
|
+
/**
|
|
56
|
+
* Hook to modify the wrangler.json object before it's written
|
|
57
|
+
*
|
|
58
|
+
* This function receives the generated wrangler.json spec and should return
|
|
59
|
+
* a modified version. It's applied as the final transformation before the
|
|
60
|
+
* file is written to disk.
|
|
61
|
+
*
|
|
62
|
+
* @param spec - The generated wrangler.json specification
|
|
63
|
+
* @returns The modified wrangler.json specification
|
|
64
|
+
*/
|
|
65
|
+
wrangler?: (
|
|
66
|
+
spec: WranglerJsonSpec,
|
|
67
|
+
) => WranglerJsonSpec | Promise<WranglerJsonSpec>;
|
|
68
|
+
};
|
|
50
69
|
}
|
|
51
70
|
|
|
52
71
|
/**
|
|
@@ -156,14 +175,19 @@ export const WranglerJson = Resource(
|
|
|
156
175
|
spec.assets.directory = path.relative(dirname, spec.assets.directory);
|
|
157
176
|
}
|
|
158
177
|
|
|
178
|
+
// Apply the wrangler configuration hook as the final transformation
|
|
179
|
+
const finalSpec = props.transform?.wrangler
|
|
180
|
+
? await props.transform.wrangler(spec)
|
|
181
|
+
: spec;
|
|
182
|
+
|
|
159
183
|
await fs.mkdir(dirname, { recursive: true });
|
|
160
|
-
await fs.writeFile(filePath, await formatJson(
|
|
184
|
+
await fs.writeFile(filePath, await formatJson(finalSpec));
|
|
161
185
|
|
|
162
186
|
// Return the resource
|
|
163
187
|
return this({
|
|
164
188
|
...props,
|
|
165
189
|
path: path.relative(cwd, filePath),
|
|
166
|
-
spec,
|
|
190
|
+
spec: finalSpec,
|
|
167
191
|
createdAt: Date.now(),
|
|
168
192
|
updatedAt: Date.now(),
|
|
169
193
|
});
|
|
@@ -216,6 +240,7 @@ export interface WranglerJsonSpec {
|
|
|
216
240
|
*/
|
|
217
241
|
ai?: {
|
|
218
242
|
binding: string;
|
|
243
|
+
experimental_remote?: boolean;
|
|
219
244
|
};
|
|
220
245
|
|
|
221
246
|
/**
|
|
@@ -223,6 +248,7 @@ export interface WranglerJsonSpec {
|
|
|
223
248
|
*/
|
|
224
249
|
browser?: {
|
|
225
250
|
binding: string;
|
|
251
|
+
experimental_remote?: boolean;
|
|
226
252
|
};
|
|
227
253
|
|
|
228
254
|
/**
|
|
@@ -230,6 +256,7 @@ export interface WranglerJsonSpec {
|
|
|
230
256
|
*/
|
|
231
257
|
images?: {
|
|
232
258
|
binding: string;
|
|
259
|
+
experimental_remote?: boolean;
|
|
233
260
|
};
|
|
234
261
|
|
|
235
262
|
/**
|
|
@@ -242,6 +269,7 @@ export interface WranglerJsonSpec {
|
|
|
242
269
|
* The ID of the KV namespace used during `wrangler dev`
|
|
243
270
|
*/
|
|
244
271
|
preview_id?: string;
|
|
272
|
+
experimental_remote?: boolean;
|
|
245
273
|
}[];
|
|
246
274
|
|
|
247
275
|
/**
|
|
@@ -266,6 +294,7 @@ export interface WranglerJsonSpec {
|
|
|
266
294
|
* The preview name of this R2 bucket at the edge.
|
|
267
295
|
*/
|
|
268
296
|
preview_bucket_name?: string;
|
|
297
|
+
experimental_remote?: boolean;
|
|
269
298
|
}[];
|
|
270
299
|
|
|
271
300
|
/**
|
|
@@ -308,6 +337,7 @@ export interface WranglerJsonSpec {
|
|
|
308
337
|
vectorize?: {
|
|
309
338
|
binding: string;
|
|
310
339
|
index_name: string;
|
|
340
|
+
experimental_remote?: boolean;
|
|
311
341
|
}[];
|
|
312
342
|
|
|
313
343
|
/**
|
|
@@ -327,6 +357,7 @@ export interface WranglerJsonSpec {
|
|
|
327
357
|
* The ID of the D1 database used during `wrangler dev`
|
|
328
358
|
*/
|
|
329
359
|
preview_database_id?: string;
|
|
360
|
+
experimental_remote?: boolean;
|
|
330
361
|
}[];
|
|
331
362
|
|
|
332
363
|
/**
|
|
@@ -398,6 +429,7 @@ export interface WranglerJsonSpec {
|
|
|
398
429
|
dispatch_namespaces?: {
|
|
399
430
|
binding: string;
|
|
400
431
|
namespace: string;
|
|
432
|
+
experimental_remote?: boolean;
|
|
401
433
|
}[];
|
|
402
434
|
}
|
|
403
435
|
|
|
@@ -412,8 +444,12 @@ function processBindings(
|
|
|
412
444
|
workerCwd: string,
|
|
413
445
|
): void {
|
|
414
446
|
// Arrays to collect different binding types
|
|
415
|
-
const kvNamespaces: {
|
|
416
|
-
|
|
447
|
+
const kvNamespaces: {
|
|
448
|
+
binding: string;
|
|
449
|
+
id: string;
|
|
450
|
+
preview_id: string;
|
|
451
|
+
experimental_remote?: boolean;
|
|
452
|
+
}[] = [];
|
|
417
453
|
const durableObjects: {
|
|
418
454
|
name: string;
|
|
419
455
|
class_name: string;
|
|
@@ -440,6 +476,7 @@ function processBindings(
|
|
|
440
476
|
database_name: string;
|
|
441
477
|
migrations_dir?: string;
|
|
442
478
|
preview_database_id: string;
|
|
479
|
+
experimental_remote?: boolean;
|
|
443
480
|
}[] = [];
|
|
444
481
|
const queues: {
|
|
445
482
|
producers: { queue: string; binding: string }[];
|
|
@@ -459,7 +496,11 @@ function processBindings(
|
|
|
459
496
|
const new_sqlite_classes: string[] = [];
|
|
460
497
|
const new_classes: string[] = [];
|
|
461
498
|
|
|
462
|
-
const vectorizeIndexes: {
|
|
499
|
+
const vectorizeIndexes: {
|
|
500
|
+
binding: string;
|
|
501
|
+
index_name: string;
|
|
502
|
+
experimental_remote?: boolean;
|
|
503
|
+
}[] = [];
|
|
463
504
|
const analyticsEngineDatasets: { binding: string; dataset: string }[] = [];
|
|
464
505
|
const hyperdrive: {
|
|
465
506
|
binding: string;
|
|
@@ -475,6 +516,7 @@ function processBindings(
|
|
|
475
516
|
const dispatchNamespaces: {
|
|
476
517
|
binding: string;
|
|
477
518
|
namespace: string;
|
|
519
|
+
experimental_remote?: boolean;
|
|
478
520
|
}[] = [];
|
|
479
521
|
const containers: {
|
|
480
522
|
class_name: string;
|
|
@@ -527,6 +569,9 @@ function processBindings(
|
|
|
527
569
|
binding: bindingName,
|
|
528
570
|
id: id,
|
|
529
571
|
preview_id: id,
|
|
572
|
+
...("dev" in binding && binding.dev?.remote
|
|
573
|
+
? { experimental_remote: true }
|
|
574
|
+
: {}),
|
|
530
575
|
});
|
|
531
576
|
} else if (
|
|
532
577
|
typeof binding === "object" &&
|
|
@@ -550,6 +595,7 @@ function processBindings(
|
|
|
550
595
|
binding: bindingName,
|
|
551
596
|
bucket_name: binding.name,
|
|
552
597
|
preview_bucket_name: binding.name,
|
|
598
|
+
...(binding.dev?.remote ? { experimental_remote: true } : {}),
|
|
553
599
|
});
|
|
554
600
|
} else if (binding.type === "secret") {
|
|
555
601
|
// Secret binding
|
|
@@ -573,6 +619,7 @@ function processBindings(
|
|
|
573
619
|
database_name: binding.name,
|
|
574
620
|
migrations_dir: binding.migrationsDir,
|
|
575
621
|
preview_database_id: binding.id,
|
|
622
|
+
...(binding.dev?.remote ? { experimental_remote: true } : {}),
|
|
576
623
|
});
|
|
577
624
|
} else if (binding.type === "queue") {
|
|
578
625
|
queues.producers.push({
|
|
@@ -583,6 +630,8 @@ function processBindings(
|
|
|
583
630
|
vectorizeIndexes.push({
|
|
584
631
|
binding: bindingName,
|
|
585
632
|
index_name: binding.name,
|
|
633
|
+
// https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
|
|
634
|
+
experimental_remote: true,
|
|
586
635
|
});
|
|
587
636
|
} else if (binding.type === "browser") {
|
|
588
637
|
if (spec.browser) {
|
|
@@ -590,6 +639,8 @@ function processBindings(
|
|
|
590
639
|
}
|
|
591
640
|
spec.browser = {
|
|
592
641
|
binding: bindingName,
|
|
642
|
+
// https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
|
|
643
|
+
experimental_remote: true,
|
|
593
644
|
};
|
|
594
645
|
} else if (binding.type === "ai") {
|
|
595
646
|
if (spec.ai) {
|
|
@@ -597,6 +648,8 @@ function processBindings(
|
|
|
597
648
|
}
|
|
598
649
|
spec.ai = {
|
|
599
650
|
binding: bindingName,
|
|
651
|
+
// https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
|
|
652
|
+
experimental_remote: true,
|
|
600
653
|
};
|
|
601
654
|
} else if (binding.type === "images") {
|
|
602
655
|
if (spec.images) {
|
|
@@ -604,6 +657,8 @@ function processBindings(
|
|
|
604
657
|
}
|
|
605
658
|
spec.images = {
|
|
606
659
|
binding: bindingName,
|
|
660
|
+
// https://developers.cloudflare.com/workers/development-testing/#recommended-remote-bindings
|
|
661
|
+
experimental_remote: true,
|
|
607
662
|
};
|
|
608
663
|
} else if (binding.type === "analytics_engine") {
|
|
609
664
|
analyticsEngineDatasets.push({
|
|
@@ -648,6 +703,7 @@ function processBindings(
|
|
|
648
703
|
dispatchNamespaces.push({
|
|
649
704
|
binding: bindingName,
|
|
650
705
|
namespace: binding.namespaceName,
|
|
706
|
+
experimental_remote: true,
|
|
651
707
|
});
|
|
652
708
|
} else if (binding.type === "secret_key") {
|
|
653
709
|
// no-op
|
package/src/context.ts
CHANGED
|
@@ -46,6 +46,10 @@ export interface BaseContext<Out extends Resource> {
|
|
|
46
46
|
id: ResourceID;
|
|
47
47
|
fqn: ResourceFQN;
|
|
48
48
|
scope: Scope;
|
|
49
|
+
/**
|
|
50
|
+
* Indicates whether this resource is being created as a replacement for another resource
|
|
51
|
+
*/
|
|
52
|
+
isReplacement: boolean;
|
|
49
53
|
get<T>(key: string): Promise<T | undefined>;
|
|
50
54
|
set<T>(key: string, value: T): Promise<void>;
|
|
51
55
|
delete<T>(key: string): Promise<T | undefined>;
|
|
@@ -88,6 +92,7 @@ export function context<
|
|
|
88
92
|
state,
|
|
89
93
|
replace,
|
|
90
94
|
props,
|
|
95
|
+
isReplacement = false,
|
|
91
96
|
}: {
|
|
92
97
|
scope: Scope;
|
|
93
98
|
phase: "create" | "update" | "delete";
|
|
@@ -98,6 +103,7 @@ export function context<
|
|
|
98
103
|
props: Props;
|
|
99
104
|
state: State<Kind, Props, Out>;
|
|
100
105
|
replace: () => never;
|
|
106
|
+
isReplacement?: boolean;
|
|
101
107
|
}): Context<Out> {
|
|
102
108
|
type InternalSymbols =
|
|
103
109
|
| typeof ResourceID
|
|
@@ -133,6 +139,7 @@ export function context<
|
|
|
133
139
|
output: state.output,
|
|
134
140
|
props,
|
|
135
141
|
replace,
|
|
142
|
+
isReplacement,
|
|
136
143
|
get: (key: string) => state.data[key],
|
|
137
144
|
set: async (key: string, value: any) => {
|
|
138
145
|
state.data[key] = value;
|
package/src/destroy.ts
CHANGED
|
@@ -119,6 +119,7 @@ export async function destroy<Type extends string>(
|
|
|
119
119
|
seq: instance[ResourceSeq],
|
|
120
120
|
props: options?.replace?.props ?? state.props,
|
|
121
121
|
state,
|
|
122
|
+
isReplacement: false,
|
|
122
123
|
replace: () => {
|
|
123
124
|
throw new Error("Cannot replace a resource that is being deleted");
|
|
124
125
|
},
|