alchemy 0.57.2 → 0.59.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.js +1 -1
- package/lib/apply.js +3 -3
- package/lib/apply.js.map +1 -1
- package/lib/cloudflare/api-error.js +1 -1
- package/lib/cloudflare/api-error.js.map +1 -1
- package/lib/cloudflare/api-response.d.ts +19 -0
- package/lib/cloudflare/api-response.d.ts.map +1 -1
- package/lib/cloudflare/api-response.js.map +1 -1
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +10 -2
- package/lib/cloudflare/api.js.map +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 +21 -3
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +83 -53
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
- package/lib/cloudflare/compatibility-date.gen.js +1 -1
- package/lib/cloudflare/container.d.ts +8 -0
- package/lib/cloudflare/container.d.ts.map +1 -1
- package/lib/cloudflare/container.js +7 -1
- package/lib/cloudflare/container.js.map +1 -1
- package/lib/cloudflare/custom-domain.d.ts +8 -0
- package/lib/cloudflare/custom-domain.d.ts.map +1 -1
- package/lib/cloudflare/custom-domain.js +11 -0
- package/lib/cloudflare/custom-domain.js.map +1 -1
- package/lib/cloudflare/d1-database.d.ts +15 -1
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +24 -17
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/d1-import.d.ts.map +1 -1
- package/lib/cloudflare/d1-import.js.map +1 -1
- package/lib/cloudflare/hyperdrive.d.ts +37 -9
- package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
- package/lib/cloudflare/hyperdrive.js +91 -5
- package/lib/cloudflare/hyperdrive.js.map +1 -1
- package/lib/cloudflare/index.d.ts +1 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +1 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +23 -4
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js +70 -58
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts +2 -0
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +9 -7
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/delete.d.ts +21 -0
- package/lib/cloudflare/miniflare/delete.d.ts.map +1 -0
- package/lib/cloudflare/miniflare/delete.js +91 -0
- package/lib/cloudflare/miniflare/delete.js.map +1 -0
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +26 -9
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts +2 -0
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.js +3 -4
- package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
- package/lib/cloudflare/pipeline.d.ts +3 -0
- package/lib/cloudflare/pipeline.d.ts.map +1 -1
- package/lib/cloudflare/pipeline.js +14 -1
- package/lib/cloudflare/pipeline.js.map +1 -1
- package/lib/cloudflare/queue-consumer.d.ts +8 -0
- package/lib/cloudflare/queue-consumer.d.ts.map +1 -1
- package/lib/cloudflare/queue-consumer.js +15 -4
- package/lib/cloudflare/queue-consumer.js.map +1 -1
- package/lib/cloudflare/queue.d.ts +22 -4
- package/lib/cloudflare/queue.d.ts.map +1 -1
- package/lib/cloudflare/queue.js +32 -9
- package/lib/cloudflare/queue.js.map +1 -1
- package/lib/cloudflare/route.d.ts +8 -0
- package/lib/cloudflare/route.d.ts.map +1 -1
- package/lib/cloudflare/route.js +13 -0
- package/lib/cloudflare/route.js.map +1 -1
- package/lib/cloudflare/tunnel.d.ts +448 -0
- package/lib/cloudflare/tunnel.d.ts.map +1 -0
- package/lib/cloudflare/tunnel.js +467 -0
- package/lib/cloudflare/tunnel.js.map +1 -0
- package/lib/cloudflare/worker-subdomain.d.ts +8 -0
- package/lib/cloudflare/worker-subdomain.d.ts.map +1 -1
- package/lib/cloudflare/worker-subdomain.js +9 -1
- package/lib/cloudflare/worker-subdomain.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +7 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +405 -375
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +1 -1
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +24 -12
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/destroy.d.ts.map +1 -1
- package/lib/destroy.js +4 -8
- package/lib/destroy.js.map +1 -1
- package/lib/esbuild/bundle.d.ts +1 -1
- package/lib/secret.d.ts +18 -1
- package/lib/secret.d.ts.map +1 -1
- package/lib/secret.js +25 -0
- package/lib/secret.js.map +1 -1
- package/package.json +1 -1
- package/src/apply.ts +5 -6
- package/src/cloudflare/api-error.ts +2 -2
- package/src/cloudflare/api-response.ts +22 -0
- package/src/cloudflare/api.ts +13 -2
- package/src/cloudflare/bucket.ts +126 -68
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/container.ts +18 -1
- package/src/cloudflare/custom-domain.ts +22 -0
- package/src/cloudflare/d1-database.ts +44 -19
- package/src/cloudflare/d1-import.ts +1 -14
- package/src/cloudflare/hyperdrive.ts +170 -14
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/kv-namespace.ts +117 -73
- package/src/cloudflare/miniflare/build-worker-options.ts +12 -8
- package/src/cloudflare/miniflare/delete.ts +136 -0
- package/src/cloudflare/miniflare/miniflare-controller.ts +26 -9
- package/src/cloudflare/miniflare/remote-binding-proxy.ts +5 -4
- package/src/cloudflare/pipeline.ts +17 -1
- package/src/cloudflare/queue-consumer.ts +27 -5
- package/src/cloudflare/queue.ts +62 -12
- package/src/cloudflare/route.ts +24 -0
- package/src/cloudflare/tunnel.ts +911 -0
- package/src/cloudflare/worker-subdomain.ts +29 -6
- package/src/cloudflare/worker.ts +484 -465
- package/src/cloudflare/wrangler.json.ts +34 -13
- package/src/destroy.ts +4 -8
- package/src/secret.ts +30 -1
- package/templates/astro/README.md +2 -0
- package/templates/nuxt/README.md +2 -0
- package/templates/react-router/README.md +2 -0
- package/templates/rwsdk/README.md +2 -0
- package/templates/sveltekit/README.md +2 -0
- package/templates/tanstack-start/README.md +2 -0
- package/templates/typescript/README.md +2 -0
- package/templates/vite/README.md +2 -0
package/src/cloudflare/bucket.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
2
|
import type { Context } from "../context.ts";
|
|
3
3
|
import { Resource, ResourceKind } from "../resource.ts";
|
|
4
|
+
import { Scope } from "../scope.ts";
|
|
4
5
|
import { withExponentialBackoff } from "../util/retry.ts";
|
|
5
6
|
import { CloudflareApiError } from "./api-error.ts";
|
|
6
7
|
import {
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
type CloudflareApi,
|
|
13
14
|
type CloudflareApiOptions,
|
|
14
15
|
} from "./api.ts";
|
|
16
|
+
import { deleteMiniflareBinding } from "./miniflare/delete.ts";
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Properties for creating or updating an R2 Bucket
|
|
@@ -83,6 +85,12 @@ export interface BucketProps extends CloudflareApiOptions {
|
|
|
83
85
|
* @default false
|
|
84
86
|
*/
|
|
85
87
|
remote?: boolean;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Set when `Scope.local` is true to force update to the bucket even if it was already deployed live.
|
|
91
|
+
* @internal
|
|
92
|
+
*/
|
|
93
|
+
force?: boolean;
|
|
86
94
|
};
|
|
87
95
|
}
|
|
88
96
|
|
|
@@ -127,7 +135,7 @@ interface R2BucketCORSRule {
|
|
|
127
135
|
* Output returned after R2 Bucket creation/update
|
|
128
136
|
*/
|
|
129
137
|
export type R2Bucket = Resource<"cloudflare::R2Bucket"> &
|
|
130
|
-
Omit<BucketProps, "delete"> & {
|
|
138
|
+
Omit<BucketProps, "delete" | "dev"> & {
|
|
131
139
|
/**
|
|
132
140
|
* Resource type identifier
|
|
133
141
|
*/
|
|
@@ -152,6 +160,22 @@ export type R2Bucket = Resource<"cloudflare::R2Bucket"> &
|
|
|
152
160
|
* The `r2.dev` subdomain for the bucket, if `allowPublicAccess` is true
|
|
153
161
|
*/
|
|
154
162
|
domain: string | undefined;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Development mode properties
|
|
166
|
+
* @internal
|
|
167
|
+
*/
|
|
168
|
+
dev: {
|
|
169
|
+
/**
|
|
170
|
+
* The ID of the bucket in development mode
|
|
171
|
+
*/
|
|
172
|
+
id: string;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Whether the bucket is running remotely
|
|
176
|
+
*/
|
|
177
|
+
remote: boolean;
|
|
178
|
+
};
|
|
155
179
|
};
|
|
156
180
|
|
|
157
181
|
export function isBucket(resource: Resource): resource is R2Bucket {
|
|
@@ -202,88 +226,122 @@ export function isBucket(resource: Resource): resource is R2Bucket {
|
|
|
202
226
|
*
|
|
203
227
|
* @see https://developers.cloudflare.com/r2/buckets/
|
|
204
228
|
*/
|
|
205
|
-
export
|
|
229
|
+
export async function R2Bucket(
|
|
230
|
+
id: string,
|
|
231
|
+
props: BucketProps = {},
|
|
232
|
+
): Promise<R2Bucket> {
|
|
233
|
+
return await _R2Bucket(id, {
|
|
234
|
+
...props,
|
|
235
|
+
dev: {
|
|
236
|
+
...(props.dev ?? {}),
|
|
237
|
+
force: Scope.current.local,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const _R2Bucket = Resource(
|
|
206
243
|
"cloudflare::R2Bucket",
|
|
207
244
|
async function (
|
|
208
245
|
this: Context<R2Bucket>,
|
|
209
|
-
|
|
246
|
+
id: string,
|
|
210
247
|
props: BucketProps = {},
|
|
211
248
|
): Promise<R2Bucket> {
|
|
212
|
-
const
|
|
213
|
-
const bucketName = props.name || this.id;
|
|
249
|
+
const bucketName = props.name || id;
|
|
214
250
|
const allowPublicAccess = props.allowPublicAccess === true;
|
|
251
|
+
const dev = {
|
|
252
|
+
id: this.output?.dev?.id ?? bucketName,
|
|
253
|
+
remote: props.dev?.remote ?? false,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
if (this.scope.local && !props.dev?.remote) {
|
|
257
|
+
return this({
|
|
258
|
+
name: this.output?.name ?? "",
|
|
259
|
+
location: this.output?.location ?? "",
|
|
260
|
+
creationDate: this.output?.creationDate ?? new Date(),
|
|
261
|
+
jurisdiction: this.output?.jurisdiction ?? "default",
|
|
262
|
+
allowPublicAccess,
|
|
263
|
+
domain: this.output?.domain,
|
|
264
|
+
type: "r2_bucket",
|
|
265
|
+
accountId: this.output?.accountId ?? "",
|
|
266
|
+
cors: props.cors,
|
|
267
|
+
dev,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const api = await createCloudflareApi(props);
|
|
272
|
+
|
|
273
|
+
if (this.phase === "delete") {
|
|
274
|
+
if (props.delete !== false) {
|
|
275
|
+
if (this.output.dev?.id) {
|
|
276
|
+
await deleteMiniflareBinding("r2", this.output.dev.id);
|
|
277
|
+
}
|
|
278
|
+
if (props.empty) {
|
|
279
|
+
await emptyBucket(api, bucketName, props);
|
|
280
|
+
}
|
|
281
|
+
await deleteBucket(api, bucketName, props);
|
|
282
|
+
}
|
|
283
|
+
return this.destroy();
|
|
284
|
+
}
|
|
215
285
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
286
|
+
if (this.phase === "create" || !this.output?.name) {
|
|
287
|
+
const bucket = await createBucket(api, bucketName, props).catch(
|
|
288
|
+
async (err) => {
|
|
289
|
+
if (
|
|
290
|
+
err instanceof CloudflareApiError &&
|
|
291
|
+
err.status === 409 &&
|
|
292
|
+
props.adopt
|
|
293
|
+
) {
|
|
294
|
+
return await getBucket(api, bucketName, props);
|
|
295
|
+
}
|
|
296
|
+
throw err;
|
|
297
|
+
},
|
|
298
|
+
);
|
|
299
|
+
const domain = await putManagedDomain(
|
|
300
|
+
api,
|
|
301
|
+
bucketName,
|
|
302
|
+
allowPublicAccess,
|
|
303
|
+
props.jurisdiction,
|
|
304
|
+
);
|
|
305
|
+
if (props.cors?.length) {
|
|
306
|
+
await putBucketCORS(api, bucketName, props);
|
|
307
|
+
}
|
|
308
|
+
return this({
|
|
309
|
+
name: bucketName,
|
|
310
|
+
location: bucket.location,
|
|
311
|
+
creationDate: new Date(bucket.creation_date),
|
|
312
|
+
jurisdiction: bucket.jurisdiction,
|
|
313
|
+
allowPublicAccess,
|
|
314
|
+
domain,
|
|
315
|
+
type: "r2_bucket",
|
|
316
|
+
accountId: api.accountId,
|
|
317
|
+
cors: props.cors,
|
|
318
|
+
dev,
|
|
319
|
+
});
|
|
320
|
+
} else {
|
|
321
|
+
if (bucketName !== this.output.name) {
|
|
322
|
+
throw new Error(
|
|
323
|
+
`Cannot update R2Bucket name after creation. Bucket name is immutable. Before: ${this.output.name}, After: ${bucketName}`,
|
|
229
324
|
);
|
|
230
|
-
|
|
325
|
+
}
|
|
326
|
+
let domain = this.output.domain;
|
|
327
|
+
if (!!domain !== allowPublicAccess) {
|
|
328
|
+
domain = await putManagedDomain(
|
|
231
329
|
api,
|
|
232
330
|
bucketName,
|
|
233
331
|
allowPublicAccess,
|
|
234
332
|
props.jurisdiction,
|
|
235
333
|
);
|
|
236
|
-
if (props.cors?.length) {
|
|
237
|
-
await putBucketCORS(api, bucketName, props);
|
|
238
|
-
}
|
|
239
|
-
return this({
|
|
240
|
-
name: bucketName,
|
|
241
|
-
location: bucket.location,
|
|
242
|
-
creationDate: new Date(bucket.creation_date),
|
|
243
|
-
jurisdiction: bucket.jurisdiction,
|
|
244
|
-
allowPublicAccess,
|
|
245
|
-
domain,
|
|
246
|
-
type: "r2_bucket",
|
|
247
|
-
accountId: api.accountId,
|
|
248
|
-
cors: props.cors,
|
|
249
|
-
dev: props.dev,
|
|
250
|
-
});
|
|
251
334
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
throw new Error(
|
|
255
|
-
`Cannot update R2Bucket name after creation. Bucket name is immutable. Before: ${this.output.name}, After: ${bucketName}`,
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
let domain = this.output.domain;
|
|
259
|
-
if (!!domain !== allowPublicAccess) {
|
|
260
|
-
domain = await putManagedDomain(
|
|
261
|
-
api,
|
|
262
|
-
bucketName,
|
|
263
|
-
allowPublicAccess,
|
|
264
|
-
props.jurisdiction,
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
if (!isDeepStrictEqual(this.output.cors ?? [], props.cors ?? [])) {
|
|
268
|
-
await putBucketCORS(api, bucketName, props);
|
|
269
|
-
}
|
|
270
|
-
return this({
|
|
271
|
-
...this.output,
|
|
272
|
-
allowPublicAccess,
|
|
273
|
-
dev: props.dev,
|
|
274
|
-
cors: props.cors,
|
|
275
|
-
domain,
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
case "delete": {
|
|
279
|
-
if (props.delete !== false) {
|
|
280
|
-
if (props.empty) {
|
|
281
|
-
await emptyBucket(api, bucketName, props);
|
|
282
|
-
}
|
|
283
|
-
await deleteBucket(api, bucketName, props);
|
|
284
|
-
}
|
|
285
|
-
return this.destroy();
|
|
335
|
+
if (!isDeepStrictEqual(this.output.cors ?? [], props.cors ?? [])) {
|
|
336
|
+
await putBucketCORS(api, bucketName, props);
|
|
286
337
|
}
|
|
338
|
+
return this({
|
|
339
|
+
...this.output,
|
|
340
|
+
allowPublicAccess,
|
|
341
|
+
dev,
|
|
342
|
+
cors: props.cors,
|
|
343
|
+
domain,
|
|
344
|
+
});
|
|
287
345
|
}
|
|
288
346
|
},
|
|
289
347
|
);
|
|
@@ -368,6 +368,15 @@ export interface ContainerApplicationProps extends CloudflareApiOptions {
|
|
|
368
368
|
* @default false
|
|
369
369
|
*/
|
|
370
370
|
adopt?: boolean;
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* If true, the container application will not be created, but will be retained if it already exists.
|
|
374
|
+
* This is used for local development.
|
|
375
|
+
*
|
|
376
|
+
* @default `false`
|
|
377
|
+
* @internal
|
|
378
|
+
*/
|
|
379
|
+
dev?: boolean;
|
|
371
380
|
}
|
|
372
381
|
|
|
373
382
|
/**
|
|
@@ -495,6 +504,13 @@ export const ContainerApplication = Resource(
|
|
|
495
504
|
_id: string,
|
|
496
505
|
props: ContainerApplicationProps,
|
|
497
506
|
): Promise<ContainerApplication> {
|
|
507
|
+
if (this.scope.local && props.dev) {
|
|
508
|
+
return this({
|
|
509
|
+
id: this.output?.id ?? "",
|
|
510
|
+
name: props.name,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
498
514
|
const api = await createCloudflareApi(props);
|
|
499
515
|
if (this.phase === "delete") {
|
|
500
516
|
if (this.output?.id) {
|
|
@@ -503,6 +519,7 @@ export const ContainerApplication = Resource(
|
|
|
503
519
|
}
|
|
504
520
|
return this.destroy();
|
|
505
521
|
}
|
|
522
|
+
|
|
506
523
|
// Prefer the immutable repo digest if present. Falls back to the tag reference.
|
|
507
524
|
const imageReference = props.image.repoDigest ?? props.image.imageRef;
|
|
508
525
|
|
|
@@ -518,7 +535,7 @@ export const ContainerApplication = Resource(
|
|
|
518
535
|
},
|
|
519
536
|
},
|
|
520
537
|
};
|
|
521
|
-
if (this.phase === "update") {
|
|
538
|
+
if (this.phase === "update" && this.output?.id) {
|
|
522
539
|
const application = await updateContainerApplication(
|
|
523
540
|
api,
|
|
524
541
|
this.output.id,
|
|
@@ -43,6 +43,15 @@ export interface CustomDomainProps extends CloudflareApiOptions {
|
|
|
43
43
|
* @default false
|
|
44
44
|
*/
|
|
45
45
|
adopt?: boolean;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* If true, the custom domain will not be created, but will be retained if it already exists.
|
|
49
|
+
* This is used for local development.
|
|
50
|
+
*
|
|
51
|
+
* @default `Scope.local`
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
dev?: boolean;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
/**
|
|
@@ -114,6 +123,18 @@ export const CustomDomain = Resource(
|
|
|
114
123
|
logicalId: string, // Changed param name from id to logicalId for clarity
|
|
115
124
|
props: CustomDomainProps,
|
|
116
125
|
): Promise<CustomDomain> {
|
|
126
|
+
if (this.scope.local && props.dev) {
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
return this({
|
|
129
|
+
...props,
|
|
130
|
+
id: this.output?.id ?? "noop-domain",
|
|
131
|
+
zoneId: props.zoneId ?? "noop-zone",
|
|
132
|
+
environment: props.environment ?? "production",
|
|
133
|
+
createdAt: this.output?.createdAt ?? now,
|
|
134
|
+
updatedAt: now,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
117
138
|
// Create Cloudflare API client with automatic account discovery
|
|
118
139
|
const api = await createCloudflareApi(props);
|
|
119
140
|
|
|
@@ -121,6 +142,7 @@ export const CustomDomain = Resource(
|
|
|
121
142
|
await deleteCustomDomain(this, api, logicalId, props);
|
|
122
143
|
return this.destroy();
|
|
123
144
|
}
|
|
145
|
+
|
|
124
146
|
// Create or Update phase
|
|
125
147
|
return await ensureCustomDomain(this, api, logicalId, {
|
|
126
148
|
...props,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { cloneD1Database } from "./d1-clone.ts";
|
|
12
12
|
import { applyLocalD1Migrations } from "./d1-local-migrations.ts";
|
|
13
13
|
import { applyMigrations, listMigrationsFiles } from "./d1-migrations.ts";
|
|
14
|
+
import { deleteMiniflareBinding } from "./miniflare/delete.ts";
|
|
14
15
|
|
|
15
16
|
const DEFAULT_MIGRATIONS_TABLE = "d1_migrations";
|
|
16
17
|
|
|
@@ -126,7 +127,6 @@ export function isD1Database(resource: Resource): resource is D1Database {
|
|
|
126
127
|
export type D1Database = Resource<"cloudflare::D1Database"> &
|
|
127
128
|
Pick<
|
|
128
129
|
D1DatabaseProps,
|
|
129
|
-
| "dev"
|
|
130
130
|
| "migrationsDir"
|
|
131
131
|
| "migrationsTable"
|
|
132
132
|
| "primaryLocationHint"
|
|
@@ -142,6 +142,22 @@ export type D1Database = Resource<"cloudflare::D1Database"> &
|
|
|
142
142
|
* The name of the database
|
|
143
143
|
*/
|
|
144
144
|
name: string;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Development mode properties
|
|
148
|
+
* @internal
|
|
149
|
+
*/
|
|
150
|
+
dev: {
|
|
151
|
+
/**
|
|
152
|
+
* The ID of the database in development mode
|
|
153
|
+
*/
|
|
154
|
+
id: string;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Whether the database is running remotely
|
|
158
|
+
*/
|
|
159
|
+
remote: boolean;
|
|
160
|
+
};
|
|
145
161
|
};
|
|
146
162
|
|
|
147
163
|
/**
|
|
@@ -247,19 +263,14 @@ const _D1Database = Resource(
|
|
|
247
263
|
id: string,
|
|
248
264
|
props: D1DatabaseProps = {},
|
|
249
265
|
): Promise<D1Database> {
|
|
250
|
-
const api = await createCloudflareApi(props);
|
|
251
266
|
const databaseName = props.name ?? id;
|
|
267
|
+
const local = this.scope.local && !props.dev?.remote;
|
|
268
|
+
const dev = {
|
|
269
|
+
id: this.output?.dev?.id ?? this.output?.id ?? id,
|
|
270
|
+
remote: props.dev?.remote ?? false,
|
|
271
|
+
};
|
|
252
272
|
|
|
253
|
-
if (
|
|
254
|
-
if (props.delete !== false && this.output?.id !== id) {
|
|
255
|
-
await deleteDatabase(api, this.output?.id);
|
|
256
|
-
}
|
|
257
|
-
// Return void (a deleted database has no content)
|
|
258
|
-
return this.destroy();
|
|
259
|
-
}
|
|
260
|
-
let dbData: CloudflareD1Response;
|
|
261
|
-
|
|
262
|
-
if (this.scope.local && !props.dev?.remote) {
|
|
273
|
+
if (local) {
|
|
263
274
|
if (props.migrationsFiles && props.migrationsFiles.length > 0) {
|
|
264
275
|
await applyLocalD1Migrations({
|
|
265
276
|
databaseId: this.output?.id ?? id,
|
|
@@ -269,22 +280,36 @@ const _D1Database = Resource(
|
|
|
269
280
|
}
|
|
270
281
|
return this({
|
|
271
282
|
type: "d1",
|
|
272
|
-
|
|
273
|
-
// so set it to the resource ID which we can later use to detect that a DB needs to be created during an `update`
|
|
274
|
-
id: this.output?.id ?? id,
|
|
283
|
+
id: this.output?.id ?? "",
|
|
275
284
|
name: databaseName,
|
|
276
285
|
readReplication: props.readReplication,
|
|
277
286
|
primaryLocationHint: props.primaryLocationHint,
|
|
278
287
|
migrationsDir: props.migrationsDir,
|
|
279
288
|
migrationsTable: props.migrationsTable ?? DEFAULT_MIGRATIONS_TABLE,
|
|
280
|
-
dev
|
|
289
|
+
dev,
|
|
281
290
|
});
|
|
282
|
-
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const api = await createCloudflareApi(props);
|
|
294
|
+
|
|
295
|
+
if (this.phase === "delete") {
|
|
296
|
+
if (this.output.dev?.id) {
|
|
297
|
+
await deleteMiniflareBinding("d1", this.output.dev.id);
|
|
298
|
+
}
|
|
299
|
+
if (props.delete !== false && this.output?.id) {
|
|
300
|
+
await deleteDatabase(api, this.output.id);
|
|
301
|
+
}
|
|
302
|
+
// Return void (a deleted database has no content)
|
|
303
|
+
return this.destroy();
|
|
304
|
+
}
|
|
305
|
+
let dbData: CloudflareD1Response;
|
|
306
|
+
|
|
307
|
+
if (
|
|
283
308
|
this.phase === "create" ||
|
|
284
309
|
// this is true IFF the database was created locally before any live deployment
|
|
285
310
|
// in that case, we should still go through the create flow for "update"
|
|
286
311
|
// after that, the ID will remain the UUID for the lifetime of the database
|
|
287
|
-
this.output
|
|
312
|
+
!this.output?.id
|
|
288
313
|
) {
|
|
289
314
|
logger.log("Creating D1 database:", databaseName);
|
|
290
315
|
try {
|
|
@@ -387,7 +412,7 @@ const _D1Database = Resource(
|
|
|
387
412
|
name: databaseName,
|
|
388
413
|
readReplication: dbData.result.read_replication,
|
|
389
414
|
primaryLocationHint: props.primaryLocationHint,
|
|
390
|
-
dev
|
|
415
|
+
dev,
|
|
391
416
|
migrationsDir: props.migrationsDir,
|
|
392
417
|
migrationsTable: props.migrationsTable ?? DEFAULT_MIGRATIONS_TABLE,
|
|
393
418
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { logger } from "../util/logger.ts";
|
|
3
|
+
import type { CloudflareApiResponse } from "./api-response.ts";
|
|
3
4
|
import type { CloudflareApi } from "./api.ts";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -194,20 +195,6 @@ interface ImportPollingResponse {
|
|
|
194
195
|
upload_url?: string;
|
|
195
196
|
}
|
|
196
197
|
|
|
197
|
-
/**
|
|
198
|
-
* Cloudflare API response structure
|
|
199
|
-
*/
|
|
200
|
-
interface CloudflareApiResponse<T> {
|
|
201
|
-
result: T;
|
|
202
|
-
success: boolean;
|
|
203
|
-
errors: Array<{
|
|
204
|
-
message: string;
|
|
205
|
-
}>;
|
|
206
|
-
messages: Array<{
|
|
207
|
-
message: string;
|
|
208
|
-
}>;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
198
|
/**
|
|
212
199
|
* Calculates MD5 hash for a string
|
|
213
200
|
*/
|