alchemy 0.70.2 → 0.71.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 +13 -15
- package/bin/commands/init.ts +0 -10
- package/lib/apply.d.ts.map +1 -1
- package/lib/apply.js +2 -0
- package/lib/apply.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +12 -2
- package/lib/cloudflare/bindings.d.ts.map +1 -1
- package/lib/cloudflare/bindings.js.map +1 -1
- package/lib/cloudflare/bound.d.ts +3 -2
- package/lib/cloudflare/bound.d.ts.map +1 -1
- package/lib/cloudflare/bucket.d.ts +6 -0
- package/lib/cloudflare/bucket.d.ts.map +1 -1
- package/lib/cloudflare/bucket.js +5 -3
- package/lib/cloudflare/bucket.js.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts +6 -1
- package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -1
- package/lib/cloudflare/bun-spa/bun-spa.js +29 -9
- package/lib/cloudflare/bun-spa/bun-spa.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/index.d.ts +2 -0
- package/lib/cloudflare/index.d.ts.map +1 -1
- package/lib/cloudflare/index.js +2 -0
- package/lib/cloudflare/index.js.map +1 -1
- package/lib/cloudflare/logpush-job.d.ts +349 -0
- package/lib/cloudflare/logpush-job.d.ts.map +1 -0
- package/lib/cloudflare/logpush-job.js +277 -0
- package/lib/cloudflare/logpush-job.js.map +1 -0
- package/lib/cloudflare/miniflare/build-worker-options.d.ts +1 -0
- package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/build-worker-options.js +16 -6
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
- package/lib/cloudflare/miniflare/remote-binding-proxy.js +2 -0
- package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
- package/lib/cloudflare/queue-consumer.js +2 -2
- package/lib/cloudflare/queue-consumer.js.map +1 -1
- package/lib/cloudflare/worker-loader.d.ts +9 -0
- package/lib/cloudflare/worker-loader.d.ts.map +1 -0
- package/lib/cloudflare/worker-loader.js +6 -0
- package/lib/cloudflare/worker-loader.js.map +1 -0
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +6 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/worker-subdomain.js +1 -1
- package/lib/cloudflare/worker.d.ts.map +1 -1
- package/lib/cloudflare/worker.js +7 -0
- package/lib/cloudflare/worker.js.map +1 -1
- package/lib/cloudflare/wrangler.json.d.ts +3 -0
- package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
- package/lib/cloudflare/wrangler.json.js +9 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/lib/github/comment.d.ts.map +1 -1
- package/lib/github/comment.js +14 -7
- package/lib/github/comment.js.map +1 -1
- package/lib/scope.d.ts.map +1 -1
- package/lib/scope.js +6 -7
- package/lib/scope.js.map +1 -1
- package/lib/util/retry.d.ts +1 -1
- package/lib/util/retry.d.ts.map +1 -1
- package/lib/util/retry.js +2 -2
- package/lib/util/retry.js.map +1 -1
- package/lib/util/validate-resource-id.d.ts +2 -0
- package/lib/util/validate-resource-id.d.ts.map +1 -0
- package/lib/util/validate-resource-id.js +11 -0
- package/lib/util/validate-resource-id.js.map +1 -0
- package/package.json +2 -2
- package/src/apply.ts +2 -0
- package/src/cloudflare/bindings.ts +13 -0
- package/src/cloudflare/bound.ts +36 -32
- package/src/cloudflare/bucket.ts +13 -4
- package/src/cloudflare/bun-spa/bun-spa.ts +42 -10
- package/src/cloudflare/compatibility-date.gen.ts +1 -1
- package/src/cloudflare/index.ts +2 -0
- package/src/cloudflare/logpush-job.ts +768 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +18 -6
- package/src/cloudflare/miniflare/remote-binding-proxy.ts +2 -0
- package/src/cloudflare/queue-consumer.ts +2 -2
- package/src/cloudflare/worker-loader.ts +12 -0
- package/src/cloudflare/worker-metadata.ts +5 -0
- package/src/cloudflare/worker-subdomain.ts +1 -1
- package/src/cloudflare/worker.ts +8 -0
- package/src/cloudflare/wrangler.json.ts +15 -0
- package/src/github/comment.ts +17 -8
- package/src/scope.ts +7 -10
- package/src/util/retry.ts +2 -1
- package/src/util/validate-resource-id.ts +10 -0
- package/workers/tunnel-proxy.js +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as miniflare from "miniflare";
|
|
2
|
+
import path from "pathe";
|
|
2
3
|
import { assertNever } from "../../util/assert-never.ts";
|
|
3
4
|
import type { HTTPServer } from "../../util/http.ts";
|
|
4
5
|
import { logger } from "../../util/logger.ts";
|
|
@@ -26,6 +27,7 @@ export interface MiniflareWorkerInput {
|
|
|
26
27
|
bundle: WorkerBundleSource;
|
|
27
28
|
port: number | undefined;
|
|
28
29
|
tunnel: boolean | undefined;
|
|
30
|
+
cwd: string;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
type RemoteOnlyBindingType =
|
|
@@ -78,11 +80,6 @@ export const buildWorkerOptions = async (
|
|
|
78
80
|
proxy: true,
|
|
79
81
|
},
|
|
80
82
|
],
|
|
81
|
-
containerEngine: {
|
|
82
|
-
localDocker: {
|
|
83
|
-
socketPath: await getDockerSocketPath(),
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
83
|
// This exposes the worker as a route that can be accessed by setting the MF-Route-Override header.
|
|
87
84
|
routes: [input.name],
|
|
88
85
|
};
|
|
@@ -119,7 +116,7 @@ export const buildWorkerOptions = async (
|
|
|
119
116
|
case "assets": {
|
|
120
117
|
options.assets = {
|
|
121
118
|
binding: key,
|
|
122
|
-
directory: binding.path,
|
|
119
|
+
directory: path.resolve(input.cwd, binding.path),
|
|
123
120
|
assetConfig: {
|
|
124
121
|
html_handling: input.assets?.html_handling,
|
|
125
122
|
not_found_handling: input.assets?.not_found_handling,
|
|
@@ -144,6 +141,11 @@ export const buildWorkerOptions = async (
|
|
|
144
141
|
imageName: binding.image.imageRef,
|
|
145
142
|
},
|
|
146
143
|
};
|
|
144
|
+
options.containerEngine = {
|
|
145
|
+
localDocker: {
|
|
146
|
+
socketPath: await getDockerSocketPath(),
|
|
147
|
+
},
|
|
148
|
+
};
|
|
147
149
|
break;
|
|
148
150
|
}
|
|
149
151
|
case "d1": {
|
|
@@ -268,6 +270,10 @@ export const buildWorkerOptions = async (
|
|
|
268
270
|
type: "r2_bucket",
|
|
269
271
|
name: key,
|
|
270
272
|
bucket_name: binding.name,
|
|
273
|
+
jurisdiction:
|
|
274
|
+
binding.jurisdiction === "default"
|
|
275
|
+
? undefined
|
|
276
|
+
: binding.jurisdiction,
|
|
271
277
|
raw: true,
|
|
272
278
|
});
|
|
273
279
|
} else {
|
|
@@ -308,6 +314,10 @@ export const buildWorkerOptions = async (
|
|
|
308
314
|
};
|
|
309
315
|
break;
|
|
310
316
|
}
|
|
317
|
+
case "worker_loader": {
|
|
318
|
+
(options.workerLoaders ??= {})[key] = {};
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
311
321
|
case "workflow": {
|
|
312
322
|
(options.workflows ??= {})[key] = {
|
|
313
323
|
name: binding.workflowName,
|
|
@@ -329,11 +339,13 @@ export const buildWorkerOptions = async (
|
|
|
329
339
|
);
|
|
330
340
|
}
|
|
331
341
|
if (isQueueEventSource(eventSource)) {
|
|
342
|
+
const dlq = eventSource.settings?.deadLetterQueue;
|
|
332
343
|
(options.queueConsumers ??= {})[queue.name] = {
|
|
333
344
|
maxBatchSize: eventSource.settings?.batchSize,
|
|
334
345
|
maxBatchTimeout: eventSource.settings?.maxWaitTimeMs,
|
|
335
346
|
maxRetries: eventSource.settings?.maxRetries,
|
|
336
347
|
retryDelay: eventSource.settings?.retryDelay,
|
|
348
|
+
deadLetterQueue: typeof dlq === "string" ? dlq : dlq?.name,
|
|
337
349
|
};
|
|
338
350
|
} else {
|
|
339
351
|
(options.queueConsumers ??= {})[eventSource.name] = {};
|
|
@@ -82,6 +82,8 @@ export async function createRemoteProxyWorker(input: {
|
|
|
82
82
|
headers,
|
|
83
83
|
body: req.body,
|
|
84
84
|
redirect: "manual",
|
|
85
|
+
// @ts-expect-error - caused by @cloudflare/workers-types
|
|
86
|
+
duplex: req.body ? "half" : undefined,
|
|
85
87
|
});
|
|
86
88
|
|
|
87
89
|
const responseHeaders = new Headers(res.headers);
|
|
@@ -318,7 +318,7 @@ export async function createQueueConsumer(
|
|
|
318
318
|
typeof props.settings.deadLetterQueue === "string"
|
|
319
319
|
? props.settings.deadLetterQueue
|
|
320
320
|
: props.settings.deadLetterQueue.name;
|
|
321
|
-
createPayload.
|
|
321
|
+
createPayload.dead_letter_queue = dlqName;
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
@@ -405,7 +405,7 @@ async function updateQueueConsumer(
|
|
|
405
405
|
typeof props.settings.deadLetterQueue === "string"
|
|
406
406
|
? props.settings.deadLetterQueue
|
|
407
407
|
: props.settings.deadLetterQueue.name;
|
|
408
|
-
updatePayload.
|
|
408
|
+
updatePayload.dead_letter_queue = dlqName;
|
|
409
409
|
}
|
|
410
410
|
}
|
|
411
411
|
|
|
@@ -568,6 +568,11 @@ export async function prepareWorkerMetadata(
|
|
|
568
568
|
name: bindingName,
|
|
569
569
|
namespace: binding.namespaceName,
|
|
570
570
|
});
|
|
571
|
+
} else if (binding.type === "worker_loader") {
|
|
572
|
+
meta.bindings.push({
|
|
573
|
+
type: "worker_loader",
|
|
574
|
+
name: bindingName,
|
|
575
|
+
});
|
|
571
576
|
} else if (binding.type === "secret_key") {
|
|
572
577
|
meta.bindings.push({
|
|
573
578
|
type: "secret_key",
|
package/src/cloudflare/worker.ts
CHANGED
|
@@ -982,9 +982,11 @@ const _Worker = Resource(
|
|
|
982
982
|
bundle,
|
|
983
983
|
port: props.dev?.port,
|
|
984
984
|
tunnel: props.dev?.tunnel ?? this.scope.tunnel,
|
|
985
|
+
cwd: props.cwd ?? process.cwd(),
|
|
985
986
|
});
|
|
986
987
|
this.onCleanup(() => controller.dispose());
|
|
987
988
|
}
|
|
989
|
+
|
|
988
990
|
await provisionResources(
|
|
989
991
|
{
|
|
990
992
|
...props,
|
|
@@ -1240,12 +1242,18 @@ async function provisionResources<B extends Bindings>(
|
|
|
1240
1242
|
containers: options.containers,
|
|
1241
1243
|
domains: props.domains?.map((domain) => {
|
|
1242
1244
|
if (typeof domain === "string") {
|
|
1245
|
+
if (domain === "") {
|
|
1246
|
+
throw new Error("Domain names cannot be empty strings");
|
|
1247
|
+
}
|
|
1243
1248
|
return {
|
|
1244
1249
|
name: domain,
|
|
1245
1250
|
zoneId: undefined,
|
|
1246
1251
|
adopt: props.adopt,
|
|
1247
1252
|
};
|
|
1248
1253
|
}
|
|
1254
|
+
if (domain.domainName === "") {
|
|
1255
|
+
throw new Error("Domain names cannot be empty strings");
|
|
1256
|
+
}
|
|
1249
1257
|
return {
|
|
1250
1258
|
name: domain.domainName,
|
|
1251
1259
|
zoneId: domain.zoneId,
|
|
@@ -376,6 +376,10 @@ export interface WranglerJsonSpec {
|
|
|
376
376
|
entrypoint?: string;
|
|
377
377
|
}[];
|
|
378
378
|
|
|
379
|
+
worker_loaders?: {
|
|
380
|
+
binding: string;
|
|
381
|
+
}[];
|
|
382
|
+
|
|
379
383
|
/**
|
|
380
384
|
* Workflow bindings
|
|
381
385
|
*/
|
|
@@ -627,6 +631,9 @@ function processBindings(
|
|
|
627
631
|
const containers: {
|
|
628
632
|
class_name: string;
|
|
629
633
|
}[] = [];
|
|
634
|
+
const workerLoaders: {
|
|
635
|
+
binding: string;
|
|
636
|
+
}[] = [];
|
|
630
637
|
|
|
631
638
|
for (const eventSource of eventSources ?? []) {
|
|
632
639
|
if (isQueueEventSource(eventSource)) {
|
|
@@ -850,6 +857,10 @@ function processBindings(
|
|
|
850
857
|
containers.push({
|
|
851
858
|
class_name: binding.className,
|
|
852
859
|
});
|
|
860
|
+
} else if (binding.type === "worker_loader") {
|
|
861
|
+
workerLoaders.push({
|
|
862
|
+
binding: bindingName,
|
|
863
|
+
});
|
|
853
864
|
} else {
|
|
854
865
|
console.log("binding", binding);
|
|
855
866
|
// biome-ignore lint/correctness/noVoidTypeReturn: it returns never
|
|
@@ -930,4 +941,8 @@ function processBindings(
|
|
|
930
941
|
bindings: unsafeBindings,
|
|
931
942
|
};
|
|
932
943
|
}
|
|
944
|
+
|
|
945
|
+
if (workerLoaders.length > 0) {
|
|
946
|
+
spec.worker_loaders = workerLoaders;
|
|
947
|
+
}
|
|
933
948
|
}
|
package/src/github/comment.ts
CHANGED
|
@@ -173,17 +173,17 @@ export const GitHubComment = Resource(
|
|
|
173
173
|
props: GitHubCommentProps,
|
|
174
174
|
): Promise<GitHubComment> {
|
|
175
175
|
// Create authenticated Octokit client - will automatically handle token resolution
|
|
176
|
-
const octokit = await createGitHubClient({
|
|
177
|
-
token: props.token?.unencrypted,
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Verify authentication and permissions
|
|
181
|
-
if (!this.quiet) {
|
|
182
|
-
await verifyGitHubAuth(octokit, props.owner, props.repository);
|
|
183
|
-
}
|
|
184
176
|
|
|
185
177
|
if (this.phase === "delete") {
|
|
186
178
|
if (this.output?.commentId && props.allowDelete) {
|
|
179
|
+
const octokit = await createGitHubClient({
|
|
180
|
+
token: props.token?.unencrypted,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Verify authentication and permissions
|
|
184
|
+
if (!this.quiet) {
|
|
185
|
+
await verifyGitHubAuth(octokit, props.owner, props.repository);
|
|
186
|
+
}
|
|
187
187
|
try {
|
|
188
188
|
// Delete the comment
|
|
189
189
|
await octokit.rest.issues.deleteComment({
|
|
@@ -209,6 +209,15 @@ export const GitHubComment = Resource(
|
|
|
209
209
|
return this.destroy();
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
const octokit = await createGitHubClient({
|
|
213
|
+
token: props.token?.unencrypted,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Verify authentication and permissions
|
|
217
|
+
if (!this.quiet) {
|
|
218
|
+
await verifyGitHubAuth(octokit, props.owner, props.repository);
|
|
219
|
+
}
|
|
220
|
+
|
|
212
221
|
try {
|
|
213
222
|
if (this.phase === "update" && this.output?.commentId) {
|
|
214
223
|
// Update existing comment
|
package/src/scope.ts
CHANGED
|
@@ -30,6 +30,7 @@ import { logger } from "./util/logger.ts";
|
|
|
30
30
|
import { AsyncMutex } from "./util/mutex.ts";
|
|
31
31
|
import { ALCHEMY_ROOT } from "./util/root-dir.ts";
|
|
32
32
|
import { createAndSendEvent } from "./util/telemetry.ts";
|
|
33
|
+
import { validateResourceID } from "./util/validate-resource-id.ts";
|
|
33
34
|
|
|
34
35
|
export class RootScopeStateAttemptError extends Error {
|
|
35
36
|
constructor() {
|
|
@@ -265,21 +266,17 @@ export class Scope {
|
|
|
265
266
|
// Store provider credentials (TypeScript ensures no conflicts with core options)
|
|
266
267
|
this.providerCredentials = providerCredentials as ProviderCredentials;
|
|
267
268
|
|
|
268
|
-
const isChild = this.parent !== undefined;
|
|
269
|
-
if (this.scopeName?.includes(":") && isChild) {
|
|
270
|
-
// TODO(sam): relax this constraint once we move to SQLite3 store
|
|
271
|
-
throw new Error(
|
|
272
|
-
`Scope name "${this.scopeName}" cannot contain double colons`,
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
269
|
this.stage = stage ?? this.parent?.stage ?? DEFAULT_STAGE;
|
|
277
270
|
this.profile = profile ?? this.parent?.profile;
|
|
278
271
|
this.parent?.children.set(this.scopeName!, this);
|
|
279
272
|
this.quiet = quiet ?? this.parent?.quiet ?? false;
|
|
280
|
-
if (this.parent
|
|
281
|
-
|
|
273
|
+
if (this.parent) {
|
|
274
|
+
if (!this.scopeName) {
|
|
275
|
+
throw new Error("Scope name is required when creating a child scope");
|
|
276
|
+
}
|
|
277
|
+
validateResourceID(this.scopeName, "Scope");
|
|
282
278
|
}
|
|
279
|
+
|
|
283
280
|
this.password = password ?? this.parent?.password;
|
|
284
281
|
this.noTrack = noTrack ?? this.parent?.noTrack ?? false;
|
|
285
282
|
const resolvedPhase = phase ?? this.parent?.phase;
|
package/src/util/retry.ts
CHANGED
|
@@ -14,6 +14,7 @@ export async function withExponentialBackoff<T>(
|
|
|
14
14
|
isRetryable: (error: any) => any,
|
|
15
15
|
maxAttempts = 5,
|
|
16
16
|
initialDelayMs = 100,
|
|
17
|
+
maxDelayMs = 10000,
|
|
17
18
|
): Promise<T> {
|
|
18
19
|
let attempt = 0;
|
|
19
20
|
let delay = initialDelayMs;
|
|
@@ -31,7 +32,7 @@ export async function withExponentialBackoff<T>(
|
|
|
31
32
|
const jitter = Math.random() * 0.1 * delay;
|
|
32
33
|
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
33
34
|
delay *= 2; // Double the delay for next attempt
|
|
34
|
-
delay = Math.min(delay,
|
|
35
|
+
delay = Math.min(delay, maxDelayMs); // Cap at 10 seconds
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function validateResourceID(id: string, qualifier = "Resource"): void {
|
|
2
|
+
//todo(sam): during effect rewrite, this should be much more limiting
|
|
3
|
+
//todo: its hard to undo the flexibility we've offered to users now
|
|
4
|
+
if (!id) {
|
|
5
|
+
throw new Error(`${qualifier} ID cannot be an empty string: "${id}"`);
|
|
6
|
+
}
|
|
7
|
+
if (id.includes(":")) {
|
|
8
|
+
throw new Error(`${qualifier} ID cannot include colons: "${id}"`);
|
|
9
|
+
}
|
|
10
|
+
}
|
package/workers/tunnel-proxy.js
CHANGED
|
@@ -34,7 +34,7 @@ var h={async fetch(e,r){let n=new URL(e.url);n.host=r.TUNNEL_HOST;let l=new Head
|
|
|
34
34
|
Alchemy</a>.</p>
|
|
35
35
|
</div>
|
|
36
36
|
<div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
|
|
37
|
-
<p class="text-sm text-slate-500">Alchemy 0.
|
|
37
|
+
<p class="text-sm text-slate-500">Alchemy 0.71.0</p>
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
40
40
|
</body>
|