alchemy 0.93.11 → 0.94.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/README.md +1 -1
- package/bin/alchemy.js +103 -103
- package/lib/aws/control/client.d.ts.map +1 -1
- package/lib/aws/control/client.js +2 -1
- package/lib/aws/control/client.js.map +1 -1
- package/lib/aws/control/resource.d.ts.map +1 -1
- package/lib/aws/control/resource.js +46 -5
- package/lib/aws/control/resource.js.map +1 -1
- package/lib/aws/ec2/utils.d.ts.map +1 -1
- package/lib/aws/ec2/utils.js +5 -3
- package/lib/aws/ec2/utils.js.map +1 -1
- package/lib/aws/utils.d.ts +8 -1
- package/lib/aws/utils.d.ts.map +1 -1
- package/lib/aws/utils.js +14 -1
- package/lib/aws/utils.js.map +1 -1
- package/lib/cloudflare/artifacts.d.ts +54 -0
- package/lib/cloudflare/artifacts.d.ts.map +1 -0
- package/lib/cloudflare/artifacts.js +14 -0
- package/lib/cloudflare/artifacts.js.map +1 -0
- package/lib/cloudflare/bindings.d.ts +16 -4
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/bound.d.ts +2 -1
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/bucket-custom-domain.d.ts.map +1 -1
- package/lib/cloudflare/bucket-custom-domain.js +22 -4
- package/lib/cloudflare/bucket-custom-domain.js.map +1 -1
- package/lib/cloudflare/d1-database.d.ts.map +1 -1
- package/lib/cloudflare/d1-database.js +8 -2
- package/lib/cloudflare/d1-database.js.map +1 -1
- package/lib/cloudflare/hyperdrive.d.ts +8 -0
- package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
- package/lib/cloudflare/hyperdrive.js +3 -1
- 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/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +3 -0
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +3 -1
- package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.js +16 -30
- package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts +11 -0
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +20 -1
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker.d.ts +37 -0
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +1 -1
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.js +11 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/cloudflare/zone.js +1 -1
- package/lib/cloudflare/zone.js.map +1 -1
- package/lib/scope.js +2 -2
- package/lib/scope.js.map +1 -1
- package/package.json +4 -4
- package/src/aws/control/client.ts +2 -1
- package/src/aws/control/resource.ts +67 -8
- package/src/aws/ec2/utils.ts +5 -3
- package/src/aws/utils.ts +20 -2
- package/src/cloudflare/artifacts.ts +69 -0
- package/src/cloudflare/bindings.ts +17 -2
- package/src/cloudflare/bound.ts +68 -65
- package/src/cloudflare/bucket-custom-domain.ts +33 -7
- package/src/cloudflare/d1-database.ts +12 -2
- package/src/cloudflare/hyperdrive.ts +13 -1
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +5 -0
- package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +3 -1
- package/src/cloudflare/miniflare/remote-binding-proxy.ts +20 -43
- package/src/cloudflare/worker-metadata.ts +33 -1
- package/src/cloudflare/worker.ts +41 -1
- package/src/cloudflare/wrangler.json.ts +19 -0
- package/src/cloudflare/zone.ts +1 -1
- package/src/scope.ts +2 -2
- package/workers/cloudflare-state-store.js +57 -57
- package/workers/tunnel-proxy.js +1 -1
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.94.0",
|
|
4
4
|
"homepage": "https://alchemy.run",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Sam Goodwin <sam@alchemy.run>",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/alchemy-run/alchemy.git",
|
|
9
|
+
"url": "git+https://github.com/alchemy-run/alchemy-async.git",
|
|
10
10
|
"directory": "alchemy"
|
|
11
11
|
},
|
|
12
12
|
"bin": {
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
"dependencies": {
|
|
180
180
|
"@aws-sdk/credential-providers": "^3.0.0",
|
|
181
181
|
"@cloudflare/unenv-preset": "2.7.7",
|
|
182
|
-
"@cloudflare/workers-types": "^
|
|
182
|
+
"@cloudflare/workers-types": "^5.20260708.1",
|
|
183
183
|
"@iarna/toml": "^2.2.5",
|
|
184
184
|
"@octokit/rest": "^21.1.1",
|
|
185
185
|
"@smithy/node-config-provider": "^4.0.0",
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
"glob": "^10.0.0",
|
|
196
196
|
"jszip": "^3.0.0",
|
|
197
197
|
"libsodium-wrappers": "^0.8.0",
|
|
198
|
-
"miniflare": "4.
|
|
198
|
+
"miniflare": "4.20260708.1",
|
|
199
199
|
"neverthrow": "^8.2.0",
|
|
200
200
|
"open": "^10.1.2",
|
|
201
201
|
"openapi-types": "^12.1.3",
|
|
@@ -3,6 +3,7 @@ import { loadConfig } from "@smithy/node-config-provider";
|
|
|
3
3
|
import { AwsClient } from "aws4fetch";
|
|
4
4
|
import { logger } from "../../util/logger.ts";
|
|
5
5
|
import { safeFetch } from "../../util/safe-fetch.ts";
|
|
6
|
+
import { awsEndpoint } from "../utils.ts";
|
|
6
7
|
import {
|
|
7
8
|
AlreadyExistsError,
|
|
8
9
|
CloudControlError,
|
|
@@ -355,7 +356,7 @@ export class CloudControlClient {
|
|
|
355
356
|
while (true) {
|
|
356
357
|
try {
|
|
357
358
|
const args = [
|
|
358
|
-
|
|
359
|
+
`${awsEndpoint("cloudcontrolapi", this.region)}/?Action=${action}&Version=2021-09-30`,
|
|
359
360
|
{
|
|
360
361
|
method: "POST",
|
|
361
362
|
headers: {
|
|
@@ -6,7 +6,11 @@ import {
|
|
|
6
6
|
type Provider,
|
|
7
7
|
} from "../../resource.ts";
|
|
8
8
|
import { logger } from "../../util/logger.ts";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
createCloudControlClient,
|
|
11
|
+
type CloudControlClient,
|
|
12
|
+
type ProgressEvent,
|
|
13
|
+
} from "./client.ts";
|
|
10
14
|
import {
|
|
11
15
|
AlreadyExistsError,
|
|
12
16
|
ConcurrentOperationError,
|
|
@@ -297,8 +301,20 @@ async function CloudControlLifecycle(
|
|
|
297
301
|
props.typeName,
|
|
298
302
|
this.output.id,
|
|
299
303
|
);
|
|
304
|
+
if (!currentResource) {
|
|
305
|
+
// The physical resource no longer exists (deleted or detached out-of-band),
|
|
306
|
+
// so there is nothing to patch. Falling through to UpdateResource would fail
|
|
307
|
+
// on types with no update support (e.g. AWS::EC2::VolumeAttachment, whose
|
|
308
|
+
// properties are all Immutable). Recreate via replacement instead. Force the
|
|
309
|
+
// replacement (delete-before-create) because the old resource is already gone
|
|
310
|
+
// (delete tolerates NotFoundError) and a deferred delete would destroy the
|
|
311
|
+
// new resource if it reuses the same identifier (e.g. an unchanged BucketName).
|
|
312
|
+
logger.log(
|
|
313
|
+
`Resource ${id} no longer exists (empty read-back on update), requiring replacement`,
|
|
314
|
+
);
|
|
315
|
+
return this.replace(true);
|
|
316
|
+
}
|
|
300
317
|
if (
|
|
301
|
-
currentResource &&
|
|
302
318
|
hasImmutablePropertyChanges(
|
|
303
319
|
props.typeName,
|
|
304
320
|
currentResource,
|
|
@@ -328,10 +344,12 @@ async function CloudControlLifecycle(
|
|
|
328
344
|
);
|
|
329
345
|
} catch (error) {
|
|
330
346
|
if (error instanceof AlreadyExistsError && props.adopt) {
|
|
331
|
-
const resource =
|
|
347
|
+
const resource = await getResourceForAdoption(
|
|
348
|
+
client,
|
|
332
349
|
props.typeName,
|
|
333
|
-
error.progressEvent.Identifier
|
|
334
|
-
|
|
350
|
+
error.progressEvent.Identifier,
|
|
351
|
+
error,
|
|
352
|
+
);
|
|
335
353
|
|
|
336
354
|
response = await updateResourceWithPatch(
|
|
337
355
|
client,
|
|
@@ -357,10 +375,11 @@ async function CloudControlLifecycle(
|
|
|
357
375
|
const concurrentResult = await client.poll(error.requestToken);
|
|
358
376
|
|
|
359
377
|
// The concurrent operation succeeded, now adopt the resource
|
|
360
|
-
const resource =
|
|
378
|
+
const resource = await getResourceForAdoption(
|
|
379
|
+
client,
|
|
361
380
|
props.typeName,
|
|
362
|
-
concurrentResult.Identifier
|
|
363
|
-
)
|
|
381
|
+
concurrentResult.Identifier,
|
|
382
|
+
);
|
|
364
383
|
|
|
365
384
|
// Apply our desired state as a patch to the existing resource
|
|
366
385
|
response = await updateResourceWithPatch(
|
|
@@ -401,6 +420,46 @@ async function CloudControlLifecycle(
|
|
|
401
420
|
};
|
|
402
421
|
}
|
|
403
422
|
|
|
423
|
+
/**
|
|
424
|
+
* Read back a resource that Cloud Control reported as already existing so it
|
|
425
|
+
* can be adopted. The read-back is retried briefly because some services are
|
|
426
|
+
* eventually consistent between the create conflict and GetResource.
|
|
427
|
+
*
|
|
428
|
+
* If the resource still cannot be read back, adoption is impossible — most
|
|
429
|
+
* commonly the name is taken in a globally shared namespace but not visible
|
|
430
|
+
* in this account (e.g. an S3 bucket owned by another AWS account, or a
|
|
431
|
+
* recently deleted name still held by the service). Cloud Control reports
|
|
432
|
+
* both as AlreadyExists, so fail with an actionable error instead of letting
|
|
433
|
+
* `undefined` flow into the patch computation.
|
|
434
|
+
*/
|
|
435
|
+
async function getResourceForAdoption(
|
|
436
|
+
client: CloudControlClient,
|
|
437
|
+
typeName: string,
|
|
438
|
+
identifier: string | undefined,
|
|
439
|
+
cause?: Error,
|
|
440
|
+
): Promise<Record<string, any>> {
|
|
441
|
+
if (identifier) {
|
|
442
|
+
let delay = 500;
|
|
443
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
444
|
+
if (attempt > 0) {
|
|
445
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
446
|
+
delay *= 2;
|
|
447
|
+
}
|
|
448
|
+
const resource = await client.getResource(typeName, identifier);
|
|
449
|
+
if (resource !== undefined) {
|
|
450
|
+
return resource;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
throw new Error(
|
|
455
|
+
`Resource ${typeName} "${identifier}" already exists but could not be read back for adoption. ` +
|
|
456
|
+
"It may belong to a different AWS account (e.g. S3 bucket names are globally unique), " +
|
|
457
|
+
"exist in a different region, or its name may still be reserved by a recent deletion. " +
|
|
458
|
+
"Choose a different name or wait for the name to become available.",
|
|
459
|
+
{ cause },
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
404
463
|
async function updateResourceWithPatch(
|
|
405
464
|
client: any,
|
|
406
465
|
typeName: string,
|
package/src/aws/ec2/utils.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { logger } from "../../util/logger.ts";
|
|
|
6
6
|
import { flattenParams } from "../../util/params.ts";
|
|
7
7
|
import type { AwsClientProps } from "../client-props.ts";
|
|
8
8
|
import { resolveAwsCredentials } from "../credentials.ts";
|
|
9
|
-
import { getRegion } from "../utils.ts";
|
|
9
|
+
import { awsEndpoint, getRegion } from "../utils.ts";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Shared EC2 utilities for all EC2 resources
|
|
@@ -100,8 +100,10 @@ export async function callEC2Api<T>(
|
|
|
100
100
|
// Try the API call, and retry once with fresh credentials on auth failure
|
|
101
101
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
102
102
|
try {
|
|
103
|
-
// Use the client's region instead of the global region
|
|
104
|
-
|
|
103
|
+
// Use the client's region instead of the global region.
|
|
104
|
+
// Honour AWS_ENDPOINT_URL / AWS_ENDPOINT_URL_EC2 for emulators
|
|
105
|
+
// (LocalStack, moto, etc.) — matches AWS SDK v3 behaviour.
|
|
106
|
+
const url = `${awsEndpoint("ec2", client.region ?? "us-east-1")}/`;
|
|
105
107
|
|
|
106
108
|
const body = new URLSearchParams({
|
|
107
109
|
Action: action,
|
package/src/aws/utils.ts
CHANGED
|
@@ -14,13 +14,29 @@ export const getRegion: Provider<string> = loadConfig({
|
|
|
14
14
|
default: "us-east-1",
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the endpoint URL for an aws4fetch service request, honouring the
|
|
19
|
+
* same `AWS_ENDPOINT_URL_<SERVICE>` / `AWS_ENDPOINT_URL` env vars as the AWS
|
|
20
|
+
* SDK / CLI so aws4fetch-based resources can be pointed at LocalStack, moto,
|
|
21
|
+
* or any other emulator.
|
|
22
|
+
*/
|
|
23
|
+
export function awsEndpoint(service: string, region: string): string {
|
|
24
|
+
return (
|
|
25
|
+
process.env[
|
|
26
|
+
`AWS_ENDPOINT_URL_${service.toUpperCase().replace(/-/g, "_")}`
|
|
27
|
+
] ??
|
|
28
|
+
process.env.AWS_ENDPOINT_URL ??
|
|
29
|
+
`https://${service}.${region}.amazonaws.com`
|
|
30
|
+
).replace(/\/+$/, "");
|
|
31
|
+
}
|
|
32
|
+
|
|
17
33
|
/**
|
|
18
34
|
* AWS Service Configuration
|
|
19
35
|
*/
|
|
20
36
|
export interface AwsServiceConfig {
|
|
21
37
|
service: string;
|
|
22
38
|
version: string;
|
|
23
|
-
endpoint
|
|
39
|
+
endpoint?: (region: string) => string;
|
|
24
40
|
}
|
|
25
41
|
|
|
26
42
|
/**
|
|
@@ -63,7 +79,9 @@ export function getAwsApiCaller<T = any>(
|
|
|
63
79
|
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
64
80
|
try {
|
|
65
81
|
const region = await getRegion();
|
|
66
|
-
const url = config.endpoint
|
|
82
|
+
const url = config.endpoint
|
|
83
|
+
? config.endpoint(region)
|
|
84
|
+
: `${awsEndpoint(config.service, region)}/`;
|
|
67
85
|
|
|
68
86
|
const body = new URLSearchParams({
|
|
69
87
|
Action: action,
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Properties for creating an Artifacts binding.
|
|
3
|
+
*
|
|
4
|
+
* Artifacts namespaces are created implicitly by Cloudflare when the first
|
|
5
|
+
* repository is created in the namespace, so this binding does not provision
|
|
6
|
+
* or delete a namespace resource.
|
|
7
|
+
*/
|
|
8
|
+
export interface ArtifactsProps {
|
|
9
|
+
/**
|
|
10
|
+
* Artifacts namespace name.
|
|
11
|
+
*/
|
|
12
|
+
namespace: string;
|
|
13
|
+
|
|
14
|
+
dev?: {
|
|
15
|
+
/**
|
|
16
|
+
* Whether to run the Artifacts binding remotely in local development.
|
|
17
|
+
*
|
|
18
|
+
* Artifacts do not have a local emulator, so generated Wrangler config
|
|
19
|
+
* defaults this binding to remote mode.
|
|
20
|
+
*
|
|
21
|
+
* @default true
|
|
22
|
+
*/
|
|
23
|
+
remote?: boolean;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Cloudflare Artifacts binding.
|
|
29
|
+
*
|
|
30
|
+
* Artifacts namespaces are implicit containers for repositories. Use this
|
|
31
|
+
* helper to bind a Worker to a namespace; repository lifecycle should be
|
|
32
|
+
* managed through the Artifacts runtime API, REST API, or Git endpoint.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { Artifacts, Worker } from "alchemy/cloudflare";
|
|
37
|
+
*
|
|
38
|
+
* await Worker("site-editor", {
|
|
39
|
+
* entrypoint: "./src/worker.ts",
|
|
40
|
+
* bindings: {
|
|
41
|
+
* SITE_ARTIFACTS: Artifacts({ namespace: "my-sites" }),
|
|
42
|
+
* },
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @see https://developers.cloudflare.com/artifacts/
|
|
47
|
+
*/
|
|
48
|
+
export interface Artifacts {
|
|
49
|
+
type: "artifacts";
|
|
50
|
+
namespace: string;
|
|
51
|
+
dev?: {
|
|
52
|
+
remote?: boolean;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function Artifacts(props: string | ArtifactsProps): Artifacts {
|
|
57
|
+
if (typeof props === "string") {
|
|
58
|
+
return {
|
|
59
|
+
type: "artifacts",
|
|
60
|
+
namespace: props,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
type: "artifacts",
|
|
66
|
+
namespace: props.namespace,
|
|
67
|
+
dev: props.dev,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -8,6 +8,7 @@ import type { Ai } from "./ai.ts";
|
|
|
8
8
|
import type { AiSearchNamespace } from "./ai-search-namespace.ts";
|
|
9
9
|
import type { AiSearch } from "./ai-search.ts";
|
|
10
10
|
import type { AnalyticsEngineDataset } from "./analytics-engine.ts";
|
|
11
|
+
import type { Artifacts } from "./artifacts.ts";
|
|
11
12
|
import type { Assets } from "./assets.ts";
|
|
12
13
|
import type { Bound } from "./bound.ts";
|
|
13
14
|
import type { BrowserRendering } from "./browser-rendering.ts";
|
|
@@ -54,6 +55,7 @@ export type Binding =
|
|
|
54
55
|
| AiSearch
|
|
55
56
|
| AiSearchNamespace
|
|
56
57
|
| Assets
|
|
58
|
+
| Artifacts
|
|
57
59
|
| Container
|
|
58
60
|
| CloudflareSecret
|
|
59
61
|
| CloudflareSecretRef
|
|
@@ -127,6 +129,7 @@ export type WorkerBindingSpec =
|
|
|
127
129
|
| WorkerBindingAiSearch
|
|
128
130
|
| WorkerBindingAiSearchNamespace
|
|
129
131
|
| WorkerBindingAnalyticsEngine
|
|
132
|
+
| WorkerBindingArtifacts
|
|
130
133
|
| WorkerBindingAssets
|
|
131
134
|
| WorkerBindingBrowserRendering
|
|
132
135
|
| WorkerBindingD1
|
|
@@ -206,6 +209,18 @@ export interface WorkerBindingAnalyticsEngine {
|
|
|
206
209
|
dataset: string;
|
|
207
210
|
}
|
|
208
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Artifacts binding type
|
|
214
|
+
*/
|
|
215
|
+
export interface WorkerBindingArtifacts {
|
|
216
|
+
/** The name of the binding */
|
|
217
|
+
name: string;
|
|
218
|
+
/** Type identifier for Artifacts binding */
|
|
219
|
+
type: "artifacts";
|
|
220
|
+
/** Artifacts namespace name */
|
|
221
|
+
namespace: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
209
224
|
/**
|
|
210
225
|
* Assets binding type
|
|
211
226
|
*/
|
|
@@ -295,8 +310,8 @@ export interface WorkerBindingJson {
|
|
|
295
310
|
name: string;
|
|
296
311
|
/** Type identifier for JSON binding */
|
|
297
312
|
type: "json";
|
|
298
|
-
/** JSON value */
|
|
299
|
-
json:
|
|
313
|
+
/** JSON value, sent raw (not stringified) so the runtime injects native types */
|
|
314
|
+
json: unknown;
|
|
300
315
|
}
|
|
301
316
|
|
|
302
317
|
/**
|
package/src/cloudflare/bound.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Ai as _Ai } from "./ai.ts";
|
|
|
5
5
|
import type { AiSearchNamespace as _AiSearchNamespace } from "./ai-search-namespace.ts";
|
|
6
6
|
import type { AiSearch as _AiSearch } from "./ai-search.ts";
|
|
7
7
|
import type { AnalyticsEngineDataset as _AnalyticsEngineDataset } from "./analytics-engine.ts";
|
|
8
|
+
import type { Artifacts as _Artifacts } from "./artifacts.ts";
|
|
8
9
|
import type { Assets } from "./assets.ts";
|
|
9
10
|
import type { Binding, Json, Self } from "./bindings.ts";
|
|
10
11
|
import type { BrowserRendering } from "./browser-rendering.ts";
|
|
@@ -61,69 +62,71 @@ export type Bound<T extends Binding> = T extends _AiSearch
|
|
|
61
62
|
? BoundWorker<RPC>
|
|
62
63
|
: T extends _Worker<any, infer RPC> | WorkerRef<infer RPC>
|
|
63
64
|
? BoundWorker<RPC>
|
|
64
|
-
: T extends
|
|
65
|
-
?
|
|
66
|
-
: T extends
|
|
67
|
-
?
|
|
68
|
-
: T extends
|
|
69
|
-
?
|
|
70
|
-
: T extends
|
|
71
|
-
?
|
|
72
|
-
: T extends
|
|
73
|
-
?
|
|
74
|
-
: T extends
|
|
75
|
-
?
|
|
76
|
-
: T extends
|
|
77
|
-
?
|
|
78
|
-
: T extends
|
|
79
|
-
?
|
|
80
|
-
: T extends
|
|
81
|
-
?
|
|
82
|
-
: T extends
|
|
83
|
-
?
|
|
84
|
-
: T extends
|
|
85
|
-
?
|
|
86
|
-
: T extends
|
|
87
|
-
?
|
|
88
|
-
: T extends
|
|
89
|
-
?
|
|
90
|
-
: T extends
|
|
91
|
-
?
|
|
92
|
-
: T extends
|
|
93
|
-
?
|
|
94
|
-
: T extends
|
|
95
|
-
?
|
|
96
|
-
: T extends
|
|
97
|
-
?
|
|
98
|
-
: T extends
|
|
99
|
-
?
|
|
100
|
-
: T extends
|
|
101
|
-
?
|
|
102
|
-
: T extends
|
|
103
|
-
?
|
|
104
|
-
: T extends
|
|
105
|
-
?
|
|
106
|
-
: T extends
|
|
107
|
-
?
|
|
108
|
-
: T extends
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
?
|
|
114
|
-
: T extends
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
: T extends _Container<
|
|
119
|
-
infer Obj
|
|
65
|
+
: T extends _Artifacts
|
|
66
|
+
? Artifacts
|
|
67
|
+
: T extends { type: "service" }
|
|
68
|
+
? Service
|
|
69
|
+
: T extends _R2Bucket
|
|
70
|
+
? R2Bucket
|
|
71
|
+
: T extends _Hyperdrive | HyperdriveRef
|
|
72
|
+
? Hyperdrive
|
|
73
|
+
: T extends Secret
|
|
74
|
+
? string
|
|
75
|
+
: T extends CloudflareSecret | CloudflareSecretRef
|
|
76
|
+
? SecretsStoreSecret
|
|
77
|
+
: T extends _EmailSender
|
|
78
|
+
? SendEmail
|
|
79
|
+
: T extends SecretKey
|
|
80
|
+
? CryptoKey
|
|
81
|
+
: T extends Assets
|
|
82
|
+
? Service
|
|
83
|
+
: T extends _Workflow<infer P>
|
|
84
|
+
? Workflow<P>
|
|
85
|
+
: T extends _D1Database
|
|
86
|
+
? D1Database
|
|
87
|
+
: T extends DispatchNamespace
|
|
88
|
+
? DispatchNamespace
|
|
89
|
+
: T extends _WorkerLoader
|
|
90
|
+
? WorkerLoader
|
|
91
|
+
: T extends _VectorizeIndex
|
|
92
|
+
? VectorizeIndex
|
|
93
|
+
: T extends _Queue<infer Body>
|
|
94
|
+
? Queue<Body>
|
|
95
|
+
: T extends _AnalyticsEngineDataset
|
|
96
|
+
? AnalyticsEngineDataset
|
|
97
|
+
: T extends _Pipeline<infer R>
|
|
98
|
+
? Pipeline<R>
|
|
99
|
+
: T extends _RateLimit
|
|
100
|
+
? RateLimit
|
|
101
|
+
: T extends string
|
|
102
|
+
? T
|
|
103
|
+
: T extends BrowserRendering
|
|
104
|
+
? BrowserRun
|
|
105
|
+
: T extends _Ai<infer M>
|
|
106
|
+
? Ai<M>
|
|
107
|
+
: T extends _Images
|
|
108
|
+
? ImagesBinding
|
|
109
|
+
: T extends _VersionMetadata
|
|
110
|
+
? WorkerVersionMetadata
|
|
111
|
+
: T extends
|
|
112
|
+
| _Worker.DevDomain
|
|
113
|
+
| _Worker.DevUrl
|
|
114
|
+
? string
|
|
115
|
+
: T extends Self
|
|
116
|
+
? Service
|
|
117
|
+
: T extends Json<
|
|
118
|
+
infer T
|
|
120
119
|
>
|
|
121
|
-
?
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
120
|
+
? T
|
|
121
|
+
: T extends _Container<
|
|
122
|
+
infer Obj
|
|
123
|
+
>
|
|
124
|
+
? DurableObjectNamespace<
|
|
125
|
+
Obj &
|
|
126
|
+
Rpc.DurableObjectBranded
|
|
127
|
+
>
|
|
128
|
+
: T extends _VpcService
|
|
129
|
+
? Fetcher
|
|
130
|
+
: T extends undefined
|
|
131
|
+
? undefined
|
|
132
|
+
: Service;
|
|
@@ -142,17 +142,24 @@ export const R2BucketCustomDomain = Resource(
|
|
|
142
142
|
props.jurisdiction,
|
|
143
143
|
payload,
|
|
144
144
|
).catch(async (error) => {
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
error.status === 409 &&
|
|
148
|
-
adopt
|
|
149
|
-
) {
|
|
150
|
-
return await updateBucketCustomDomain(
|
|
145
|
+
if (error instanceof CloudflareApiError && error.status === 409) {
|
|
146
|
+
const existing = await getBucketCustomDomain(
|
|
151
147
|
api,
|
|
152
148
|
props.bucketName,
|
|
149
|
+
payload.domain,
|
|
153
150
|
props.jurisdiction,
|
|
154
|
-
payload,
|
|
155
151
|
);
|
|
152
|
+
if (existing && customDomainMatches(existing, payload)) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (adopt) {
|
|
156
|
+
return await updateBucketCustomDomain(
|
|
157
|
+
api,
|
|
158
|
+
props.bucketName,
|
|
159
|
+
props.jurisdiction,
|
|
160
|
+
payload,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
156
163
|
}
|
|
157
164
|
throw error;
|
|
158
165
|
});
|
|
@@ -190,6 +197,25 @@ export const R2BucketCustomDomain = Resource(
|
|
|
190
197
|
},
|
|
191
198
|
);
|
|
192
199
|
|
|
200
|
+
function customDomainMatches(
|
|
201
|
+
existing: R2BucketCustomDomain,
|
|
202
|
+
payload: R2BucketCustomDomain,
|
|
203
|
+
) {
|
|
204
|
+
return (
|
|
205
|
+
existing.domain === payload.domain &&
|
|
206
|
+
existing.zoneId === payload.zoneId &&
|
|
207
|
+
existing.enabled === payload.enabled &&
|
|
208
|
+
(existing.minTLS ?? undefined) === (payload.minTLS ?? undefined) &&
|
|
209
|
+
arraysEqual(existing.ciphers, payload.ciphers)
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function arraysEqual(a: string[] | undefined, b: string[] | undefined) {
|
|
214
|
+
if (a === b) return true;
|
|
215
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
216
|
+
return a.every((v, i) => v === b[i]);
|
|
217
|
+
}
|
|
218
|
+
|
|
193
219
|
async function createBucketCustomDomain(
|
|
194
220
|
api: CloudflareApi,
|
|
195
221
|
bucket: string,
|
|
@@ -307,11 +307,21 @@ const _D1Database = Resource(
|
|
|
307
307
|
|
|
308
308
|
if (local) {
|
|
309
309
|
if (props.migrationsFiles?.length || props.importFiles?.length) {
|
|
310
|
+
const localMigrationsFiles = props.migrationsDir
|
|
311
|
+
? await listSqlFiles(props.migrationsDir)
|
|
312
|
+
: [];
|
|
313
|
+
const localImportFiles = props.importFiles?.length
|
|
314
|
+
? await Promise.all(
|
|
315
|
+
props.importFiles.map((file) =>
|
|
316
|
+
readSqlFile(this.scope.rootDir, file.id),
|
|
317
|
+
),
|
|
318
|
+
)
|
|
319
|
+
: [];
|
|
310
320
|
await applyLocalD1Migrations({
|
|
311
321
|
databaseId: dev.id,
|
|
312
322
|
migrationsTable: props.migrationsTable ?? DEFAULT_MIGRATIONS_TABLE,
|
|
313
|
-
migrations:
|
|
314
|
-
imports:
|
|
323
|
+
migrations: localMigrationsFiles,
|
|
324
|
+
imports: localImportFiles,
|
|
315
325
|
rootDir: this.scope.rootDir,
|
|
316
326
|
});
|
|
317
327
|
}
|
|
@@ -47,6 +47,11 @@ export interface HyperdrivePublicOrigin {
|
|
|
47
47
|
*/
|
|
48
48
|
scheme?: "postgres" | "mysql";
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Connection query string, without the leading "?"
|
|
52
|
+
*/
|
|
53
|
+
query?: string;
|
|
54
|
+
|
|
50
55
|
/**
|
|
51
56
|
* Database user
|
|
52
57
|
*/
|
|
@@ -90,6 +95,11 @@ export interface HyperdriveOriginWithAccess {
|
|
|
90
95
|
*/
|
|
91
96
|
scheme?: "postgres" | "mysql";
|
|
92
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Connection query string, without the leading "?"
|
|
100
|
+
*/
|
|
101
|
+
query?: string;
|
|
102
|
+
|
|
93
103
|
/**
|
|
94
104
|
* Database user
|
|
95
105
|
*/
|
|
@@ -610,6 +620,7 @@ export const normalizeHyperdriveOrigin = (
|
|
|
610
620
|
host: url.hostname,
|
|
611
621
|
port: normalizePort(scheme, url.port),
|
|
612
622
|
database: url.pathname.slice(1),
|
|
623
|
+
query: url.search?.slice(1) ?? "",
|
|
613
624
|
};
|
|
614
625
|
}
|
|
615
626
|
const scheme = normalizeScheme(origin.scheme);
|
|
@@ -623,6 +634,7 @@ export const normalizeHyperdriveOrigin = (
|
|
|
623
634
|
}),
|
|
624
635
|
scheme,
|
|
625
636
|
port: normalizePort(scheme, origin.port),
|
|
637
|
+
query: origin.query ?? "",
|
|
626
638
|
};
|
|
627
639
|
};
|
|
628
640
|
|
|
@@ -660,6 +672,6 @@ const toConnectionString = (
|
|
|
660
672
|
"password" in origin ? origin.password : origin.access_client_secret,
|
|
661
673
|
);
|
|
662
674
|
return new Secret(
|
|
663
|
-
`${origin.scheme}://${origin.user}:${password}@${origin.host}:${origin.port}/${origin.database}`,
|
|
675
|
+
`${origin.scheme}://${origin.user}:${password}@${origin.host}:${origin.port}/${origin.database}${origin.query ? `?${origin.query}` : ""}`,
|
|
664
676
|
);
|
|
665
677
|
};
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ export * from "./api-gateway-operation.ts";
|
|
|
20
20
|
export * from "./api-schema.ts";
|
|
21
21
|
export * from "./api-shield.ts";
|
|
22
22
|
export * from "./api.ts";
|
|
23
|
+
export * from "./artifacts.ts";
|
|
23
24
|
export * from "./assets.ts";
|
|
24
25
|
export * from "./astro/astro.ts";
|
|
25
26
|
export * from "./bindings.ts";
|
|
@@ -167,6 +167,11 @@ export const buildWorkerOptions = async (
|
|
|
167
167
|
};
|
|
168
168
|
break;
|
|
169
169
|
}
|
|
170
|
+
case "artifacts": {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"Artifacts bindings are not supported in Alchemy local dev yet. Deploy the Worker or use generated Wrangler config with remote Artifacts bindings.",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
170
175
|
case "assets": {
|
|
171
176
|
options.assets = {
|
|
172
177
|
binding: key,
|
|
@@ -249,7 +249,9 @@ const writeMiniflareResponseToNode = (
|
|
|
249
249
|
});
|
|
250
250
|
|
|
251
251
|
if (res.body) {
|
|
252
|
-
Readable.fromWeb(res.body)
|
|
252
|
+
const body = Readable.fromWeb(res.body);
|
|
253
|
+
body.on("error", () => out.destroy());
|
|
254
|
+
body.pipe(out, { end: true });
|
|
253
255
|
} else {
|
|
254
256
|
out.end();
|
|
255
257
|
}
|