alchemy 0.60.2 → 0.62.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 -2
- package/bin/alchemy.js +1480 -1498
- package/bin/commands/init.ts +94 -25
- package/bin/services/execute-alchemy.ts +32 -23
- package/lib/alchemy.d.ts +2 -2
- package/lib/alchemy.d.ts.map +1 -1
- package/lib/alchemy.js +1 -0
- package/lib/alchemy.js.map +1 -1
- package/lib/aws/client-props.d.ts +149 -0
- package/lib/aws/client-props.d.ts.map +1 -0
- package/lib/aws/client-props.js +1 -0
- package/lib/aws/client-props.js.map +1 -0
- package/lib/aws/credentials.d.ts +68 -5
- package/lib/aws/credentials.d.ts.map +1 -1
- package/lib/aws/credentials.js +148 -0
- package/lib/aws/credentials.js.map +1 -1
- package/lib/aws/ec2/internet-gateway-attachment.d.ts +57 -3
- package/lib/aws/ec2/internet-gateway-attachment.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway-attachment.js +57 -3
- package/lib/aws/ec2/internet-gateway-attachment.js.map +1 -1
- package/lib/aws/ec2/internet-gateway.d.ts +68 -40
- package/lib/aws/ec2/internet-gateway.d.ts.map +1 -1
- package/lib/aws/ec2/internet-gateway.js +68 -40
- package/lib/aws/ec2/internet-gateway.js.map +1 -1
- package/lib/aws/ec2/nat-gateway.d.ts +99 -18
- package/lib/aws/ec2/nat-gateway.d.ts.map +1 -1
- package/lib/aws/ec2/nat-gateway.js +113 -37
- package/lib/aws/ec2/nat-gateway.js.map +1 -1
- package/lib/aws/ec2/route-table-association.d.ts +1 -1
- package/lib/aws/ec2/route-table-association.js +1 -1
- package/lib/aws/ec2/route-table.d.ts +64 -1
- package/lib/aws/ec2/route-table.d.ts.map +1 -1
- package/lib/aws/ec2/route-table.js +64 -1
- package/lib/aws/ec2/route-table.js.map +1 -1
- package/lib/aws/ec2/route.d.ts +60 -1
- package/lib/aws/ec2/route.d.ts.map +1 -1
- package/lib/aws/ec2/route.js +60 -1
- package/lib/aws/ec2/route.js.map +1 -1
- package/lib/aws/ec2/security-group.d.ts +96 -3
- package/lib/aws/ec2/security-group.d.ts.map +1 -1
- package/lib/aws/ec2/security-group.js +96 -3
- package/lib/aws/ec2/security-group.js.map +1 -1
- package/lib/aws/ec2/subnet.d.ts +61 -3
- package/lib/aws/ec2/subnet.d.ts.map +1 -1
- package/lib/aws/ec2/subnet.js +61 -3
- package/lib/aws/ec2/subnet.js.map +1 -1
- package/lib/aws/ec2/utils.d.ts +10 -2
- package/lib/aws/ec2/utils.d.ts.map +1 -1
- package/lib/aws/ec2/utils.js +68 -7
- package/lib/aws/ec2/utils.js.map +1 -1
- package/lib/aws/ec2/vpc.d.ts +50 -2
- package/lib/aws/ec2/vpc.d.ts.map +1 -1
- package/lib/aws/ec2/vpc.js +50 -2
- package/lib/aws/ec2/vpc.js.map +1 -1
- package/lib/aws/index.d.ts +2 -0
- package/lib/aws/index.d.ts.map +1 -1
- package/lib/aws/index.js +2 -0
- package/lib/aws/index.js.map +1 -1
- package/lib/cloudflare/api-response.js +2 -2
- package/lib/cloudflare/api-response.js.map +1 -1
- package/lib/cloudflare/api.d.ts +19 -0
- package/lib/cloudflare/api.d.ts.map +1 -1
- package/lib/cloudflare/api.js +4 -0
- package/lib/cloudflare/api.js.map +1 -1
- package/lib/cloudflare/assets.d.ts.map +1 -1
- package/lib/cloudflare/assets.js +3 -4
- package/lib/cloudflare/assets.js.map +1 -1
- package/lib/cloudflare/bucket.d.ts +5 -3
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +28 -20
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bundle/plugin-compat-warning.js +7 -7
- package/lib/cloudflare/bundle/plugin-compat-warning.js.map +1 -1
- package/lib/cloudflare/cloudflare-env-proxy.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/d1-migrations.d.ts.map +1 -1
- package/lib/cloudflare/d1-migrations.js +3 -2
- package/lib/cloudflare/d1-migrations.js.map +1 -1
- package/lib/cloudflare/index.d.ts +2 -1
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +2 -1
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/kv-namespace.d.ts +2 -2
- package/lib/cloudflare/kv-namespace.d.ts.map +1 -1
- package/lib/cloudflare/kv-namespace.js.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +16 -3
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +3 -0
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/oauth.js +3 -3
- package/lib/cloudflare/oauth.js.map +1 -1
- package/lib/cloudflare/quick-tunnel.d.ts +17 -0
- package/lib/cloudflare/quick-tunnel.d.ts.map +1 -0
- package/lib/cloudflare/quick-tunnel.js +26 -0
- package/lib/cloudflare/quick-tunnel.js.map +1 -0
- package/lib/cloudflare/sveltekit/plugin.d.ts +2 -2
- package/lib/cloudflare/sveltekit/plugin.d.ts.map +1 -1
- package/lib/cloudflare/sveltekit/plugin.js +5 -4
- package/lib/cloudflare/sveltekit/plugin.js.map +1 -1
- package/lib/cloudflare/website.d.ts.map +1 -1
- package/lib/cloudflare/website.js +26 -97
- package/lib/cloudflare/website.js.map +1 -1
- package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
- package/lib/cloudflare/worker-bundle.js +11 -9
- package/lib/cloudflare/worker-bundle.js.map +1 -1
- package/lib/cloudflare/worker-tail.d.ts.map +1 -1
- package/lib/cloudflare/worker-tail.js +6 -6
- package/lib/cloudflare/worker-tail.js.map +1 -1
- package/lib/resource.d.ts +1 -0
- package/lib/resource.d.ts.map +1 -1
- package/lib/resource.js +13 -2
- package/lib/resource.js.map +1 -1
- package/lib/scope.d.ts +38 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +44 -19
- package/lib/scope.js.map +1 -1
- package/lib/state/cloudflare-state-store.d.ts.map +1 -1
- package/lib/state/cloudflare-state-store.js +2 -1
- package/lib/state/cloudflare-state-store.js.map +1 -1
- package/lib/util/idempotent-spawn.d.ts +24 -0
- package/lib/util/idempotent-spawn.d.ts.map +1 -0
- package/lib/util/idempotent-spawn.js +237 -0
- package/lib/util/idempotent-spawn.js.map +1 -0
- package/lib/util/index.d.ts +2 -0
- package/lib/util/index.d.ts.map +1 -0
- package/lib/util/index.js +2 -0
- package/lib/util/index.js.map +1 -0
- package/lib/util/safe-fetch.d.ts +24 -0
- package/lib/util/safe-fetch.d.ts.map +1 -1
- package/lib/util/safe-fetch.js +72 -0
- package/lib/util/safe-fetch.js.map +1 -1
- package/package.json +9 -5
- package/src/alchemy.ts +3 -2
- package/src/aws/client-props.ts +156 -0
- package/src/aws/credentials.ts +165 -4
- package/src/aws/ec2/internet-gateway-attachment.ts +59 -4
- package/src/aws/ec2/internet-gateway.ts +70 -41
- package/src/aws/ec2/nat-gateway.ts +123 -33
- package/src/aws/ec2/route-table-association.ts +1 -1
- package/src/aws/ec2/route-table.ts +66 -2
- package/src/aws/ec2/route.ts +62 -2
- package/src/aws/ec2/security-group.ts +98 -4
- package/src/aws/ec2/subnet.ts +63 -4
- package/src/aws/ec2/utils.ts +73 -7
- package/src/aws/ec2/vpc.ts +52 -3
- package/src/aws/index.ts +2 -0
- package/src/cloudflare/api-response.ts +2 -2
- package/src/cloudflare/api.ts +23 -0
- package/src/cloudflare/assets.ts +3 -4
- package/src/cloudflare/bucket.ts +39 -29
- package/src/cloudflare/bundle/plugin-compat-warning.ts +7 -7
- package/src/cloudflare/cloudflare-env-proxy.ts +2 -2
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/d1-migrations.ts +4 -5
- package/src/cloudflare/index.ts +2 -1
- package/src/cloudflare/kv-namespace.ts +26 -27
- package/src/cloudflare/miniflare/build-worker-options.ts +16 -3
- package/src/cloudflare/miniflare/miniflare-controller.ts +4 -0
- package/src/cloudflare/oauth.ts +3 -3
- package/src/cloudflare/quick-tunnel.ts +30 -0
- package/src/cloudflare/sveltekit/plugin.ts +5 -5
- package/src/cloudflare/website.ts +30 -111
- package/src/cloudflare/worker-bundle.ts +11 -9
- package/src/cloudflare/worker-tail.ts +6 -8
- package/src/resource.ts +19 -2
- package/src/scope.ts +111 -20
- package/src/state/cloudflare-state-store.ts +2 -1
- package/src/util/idempotent-spawn.ts +303 -0
- package/src/util/index.ts +1 -0
- package/src/util/safe-fetch.ts +114 -0
- package/templates/rwsdk/package.json +1 -1
package/src/cloudflare/bucket.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { Scope } from "../scope.ts";
|
|
|
5
5
|
import { withExponentialBackoff } from "../util/retry.ts";
|
|
6
6
|
import { CloudflareApiError } from "./api-error.ts";
|
|
7
7
|
import {
|
|
8
|
-
extractCloudflareError,
|
|
9
8
|
extractCloudflareResult,
|
|
9
|
+
type CloudflareApiErrorPayload,
|
|
10
10
|
} from "./api-response.ts";
|
|
11
11
|
import {
|
|
12
12
|
createCloudflareApi,
|
|
@@ -478,36 +478,40 @@ async function emptyBucket(
|
|
|
478
478
|
bucketName: string,
|
|
479
479
|
props: BucketProps,
|
|
480
480
|
) {
|
|
481
|
-
let
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
481
|
+
let cursor: string | undefined;
|
|
482
|
+
while (true) {
|
|
483
|
+
const result = await listObjects(api, bucketName, props, cursor);
|
|
484
|
+
if (result.keys.length) {
|
|
485
|
+
// Another undocumented API! But it lets us delete multiple objects at once instead of one by one.
|
|
486
|
+
await extractCloudflareResult(
|
|
487
|
+
`delete ${result.keys.length} objects from bucket "${bucketName}"`,
|
|
486
488
|
api.delete(
|
|
487
|
-
`/accounts/${api.accountId}/r2/buckets/${bucketName}/objects
|
|
488
|
-
{
|
|
489
|
+
`/accounts/${api.accountId}/r2/buckets/${bucketName}/objects`,
|
|
490
|
+
{
|
|
491
|
+
headers: withJurisdiction(props),
|
|
492
|
+
method: "DELETE",
|
|
493
|
+
body: JSON.stringify(result.keys),
|
|
494
|
+
},
|
|
489
495
|
),
|
|
490
|
-
)
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
batch = [];
|
|
496
|
+
);
|
|
497
|
+
if (result.cursor) {
|
|
498
|
+
cursor = result.cursor;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
496
501
|
}
|
|
502
|
+
break;
|
|
497
503
|
}
|
|
498
|
-
await Promise.all(batch);
|
|
499
504
|
}
|
|
500
505
|
|
|
501
506
|
/**
|
|
502
|
-
*
|
|
503
|
-
* handling pagination automatically.
|
|
507
|
+
* Lists objects in a bucket.
|
|
504
508
|
*/
|
|
505
|
-
export async function
|
|
509
|
+
export async function listObjects(
|
|
506
510
|
api: CloudflareApi,
|
|
507
511
|
bucketName: string,
|
|
508
512
|
props: { jurisdiction?: string },
|
|
509
513
|
cursor?: string,
|
|
510
|
-
)
|
|
514
|
+
) {
|
|
511
515
|
const params = new URLSearchParams({
|
|
512
516
|
per_page: "1000",
|
|
513
517
|
});
|
|
@@ -518,11 +522,6 @@ export async function* listObjects(
|
|
|
518
522
|
`/accounts/${api.accountId}/r2/buckets/${bucketName}/objects?${params.toString()}`,
|
|
519
523
|
{ headers: withJurisdiction(props) },
|
|
520
524
|
);
|
|
521
|
-
if (!response.ok) {
|
|
522
|
-
throw new Error(
|
|
523
|
-
`Failed to list objects in bucket "${bucketName}": ${await extractCloudflareError(response)}`,
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
525
|
const json: {
|
|
527
526
|
result: { key: string }[];
|
|
528
527
|
result_info?: {
|
|
@@ -530,13 +529,24 @@ export async function* listObjects(
|
|
|
530
529
|
is_truncated: boolean;
|
|
531
530
|
per_page: number;
|
|
532
531
|
};
|
|
532
|
+
success: boolean;
|
|
533
|
+
errors: CloudflareApiErrorPayload[];
|
|
533
534
|
} = await response.json();
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
535
|
+
if (!json.success) {
|
|
536
|
+
// 10006 indicates that the bucket does not exist, so there are no objects to list
|
|
537
|
+
if (json.errors.some((e) => e.code === 10006)) {
|
|
538
|
+
return { keys: [], cursor: undefined };
|
|
539
|
+
}
|
|
540
|
+
throw new CloudflareApiError(
|
|
541
|
+
`Failed to list objects in bucket "${bucketName}": ${json.errors.map((e) => `- [${e.code}] ${e.message}${e.documentation_url ? ` (${e.documentation_url})` : ""}`).join("\n")}`,
|
|
542
|
+
response,
|
|
543
|
+
json.errors,
|
|
544
|
+
);
|
|
539
545
|
}
|
|
546
|
+
return {
|
|
547
|
+
keys: json.result.map((object) => object.key),
|
|
548
|
+
cursor: json.result_info?.cursor,
|
|
549
|
+
};
|
|
540
550
|
}
|
|
541
551
|
|
|
542
552
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Plugin, PluginBuild } from "esbuild";
|
|
2
|
-
import kleur from "kleur";
|
|
3
2
|
import module from "node:module";
|
|
4
3
|
import path from "node:path";
|
|
4
|
+
import pc from "picocolors";
|
|
5
5
|
import { logger } from "../../util/logger.ts";
|
|
6
6
|
|
|
7
7
|
const NODE_REGEX = new RegExp(
|
|
@@ -54,13 +54,13 @@ export function esbuildPluginCompatWarning(mode: "als" | null): Plugin {
|
|
|
54
54
|
if (packages.size > 0) {
|
|
55
55
|
const formattedImports = Array.from(packages)
|
|
56
56
|
.sort()
|
|
57
|
-
.map((imp) =>
|
|
57
|
+
.map((imp) => pc.yellow(imp))
|
|
58
58
|
.join(", ");
|
|
59
59
|
|
|
60
60
|
logger.warn(
|
|
61
61
|
[
|
|
62
|
-
`Detected Node.js imports (${formattedImports}) but ${
|
|
63
|
-
`Add ${
|
|
62
|
+
`Detected Node.js imports (${formattedImports}) but ${pc.red("nodejs_compat")} compatibility flag is not set. `,
|
|
63
|
+
`Add ${pc.blue("nodejs_compat")} to your compatibility flags and ensure compatibilityDate >= 2024-09-23. Imported from:`,
|
|
64
64
|
formatImporters(
|
|
65
65
|
nodeImporters,
|
|
66
66
|
build.initialOptions.absWorkingDir ?? process.cwd(),
|
|
@@ -72,8 +72,8 @@ export function esbuildPluginCompatWarning(mode: "als" | null): Plugin {
|
|
|
72
72
|
if (alsImporters.size > 0 && !mode) {
|
|
73
73
|
logger.warn(
|
|
74
74
|
[
|
|
75
|
-
`Detected import of ${
|
|
76
|
-
`Add ${
|
|
75
|
+
`Detected import of ${pc.yellow("node:async_hooks")} but ${pc.red("nodejs_als")} compatibility flag is not set. `,
|
|
76
|
+
`Add ${pc.blue("nodejs_als")} or ${pc.blue("nodejs_compat")} to your compatibility flags. Imported from:`,
|
|
77
77
|
formatImporters(
|
|
78
78
|
alsImporters,
|
|
79
79
|
build.initialOptions.absWorkingDir ?? process.cwd(),
|
|
@@ -89,6 +89,6 @@ export function esbuildPluginCompatWarning(mode: "als" | null): Plugin {
|
|
|
89
89
|
function formatImporters(importers: Set<string>, cwd: string) {
|
|
90
90
|
return Array.from(importers)
|
|
91
91
|
.sort()
|
|
92
|
-
.map((imp) => `- ${
|
|
92
|
+
.map((imp) => `- ${pc.yellow(path.relative(cwd, imp))}`)
|
|
93
93
|
.join("\n");
|
|
94
94
|
}
|
|
@@ -41,10 +41,10 @@ export const getPlatformProxyOptions = (
|
|
|
41
41
|
...input,
|
|
42
42
|
configPath: validateConfigPath(input.configPath ?? DEFAULT_CONFIG_PATH),
|
|
43
43
|
persist,
|
|
44
|
-
experimental: input.experimental ?? {
|
|
44
|
+
experimental: (input as any).experimental ?? {
|
|
45
45
|
remoteBindings: true,
|
|
46
46
|
},
|
|
47
|
-
};
|
|
47
|
+
} as any;
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
const warned = new Set<string>();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { glob } from "glob";
|
|
1
2
|
import * as fs from "node:fs/promises";
|
|
2
3
|
import * as path from "node:path";
|
|
3
4
|
import { logger } from "../util/logger.ts";
|
|
@@ -159,11 +160,9 @@ async function migrateLegacySchema(
|
|
|
159
160
|
export async function listMigrationsFiles(
|
|
160
161
|
migrationsDir: string,
|
|
161
162
|
): Promise<Array<{ id: string; sql: string }>> {
|
|
162
|
-
const entries = await
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}),
|
|
166
|
-
);
|
|
163
|
+
const entries = await glob("**/*.sql", {
|
|
164
|
+
cwd: migrationsDir,
|
|
165
|
+
});
|
|
167
166
|
|
|
168
167
|
const sqlFiles = entries.sort((a: string, b: string) => {
|
|
169
168
|
const aNum = getPrefix(a);
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -39,13 +39,14 @@ export * from "./permission-groups.ts";
|
|
|
39
39
|
export * from "./pipeline.ts";
|
|
40
40
|
export * from "./queue-consumer.ts";
|
|
41
41
|
export * from "./queue.ts";
|
|
42
|
+
export * from "./quick-tunnel.ts";
|
|
42
43
|
export * from "./rate-limit.ts";
|
|
43
44
|
export * from "./react-router/react-router.ts";
|
|
44
45
|
export * from "./redirect-rule.ts";
|
|
45
46
|
export * from "./redwood/redwood.ts";
|
|
46
47
|
export * from "./route.ts";
|
|
47
|
-
export * from "./ruleset.ts";
|
|
48
48
|
export * from "./rule.ts";
|
|
49
|
+
export * from "./ruleset.ts";
|
|
49
50
|
export * from "./secret-key.ts";
|
|
50
51
|
export * from "./secret.ts";
|
|
51
52
|
export * from "./secrets-store.ts";
|
|
@@ -100,41 +100,40 @@ export function isKVNamespace(resource: Resource): resource is KVNamespace {
|
|
|
100
100
|
/**
|
|
101
101
|
* Output returned after KV Namespace creation/update
|
|
102
102
|
*/
|
|
103
|
-
export
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
namespaceId: string;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Time at which the namespace was created
|
|
114
|
-
*/
|
|
115
|
-
createdAt: number;
|
|
103
|
+
export type KVNamespace = Resource<"cloudflare::KVNamespace"> &
|
|
104
|
+
Omit<KVNamespaceProps, "delete" | "dev"> & {
|
|
105
|
+
type: "kv_namespace";
|
|
106
|
+
/**
|
|
107
|
+
* The ID of the namespace
|
|
108
|
+
*/
|
|
109
|
+
namespaceId: string;
|
|
116
110
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
/**
|
|
112
|
+
* Time at which the namespace was created
|
|
113
|
+
*/
|
|
114
|
+
createdAt: number;
|
|
121
115
|
|
|
122
|
-
/**
|
|
123
|
-
* Development mode properties
|
|
124
|
-
* @internal
|
|
125
|
-
*/
|
|
126
|
-
dev: {
|
|
127
116
|
/**
|
|
128
|
-
*
|
|
117
|
+
* Time at which the namespace was last modified
|
|
129
118
|
*/
|
|
130
|
-
|
|
119
|
+
modifiedAt: number;
|
|
131
120
|
|
|
132
121
|
/**
|
|
133
|
-
*
|
|
122
|
+
* Development mode properties
|
|
123
|
+
* @internal
|
|
134
124
|
*/
|
|
135
|
-
|
|
125
|
+
dev: {
|
|
126
|
+
/**
|
|
127
|
+
* The ID of the KV namespace in development mode
|
|
128
|
+
*/
|
|
129
|
+
id: string;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Whether the KV namespace is running remotely
|
|
133
|
+
*/
|
|
134
|
+
remote: boolean;
|
|
135
|
+
};
|
|
136
136
|
};
|
|
137
|
-
}
|
|
138
137
|
|
|
139
138
|
/**
|
|
140
139
|
* A Cloudflare KV Namespace is a key-value store that can be used to store data for your application.
|
|
@@ -69,9 +69,15 @@ export const buildWorkerOptions = async (
|
|
|
69
69
|
name: input.name,
|
|
70
70
|
compatibilityDate: input.compatibilityDate,
|
|
71
71
|
compatibilityFlags: input.compatibilityFlags,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
unsafeDirectSockets: [
|
|
73
|
+
// This matches the Wrangler configuration by exposing the default handler (e.g. `export default { fetch }`).
|
|
74
|
+
// However, unlike Wrangler, we set `proxy: false` to avoid the following error when connecting via a websocket:
|
|
75
|
+
// workerd/io/worker.c++:2164: info: uncaught exception; source = Uncaught (in promise); stack = TypeError: Invalid URL string.
|
|
76
|
+
{
|
|
77
|
+
entrypoint: "default",
|
|
78
|
+
proxy: false,
|
|
79
|
+
},
|
|
80
|
+
],
|
|
75
81
|
containerEngine: {
|
|
76
82
|
localDocker: {
|
|
77
83
|
socketPath:
|
|
@@ -164,6 +170,13 @@ export const buildWorkerOptions = async (
|
|
|
164
170
|
scriptName: binding.scriptName,
|
|
165
171
|
useSQLite: binding.sqlite,
|
|
166
172
|
};
|
|
173
|
+
// This matches Wrangler configuration for exposing durable objects as an entrypoint.
|
|
174
|
+
// See https://github.com/cloudflare/workers-sdk/blob/ae0c806087c203da6a3d7da450e8fabe0d81c987/packages/wrangler/src/dev/miniflare/index.ts#L1266
|
|
175
|
+
options.unsafeDirectSockets!.push({
|
|
176
|
+
entrypoint: binding.className,
|
|
177
|
+
serviceName: binding.scriptName,
|
|
178
|
+
proxy: true,
|
|
179
|
+
});
|
|
167
180
|
break;
|
|
168
181
|
}
|
|
169
182
|
case "hyperdrive": {
|
|
@@ -82,6 +82,10 @@ export class MiniflareController {
|
|
|
82
82
|
log: process.env.DEBUG
|
|
83
83
|
? new miniflare.Log(miniflare.LogLevel.DEBUG)
|
|
84
84
|
: undefined,
|
|
85
|
+
|
|
86
|
+
// This is required to allow websites and other separate processes
|
|
87
|
+
// to detect Alchemy-managed Durable Objects via the Wrangler dev registry.
|
|
88
|
+
unsafeDevRegistryDurableObjectProxy: true,
|
|
85
89
|
};
|
|
86
90
|
for (const worker of this.options.values()) {
|
|
87
91
|
options.workers.push(worker);
|
package/src/cloudflare/oauth.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import envPaths from "env-paths";
|
|
2
2
|
import { err, ok, okAsync, ResultAsync } from "neverthrow";
|
|
3
3
|
import assert from "node:assert";
|
|
4
4
|
import crypto from "node:crypto";
|
|
@@ -6,7 +6,7 @@ import fs from "node:fs/promises";
|
|
|
6
6
|
import os from "node:os";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import open from "open";
|
|
9
|
-
import
|
|
9
|
+
import pc from "picocolors";
|
|
10
10
|
import { HTTPServer } from "../util/http.ts";
|
|
11
11
|
import { memoize } from "../util/memoize.ts";
|
|
12
12
|
import {
|
|
@@ -133,7 +133,7 @@ export const wranglerLogin = (
|
|
|
133
133
|
[
|
|
134
134
|
"Opening browser to authorize with Cloudflare...",
|
|
135
135
|
"",
|
|
136
|
-
|
|
136
|
+
pc.gray(
|
|
137
137
|
"If you are not automatically redirected, please open the following URL in your browser:",
|
|
138
138
|
),
|
|
139
139
|
challenge.url,
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Scope } from "../scope.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Starts a Cloudflare Quick Tunnel to a local server.
|
|
5
|
+
*
|
|
6
|
+
* @returns the Local URL and the publicly accessible URL of the Quick Tunnel
|
|
7
|
+
*/
|
|
8
|
+
export const quickTunnel = async (
|
|
9
|
+
scope: Scope,
|
|
10
|
+
localUrl = "http://localhost:8080",
|
|
11
|
+
) => ({
|
|
12
|
+
/**
|
|
13
|
+
* The URL of the local server.
|
|
14
|
+
*/
|
|
15
|
+
localUrl,
|
|
16
|
+
/**
|
|
17
|
+
* The URL of the publicly accessible Quick Tunnel.
|
|
18
|
+
*/
|
|
19
|
+
tunnelUrl: await scope.spawn("tunnel", {
|
|
20
|
+
cmd: `cloudflared tunnel --url ${localUrl}`,
|
|
21
|
+
// used to check if a PID is a cloudflared process when the parent exits, for resumability
|
|
22
|
+
processName: "cloudflared",
|
|
23
|
+
extract: (line) => {
|
|
24
|
+
const match = line.match(/https:\/\/([^\s]+)\.trycloudflare\.com/);
|
|
25
|
+
if (match) {
|
|
26
|
+
return `https://${match[1]}.trycloudflare.com`;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import adapter, { type AdapterOptions } from "@sveltejs/adapter-cloudflare";
|
|
2
2
|
import { getPlatformProxyOptions } from "../cloudflare-env-proxy.ts";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
const platformProxy =
|
|
4
|
+
export default (options?: AdapterOptions) => {
|
|
5
|
+
const { platformProxy: proxyOptions, ...config } = options ?? {};
|
|
6
|
+
const platformProxy = getPlatformProxyOptions(proxyOptions);
|
|
6
7
|
return adapter({
|
|
7
8
|
platformProxy,
|
|
8
|
-
|
|
9
|
+
config: platformProxy.configPath,
|
|
10
|
+
...config,
|
|
9
11
|
});
|
|
10
12
|
};
|
|
11
|
-
|
|
12
|
-
export default alchemy;
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { once } from "node:events";
|
|
4
2
|
import fs from "node:fs/promises";
|
|
5
3
|
import path from "node:path";
|
|
6
4
|
import { alchemy } from "../alchemy.ts";
|
|
7
5
|
import { Exec } from "../os/index.ts";
|
|
8
6
|
import { Scope } from "../scope.ts";
|
|
9
|
-
import { isSecret
|
|
7
|
+
import { isSecret } from "../secret.ts";
|
|
10
8
|
import { dedent } from "../util/dedent.ts";
|
|
11
|
-
import { DeferredPromise } from "../util/deferred-promise.ts";
|
|
12
|
-
import { exists } from "../util/exists.ts";
|
|
13
9
|
import { logger } from "../util/logger.ts";
|
|
14
10
|
import { Assets } from "./assets.ts";
|
|
15
11
|
import type { Bindings } from "./bindings.ts";
|
|
@@ -304,14 +300,38 @@ export async function Website<B extends Bindings>(
|
|
|
304
300
|
|
|
305
301
|
let url: string | undefined;
|
|
306
302
|
if (dev && scope.local) {
|
|
307
|
-
url = await
|
|
308
|
-
|
|
309
|
-
|
|
303
|
+
url = await scope.spawn(name, {
|
|
304
|
+
cmd: typeof dev === "string" ? dev : dev.command,
|
|
305
|
+
cwd: paths.cwd,
|
|
306
|
+
extract: (line) => {
|
|
307
|
+
const URL_REGEX =
|
|
308
|
+
/http:\/\/(localhost|0\.0\.0\.0|127\.0\.0\.1|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}):\d+\/?/;
|
|
309
|
+
const match = line
|
|
310
|
+
.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "")
|
|
311
|
+
.match(URL_REGEX);
|
|
312
|
+
if (match) {
|
|
313
|
+
return match[0];
|
|
314
|
+
}
|
|
315
|
+
},
|
|
310
316
|
env: {
|
|
311
|
-
...
|
|
317
|
+
...Object.fromEntries(
|
|
318
|
+
Object.entries(env ?? {}).flatMap(([key, value]) => {
|
|
319
|
+
if (isSecret(value)) {
|
|
320
|
+
return [[key, value.unencrypted]];
|
|
321
|
+
}
|
|
322
|
+
if (typeof value === "string") {
|
|
323
|
+
return [[key, value]];
|
|
324
|
+
}
|
|
325
|
+
return [];
|
|
326
|
+
}),
|
|
327
|
+
),
|
|
312
328
|
...(typeof dev === "object" ? dev.env : {}),
|
|
329
|
+
FORCE_COLOR: "1",
|
|
330
|
+
...process.env,
|
|
331
|
+
// NOTE: we must set this to ensure the user does not accidentally set `NODE_ENV=production`
|
|
332
|
+
// which breaks `vite dev` (it won't, for example, re-write `process.env.TSS_APP_BASE` in the `.js` client side bundle)
|
|
333
|
+
NODE_ENV: "development",
|
|
313
334
|
},
|
|
314
|
-
cwd: paths.cwd,
|
|
315
335
|
});
|
|
316
336
|
}
|
|
317
337
|
|
|
@@ -348,104 +368,3 @@ async function writeMiniflareSymlink(cwd: string) {
|
|
|
348
368
|
}
|
|
349
369
|
});
|
|
350
370
|
}
|
|
351
|
-
|
|
352
|
-
async function runDevCommand(
|
|
353
|
-
scope: Scope,
|
|
354
|
-
props: {
|
|
355
|
-
id: string;
|
|
356
|
-
command: string;
|
|
357
|
-
env: Record<string, string | Secret<string> | undefined>;
|
|
358
|
-
cwd?: string;
|
|
359
|
-
},
|
|
360
|
-
) {
|
|
361
|
-
const persistFile = path.join(process.cwd(), ".alchemy", `${props.id}.pid`);
|
|
362
|
-
if (await exists(persistFile)) {
|
|
363
|
-
const pid = Number.parseInt(await fs.readFile(persistFile, "utf8"));
|
|
364
|
-
try {
|
|
365
|
-
// Actually kill the process if it's alive
|
|
366
|
-
process.kill(pid, "SIGTERM");
|
|
367
|
-
} catch {
|
|
368
|
-
// ignore
|
|
369
|
-
}
|
|
370
|
-
try {
|
|
371
|
-
await fs.unlink(persistFile);
|
|
372
|
-
} catch {
|
|
373
|
-
// ignore
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
const command = props.command.split(" ");
|
|
377
|
-
const [cmd, ...args] = command;
|
|
378
|
-
|
|
379
|
-
const childProcess = spawn(cmd, args, {
|
|
380
|
-
cwd: props.cwd,
|
|
381
|
-
shell: true,
|
|
382
|
-
env: {
|
|
383
|
-
FORCE_COLOR: "1",
|
|
384
|
-
...process.env,
|
|
385
|
-
...Object.fromEntries(
|
|
386
|
-
Object.entries(props.env ?? {}).flatMap(([key, value]) => {
|
|
387
|
-
if (isSecret(value)) {
|
|
388
|
-
return [[key, value.unencrypted]];
|
|
389
|
-
}
|
|
390
|
-
if (typeof value === "string") {
|
|
391
|
-
return [[key, value]];
|
|
392
|
-
}
|
|
393
|
-
return [];
|
|
394
|
-
}),
|
|
395
|
-
),
|
|
396
|
-
// NOTE: we must set this to ensure the user does not accidentally set `NODE_ENV=production`
|
|
397
|
-
// which breaks `vite dev` (it won't, for example, re-write `process.env.TSS_APP_BASE` in the `.js` client side bundle)
|
|
398
|
-
NODE_ENV: "development",
|
|
399
|
-
},
|
|
400
|
-
stdio: "pipe",
|
|
401
|
-
});
|
|
402
|
-
await once(childProcess, "spawn");
|
|
403
|
-
scope.onCleanup(async () => {
|
|
404
|
-
if (childProcess.exitCode === null && !childProcess.killed) {
|
|
405
|
-
childProcess.kill("SIGTERM");
|
|
406
|
-
await Promise.any([
|
|
407
|
-
once(childProcess, "exit"),
|
|
408
|
-
new Promise((resolve) => setTimeout(resolve, 5000)),
|
|
409
|
-
]);
|
|
410
|
-
if (!childProcess.killed) {
|
|
411
|
-
childProcess.kill("SIGKILL");
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
try {
|
|
415
|
-
await fs.unlink(persistFile);
|
|
416
|
-
} catch {
|
|
417
|
-
// ignore
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
if (childProcess.pid) {
|
|
421
|
-
await fs.mkdir(path.dirname(persistFile), { recursive: true });
|
|
422
|
-
await fs.writeFile(persistFile, childProcess.pid.toString());
|
|
423
|
-
}
|
|
424
|
-
const URL_REGEX =
|
|
425
|
-
/http:\/\/(?:(?:localhost|0\.0\.0\.0|127\.0\.0\.1)|(?:\d{1,3}\.){3}\d{1,3}):\d+(?:\/)?/;
|
|
426
|
-
const promise = new DeferredPromise<string>();
|
|
427
|
-
childProcess.stdout.on("data", (data) => {
|
|
428
|
-
if (promise.status === "pending") {
|
|
429
|
-
const match = data
|
|
430
|
-
.toString()
|
|
431
|
-
.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "")
|
|
432
|
-
.match(URL_REGEX);
|
|
433
|
-
if (match) {
|
|
434
|
-
promise.resolve(match[0]);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
process.stdout.write(data);
|
|
438
|
-
});
|
|
439
|
-
childProcess.stderr.on("data", (data) => {
|
|
440
|
-
process.stderr.write(data);
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
return await Promise.race([
|
|
444
|
-
promise.value,
|
|
445
|
-
once(childProcess, "exit").then(([code, signal]) => {
|
|
446
|
-
throw new Error(
|
|
447
|
-
`Dev command "${props.command}" for "${props.id}" failed to start (code: ${code}, signal: ${signal})`,
|
|
448
|
-
);
|
|
449
|
-
}),
|
|
450
|
-
]);
|
|
451
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import esbuild from "esbuild";
|
|
2
|
+
import { globIterate } from "glob";
|
|
2
3
|
import { err, ok, type Result } from "neverthrow";
|
|
3
4
|
import fs from "node:fs/promises";
|
|
4
5
|
import path from "pathe";
|
|
@@ -92,21 +93,22 @@ export namespace WorkerBundle {
|
|
|
92
93
|
paths: string[],
|
|
93
94
|
format: "esm" | "cjs",
|
|
94
95
|
): WorkerBundle.Module[] => {
|
|
95
|
-
return paths.map((
|
|
96
|
-
const
|
|
96
|
+
return paths.map((filePath) => {
|
|
97
|
+
const normalizedPath = path.normalize(filePath);
|
|
98
|
+
const ext = normalizedPath.split(".").pop();
|
|
97
99
|
switch (ext) {
|
|
98
100
|
case "js":
|
|
99
|
-
return { type: format, path };
|
|
101
|
+
return { type: format, path: normalizedPath };
|
|
100
102
|
case "mjs":
|
|
101
|
-
return { type: "esm", path };
|
|
103
|
+
return { type: "esm", path: normalizedPath };
|
|
102
104
|
case "cjs":
|
|
103
|
-
return { type: "cjs", path };
|
|
105
|
+
return { type: "cjs", path: normalizedPath };
|
|
104
106
|
case "wasm":
|
|
105
|
-
return { type: "wasm", path };
|
|
107
|
+
return { type: "wasm", path: normalizedPath };
|
|
106
108
|
case "map":
|
|
107
|
-
return { type: "sourcemap", path };
|
|
109
|
+
return { type: "sourcemap", path: normalizedPath };
|
|
108
110
|
default:
|
|
109
|
-
return { type: "text", path };
|
|
111
|
+
return { type: "text", path: normalizedPath };
|
|
110
112
|
}
|
|
111
113
|
});
|
|
112
114
|
};
|
|
@@ -222,7 +224,7 @@ export namespace WorkerBundleSource {
|
|
|
222
224
|
const fileNames = new Set<string>();
|
|
223
225
|
await Promise.all(
|
|
224
226
|
this.globs.map(async (glob) => {
|
|
225
|
-
for await (const file of
|
|
227
|
+
for await (const file of globIterate(glob, { cwd: this.root })) {
|
|
226
228
|
fileNames.add(file);
|
|
227
229
|
}
|
|
228
230
|
}),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pc from "picocolors";
|
|
2
2
|
import { WebSocket } from "ws";
|
|
3
3
|
import { logger } from "../util/logger.ts";
|
|
4
4
|
import type { CloudflareApiResponse } from "./api-response.ts";
|
|
@@ -102,7 +102,7 @@ class TailClient {
|
|
|
102
102
|
});
|
|
103
103
|
ws.on("message", (message) => {
|
|
104
104
|
const data: TailEventMessage = JSON.parse(message.toString());
|
|
105
|
-
const prefix =
|
|
105
|
+
const prefix = pc.blue(`[${this.id}]`);
|
|
106
106
|
if (data.event && "request" in data.event) {
|
|
107
107
|
// TODO: handle other event types
|
|
108
108
|
const event = data.event;
|
|
@@ -110,18 +110,16 @@ class TailClient {
|
|
|
110
110
|
const status = event.response?.status ?? 500;
|
|
111
111
|
// TODO: make this look nicer
|
|
112
112
|
logger.log(
|
|
113
|
-
`${prefix} ${
|
|
113
|
+
`${prefix} ${pc.gray(event.request.method)} ${url.pathname} ${pc.dim(">")} ${status >= 200 && status < 300 ? pc.green(status) : pc.red(status)} ${pc.gray(`(cpu: ${Math.round(data.cpuTime)}ms, wall: ${Math.round(data.wallTime)}ms)`)}`,
|
|
114
114
|
);
|
|
115
115
|
}
|
|
116
116
|
for (const log of data.logs) {
|
|
117
|
-
logger.log(
|
|
118
|
-
`${prefix} ${kleur.gray(log.level)} ${log.message.join(" ")}`,
|
|
119
|
-
);
|
|
117
|
+
logger.log(`${prefix} ${pc.gray(log.level)} ${log.message.join(" ")}`);
|
|
120
118
|
}
|
|
121
119
|
for (const exception of data.exceptions) {
|
|
122
|
-
const start = `${prefix} ${
|
|
120
|
+
const start = `${prefix} ${pc.red(exception.name)}`;
|
|
123
121
|
logger.log(
|
|
124
|
-
`${start} ${exception.message}\n${
|
|
122
|
+
`${start} ${exception.message}\n${pc.gray(exception.stack)}`,
|
|
125
123
|
);
|
|
126
124
|
}
|
|
127
125
|
});
|