alchemy 0.91.2 → 0.92.2
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 +2 -12
- package/bin/commands/util.ts +0 -3
- package/lib/cloudflare/auth.d.ts +0 -3
- package/lib/cloudflare/auth.d.ts.map +1 -1
- package/lib/cloudflare/auth.js +0 -3
- package/lib/cloudflare/auth.js.map +1 -1
- package/lib/cloudflare/bindings.d.ts +10 -2
- 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/email-address.d.ts +11 -2
- package/lib/cloudflare/email-address.d.ts.map +1 -1
- package/lib/cloudflare/email-address.js +20 -8
- package/lib/cloudflare/email-address.js.map +1 -1
- package/lib/cloudflare/email-catch-all.d.ts +13 -5
- package/lib/cloudflare/email-catch-all.d.ts.map +1 -1
- package/lib/cloudflare/email-catch-all.js +21 -4
- package/lib/cloudflare/email-catch-all.js.map +1 -1
- package/lib/cloudflare/email-common.d.ts +4 -0
- package/lib/cloudflare/email-common.d.ts.map +1 -0
- package/lib/cloudflare/email-common.js +7 -0
- package/lib/cloudflare/email-common.js.map +1 -0
- package/lib/cloudflare/email-routing.d.ts +1 -0
- package/lib/cloudflare/email-routing.d.ts.map +1 -1
- package/lib/cloudflare/email-routing.js +6 -2
- package/lib/cloudflare/email-routing.js.map +1 -1
- package/lib/cloudflare/email-rule.d.ts +4 -3
- package/lib/cloudflare/email-rule.d.ts.map +1 -1
- package/lib/cloudflare/email-rule.js +33 -4
- package/lib/cloudflare/email-rule.js.map +1 -1
- package/lib/cloudflare/email-sender.d.ts +38 -0
- package/lib/cloudflare/email-sender.d.ts.map +1 -0
- package/lib/cloudflare/email-sender.js +23 -0
- package/lib/cloudflare/email-sender.js.map +1 -0
- 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 +29 -0
- package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js +14 -1
- package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
- package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
- package/lib/cloudflare/worker-metadata.js +9 -0
- package/lib/cloudflare/worker-metadata.js.map +1 -1
- package/lib/cloudflare/wrangler.json.js +13 -0
- package/lib/cloudflare/wrangler.json.js.map +1 -1
- package/package.json +3 -3
- package/src/cloudflare/auth.ts +0 -3
- package/src/cloudflare/bindings.ts +11 -0
- package/src/cloudflare/bound.ts +56 -53
- package/src/cloudflare/email-address.ts +39 -12
- package/src/cloudflare/email-catch-all.ts +41 -10
- package/src/cloudflare/email-common.ts +11 -0
- package/src/cloudflare/email-routing.ts +7 -2
- package/src/cloudflare/email-rule.ts +43 -7
- package/src/cloudflare/email-sender.ts +49 -0
- package/src/cloudflare/index.ts +1 -0
- package/src/cloudflare/miniflare/build-worker-options.ts +69 -1
- package/src/cloudflare/miniflare/miniflare-controller.ts +18 -1
- package/src/cloudflare/worker-metadata.ts +8 -0
- package/src/cloudflare/wrangler.json.ts +17 -0
- package/workers/cloudflare-state-store.js +57 -57
- package/workers/remote-binding-proxy.js +7 -3
- package/workers/remote-binding-proxy.ts +20 -3
- package/workers/tunnel-proxy.js +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Context } from "../context.ts";
|
|
2
|
-
import { Resource } from "../resource.ts";
|
|
2
|
+
import { Resource, ResourceKind } from "../resource.ts";
|
|
3
3
|
import { handleApiError } from "./api-error.ts";
|
|
4
4
|
import { type CloudflareApiOptions, createCloudflareApi } from "./api.ts";
|
|
5
|
+
import { resolveEmailZoneId } from "./email-common.ts";
|
|
5
6
|
import type { CloudflareResponse } from "./response.ts";
|
|
6
7
|
import type { Zone } from "./zone.ts";
|
|
7
8
|
|
|
@@ -19,7 +20,7 @@ export interface EmailMatcher {
|
|
|
19
20
|
/**
|
|
20
21
|
* Field to match against (required for literal matchers)
|
|
21
22
|
*/
|
|
22
|
-
field?: "to"
|
|
23
|
+
field?: "to";
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Value to match (required for literal matchers)
|
|
@@ -141,9 +142,42 @@ export interface EmailRule {
|
|
|
141
142
|
actions: EmailAction[];
|
|
142
143
|
|
|
143
144
|
/**
|
|
144
|
-
*
|
|
145
|
+
* Deprecated rule tag returned by the Cloudflare API.
|
|
145
146
|
*/
|
|
146
|
-
tag
|
|
147
|
+
tag?: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function isEmailRule(resource: any): resource is EmailRule {
|
|
151
|
+
return resource?.[ResourceKind] === "cloudflare::EmailRule";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function validateMatchers(matchers: EmailMatcher[]) {
|
|
155
|
+
for (const matcher of matchers) {
|
|
156
|
+
if (matcher.type === "all") {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (matcher.field !== "to") {
|
|
160
|
+
throw new Error(
|
|
161
|
+
'EmailRule literal matchers currently only support field: "to".',
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (!matcher.value) {
|
|
165
|
+
throw new Error("EmailRule literal matchers require a value.");
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function validateActions(actions: EmailAction[]) {
|
|
171
|
+
for (const action of actions) {
|
|
172
|
+
if (action.type === "drop") {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (!action.value?.length) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`EmailRule action "${action.type}" requires at least one value.`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
147
181
|
}
|
|
148
182
|
|
|
149
183
|
/**
|
|
@@ -235,7 +269,9 @@ export const EmailRule = Resource(
|
|
|
235
269
|
props: EmailRuleProps,
|
|
236
270
|
): Promise<EmailRule> {
|
|
237
271
|
const api = await createCloudflareApi(props);
|
|
238
|
-
const zoneId =
|
|
272
|
+
const zoneId = await resolveEmailZoneId(api, props.zone);
|
|
273
|
+
validateMatchers(props.matchers);
|
|
274
|
+
validateActions(props.actions);
|
|
239
275
|
|
|
240
276
|
if (this.phase === "delete") {
|
|
241
277
|
if (this.output?.ruleId) {
|
|
@@ -282,7 +318,7 @@ export const EmailRule = Resource(
|
|
|
282
318
|
priority: result.result.priority,
|
|
283
319
|
matchers: result.result.matchers,
|
|
284
320
|
actions: result.result.actions,
|
|
285
|
-
tag: result.result.tag,
|
|
321
|
+
tag: result.result.tag || undefined,
|
|
286
322
|
};
|
|
287
323
|
}
|
|
288
324
|
}
|
|
@@ -316,7 +352,7 @@ export const EmailRule = Resource(
|
|
|
316
352
|
priority: result.result.priority,
|
|
317
353
|
matchers: result.result.matchers,
|
|
318
354
|
actions: result.result.actions,
|
|
319
|
-
tag: result.result.tag,
|
|
355
|
+
tag: result.result.tag || undefined,
|
|
320
356
|
};
|
|
321
357
|
},
|
|
322
358
|
);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface BaseEmailSenderProps {
|
|
2
|
+
allowedSenderAddresses?: string[];
|
|
3
|
+
dev?: {
|
|
4
|
+
remote?: boolean;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type EmailSenderProps = BaseEmailSenderProps &
|
|
9
|
+
(
|
|
10
|
+
| {
|
|
11
|
+
destinationAddress?: string;
|
|
12
|
+
allowedDestinationAddresses?: never;
|
|
13
|
+
}
|
|
14
|
+
| {
|
|
15
|
+
destinationAddress?: never;
|
|
16
|
+
allowedDestinationAddresses?: string[];
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Type representing a Cloudflare Email Service send binding.
|
|
22
|
+
* @see https://developers.cloudflare.com/email-service/api/send-emails/workers-api/
|
|
23
|
+
*/
|
|
24
|
+
export type EmailSender = EmailSenderProps & {
|
|
25
|
+
type: "send_email";
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Creates a Cloudflare Email Service binding for sending emails from Workers.
|
|
30
|
+
*
|
|
31
|
+
* Set `dev.remote` to `true` to use the real Cloudflare email binding while
|
|
32
|
+
* running the Worker locally.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const email = EmailSender({
|
|
37
|
+
* allowedSenderAddresses: ["noreply@example.com"],
|
|
38
|
+
* dev: { remote: true },
|
|
39
|
+
* });
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @see https://developers.cloudflare.com/email-service/api/send-emails/workers-api/
|
|
43
|
+
*/
|
|
44
|
+
export function EmailSender(props: EmailSenderProps = {}): EmailSender {
|
|
45
|
+
return {
|
|
46
|
+
type: "send_email",
|
|
47
|
+
...props,
|
|
48
|
+
};
|
|
49
|
+
}
|
package/src/cloudflare/index.ts
CHANGED
|
@@ -38,6 +38,7 @@ export * from "./email-address.ts";
|
|
|
38
38
|
export * from "./email-catch-all.ts";
|
|
39
39
|
export * from "./email-routing.ts";
|
|
40
40
|
export * from "./email-rule.ts";
|
|
41
|
+
export * from "./email-sender.ts";
|
|
41
42
|
export * from "./health-check.ts";
|
|
42
43
|
export * from "./hyperdrive-ref.ts";
|
|
43
44
|
export * from "./hyperdrive.ts";
|
|
@@ -50,7 +50,10 @@ type RemoteBinding =
|
|
|
50
50
|
}
|
|
51
51
|
> & { raw: true })
|
|
52
52
|
// Fetcher type bindings do not require the `raw` flag and will throw an error if it is present.
|
|
53
|
-
| Extract<
|
|
53
|
+
| Extract<
|
|
54
|
+
WorkerBindingSpec,
|
|
55
|
+
{ type: "send_email" | "service" | "vpc_service" }
|
|
56
|
+
>;
|
|
54
57
|
|
|
55
58
|
type BaseWorkerOptions = {
|
|
56
59
|
[K in keyof miniflare.WorkerOptions]: K extends
|
|
@@ -261,6 +264,36 @@ export const buildWorkerOptions = async (
|
|
|
261
264
|
(options.bindings ??= {})[key] = binding.unencrypted;
|
|
262
265
|
break;
|
|
263
266
|
}
|
|
267
|
+
case "send_email": {
|
|
268
|
+
const config = {
|
|
269
|
+
name: key,
|
|
270
|
+
destination_address: binding.destinationAddress,
|
|
271
|
+
allowed_destination_addresses: binding.allowedDestinationAddresses,
|
|
272
|
+
allowed_sender_addresses: binding.allowedSenderAddresses,
|
|
273
|
+
};
|
|
274
|
+
if (isRemoteBinding(binding)) {
|
|
275
|
+
remoteBindings.push({
|
|
276
|
+
type: "send_email",
|
|
277
|
+
...config,
|
|
278
|
+
});
|
|
279
|
+
} else {
|
|
280
|
+
((
|
|
281
|
+
options as BaseWorkerOptions & {
|
|
282
|
+
email?: {
|
|
283
|
+
send_email?: Array<typeof config>;
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
).email ??= {}).send_email ??= [];
|
|
287
|
+
(
|
|
288
|
+
options as BaseWorkerOptions & {
|
|
289
|
+
email: {
|
|
290
|
+
send_email: Array<typeof config>;
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
).email.send_email.push(config);
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
264
297
|
case "secret_key": {
|
|
265
298
|
throw new Error("Secret keys are not supported in local mode");
|
|
266
299
|
}
|
|
@@ -441,6 +474,41 @@ export const buildWorkerOptions = async (
|
|
|
441
474
|
remoteProxyConnectionString: remoteProxy.connectionString,
|
|
442
475
|
};
|
|
443
476
|
break;
|
|
477
|
+
case "send_email":
|
|
478
|
+
((
|
|
479
|
+
options as BaseWorkerOptions & {
|
|
480
|
+
email?: {
|
|
481
|
+
send_email?: Array<{
|
|
482
|
+
name: string;
|
|
483
|
+
destination_address?: string;
|
|
484
|
+
allowed_destination_addresses?: string[];
|
|
485
|
+
allowed_sender_addresses?: string[];
|
|
486
|
+
remoteProxyConnectionString: typeof remoteProxy.connectionString;
|
|
487
|
+
}>;
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
).email ??= {}).send_email ??= [];
|
|
491
|
+
(
|
|
492
|
+
options as BaseWorkerOptions & {
|
|
493
|
+
email: {
|
|
494
|
+
send_email: Array<{
|
|
495
|
+
name: string;
|
|
496
|
+
destination_address?: string;
|
|
497
|
+
allowed_destination_addresses?: string[];
|
|
498
|
+
allowed_sender_addresses?: string[];
|
|
499
|
+
remoteProxyConnectionString: typeof remoteProxy.connectionString;
|
|
500
|
+
}>;
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
).email.send_email.push({
|
|
504
|
+
name: binding.name,
|
|
505
|
+
destination_address: binding.destination_address,
|
|
506
|
+
allowed_destination_addresses:
|
|
507
|
+
binding.allowed_destination_addresses,
|
|
508
|
+
allowed_sender_addresses: binding.allowed_sender_addresses,
|
|
509
|
+
remoteProxyConnectionString: remoteProxy.connectionString,
|
|
510
|
+
});
|
|
511
|
+
break;
|
|
444
512
|
case "service":
|
|
445
513
|
(options.serviceBindings ??= {})[binding.name] = {
|
|
446
514
|
name: binding.name,
|
|
@@ -101,7 +101,7 @@ export class MiniflareController {
|
|
|
101
101
|
unsafeDevRegistryPath: miniflare.getDefaultDevRegistryPath(),
|
|
102
102
|
log: process.env.DEBUG
|
|
103
103
|
? new miniflare.Log(miniflare.LogLevel.DEBUG)
|
|
104
|
-
:
|
|
104
|
+
: new DefaultLogger(),
|
|
105
105
|
// This is required to allow websites and other separate processes
|
|
106
106
|
// to detect Alchemy-managed Durable Objects via the Wrangler dev registry.
|
|
107
107
|
unsafeDevRegistryDurableObjectProxy: true,
|
|
@@ -170,3 +170,20 @@ export class MiniflareController {
|
|
|
170
170
|
]);
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
+
|
|
174
|
+
class DefaultLogger extends miniflare.Log {
|
|
175
|
+
constructor() {
|
|
176
|
+
// Miniflare emits send_email activity at info level.
|
|
177
|
+
super(miniflare.LogLevel.INFO);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
override info(message: string) {
|
|
181
|
+
if (
|
|
182
|
+
message.startsWith("Ready on") ||
|
|
183
|
+
message.startsWith("Updated and ready on")
|
|
184
|
+
) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
super.info(message);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -492,6 +492,14 @@ export async function prepareWorkerMetadata(
|
|
|
492
492
|
jurisdiction:
|
|
493
493
|
binding.jurisdiction === "default" ? undefined : binding.jurisdiction,
|
|
494
494
|
});
|
|
495
|
+
} else if (binding.type === "send_email") {
|
|
496
|
+
meta.bindings.push({
|
|
497
|
+
type: "send_email",
|
|
498
|
+
name: bindingName,
|
|
499
|
+
destination_address: binding.destinationAddress,
|
|
500
|
+
allowed_destination_addresses: binding.allowedDestinationAddresses,
|
|
501
|
+
allowed_sender_addresses: binding.allowedSenderAddresses,
|
|
502
|
+
});
|
|
495
503
|
} else if (binding.type === "secrets_store_secret") {
|
|
496
504
|
meta.bindings.push({
|
|
497
505
|
type: "secrets_store_secret",
|
|
@@ -263,6 +263,7 @@ async function processBindings(
|
|
|
263
263
|
const durableObjects: WranglerJsonConfig["durable_objects"]["bindings"] = [];
|
|
264
264
|
const r2Buckets: WranglerJsonConfig["r2_buckets"] = [];
|
|
265
265
|
const services: WranglerJsonConfig["services"] = [];
|
|
266
|
+
const sendEmailBindings: WranglerJsonConfig["send_email"] = [];
|
|
266
267
|
const secrets: string[] = [];
|
|
267
268
|
const workflows: WranglerJsonConfig["workflows"] = [];
|
|
268
269
|
const d1Databases: WranglerJsonConfig["d1_databases"] = [];
|
|
@@ -389,6 +390,14 @@ async function processBindings(
|
|
|
389
390
|
binding.jurisdiction === "default" ? undefined : binding.jurisdiction,
|
|
390
391
|
...(binding.dev?.remote ? { remote: true } : {}),
|
|
391
392
|
});
|
|
393
|
+
} else if (binding.type === "send_email") {
|
|
394
|
+
sendEmailBindings.push({
|
|
395
|
+
name: bindingName,
|
|
396
|
+
destination_address: binding.destinationAddress,
|
|
397
|
+
allowed_destination_addresses: binding.allowedDestinationAddresses,
|
|
398
|
+
allowed_sender_addresses: binding.allowedSenderAddresses,
|
|
399
|
+
...(binding.dev?.remote ? { remote: true } : {}),
|
|
400
|
+
});
|
|
392
401
|
} else if (binding.type === "secret") {
|
|
393
402
|
// Secret binding
|
|
394
403
|
secrets.push(bindingName);
|
|
@@ -569,6 +578,14 @@ async function processBindings(
|
|
|
569
578
|
spec.services = services;
|
|
570
579
|
}
|
|
571
580
|
|
|
581
|
+
if (sendEmailBindings.length > 0) {
|
|
582
|
+
(
|
|
583
|
+
spec as WranglerJsonSpec & {
|
|
584
|
+
send_email?: typeof sendEmailBindings;
|
|
585
|
+
}
|
|
586
|
+
).send_email = sendEmailBindings;
|
|
587
|
+
}
|
|
588
|
+
|
|
572
589
|
if (d1Databases.length > 0) {
|
|
573
590
|
spec.d1_databases = d1Databases;
|
|
574
591
|
}
|