@vibesdotdev/integrations-resend 0.0.0-stage → 1.0.0-rc.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/LICENSE +126 -0
- package/README.md +21 -2
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/runtime-client.d.ts +12 -0
- package/dist/client/runtime-client.d.ts.map +1 -0
- package/dist/client/runtime-client.js +61 -0
- package/dist/client/runtime-client.js.map +1 -0
- package/dist/config/resend.config.descriptor.d.ts +46 -0
- package/dist/config/resend.config.descriptor.d.ts.map +1 -0
- package/dist/config/resend.config.descriptor.js +50 -0
- package/dist/config/resend.config.descriptor.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/resend/index.d.ts +10 -0
- package/dist/resend/index.d.ts.map +1 -0
- package/dist/resend/index.js +10 -0
- package/dist/resend/index.js.map +1 -0
- package/dist/resend/resend-events.descriptors.d.ts +62 -0
- package/dist/resend/resend-events.descriptors.d.ts.map +1 -0
- package/dist/resend/resend-events.descriptors.js +55 -0
- package/dist/resend/resend-events.descriptors.js.map +1 -0
- package/dist/resend/resend-events.impls.d.ts +35 -0
- package/dist/resend/resend-events.impls.d.ts.map +1 -0
- package/dist/resend/resend-events.impls.js +115 -0
- package/dist/resend/resend-events.impls.js.map +1 -0
- package/dist/resend/resend-message-source.descriptor.d.ts +30 -0
- package/dist/resend/resend-message-source.descriptor.d.ts.map +1 -0
- package/dist/resend/resend-message-source.descriptor.js +24 -0
- package/dist/resend/resend-message-source.descriptor.js.map +1 -0
- package/dist/resend/resend-normalize.d.ts +24 -0
- package/dist/resend/resend-normalize.d.ts.map +1 -0
- package/dist/resend/resend-normalize.js +22 -0
- package/dist/resend/resend-normalize.js.map +1 -0
- package/dist/resend/resend-received.d.ts +25 -0
- package/dist/resend/resend-received.d.ts.map +1 -0
- package/dist/resend/resend-received.js +26 -0
- package/dist/resend/resend-received.js.map +1 -0
- package/dist/resend/resend-response.d.ts +3 -0
- package/dist/resend/resend-response.d.ts.map +1 -0
- package/dist/resend/resend-response.js +14 -0
- package/dist/resend/resend-response.js.map +1 -0
- package/dist/resend/resend-signature-verifier.descriptor.d.ts +20 -0
- package/dist/resend/resend-signature-verifier.descriptor.d.ts.map +1 -0
- package/dist/resend/resend-signature-verifier.descriptor.js +20 -0
- package/dist/resend/resend-signature-verifier.descriptor.js.map +1 -0
- package/dist/resend/resend-signature-verifier.impl.d.ts +16 -0
- package/dist/resend/resend-signature-verifier.impl.d.ts.map +1 -0
- package/dist/resend/resend-signature-verifier.impl.js +19 -0
- package/dist/resend/resend-signature-verifier.impl.js.map +1 -0
- package/dist/resend/resend-signature.d.ts +32 -0
- package/dist/resend/resend-signature.d.ts.map +1 -0
- package/dist/resend/resend-signature.js +77 -0
- package/dist/resend/resend-signature.js.map +1 -0
- package/dist/resend/resend-webhook.descriptor.d.ts +39 -0
- package/dist/resend/resend-webhook.descriptor.d.ts.map +1 -0
- package/dist/resend/resend-webhook.descriptor.js +24 -0
- package/dist/resend/resend-webhook.descriptor.js.map +1 -0
- package/dist/resend/resend-webhook.impl.d.ts +19 -0
- package/dist/resend/resend-webhook.impl.d.ts.map +1 -0
- package/dist/resend/resend-webhook.impl.js +111 -0
- package/dist/resend/resend-webhook.impl.js.map +1 -0
- package/dist/resend/resend.descriptor.d.ts +37 -0
- package/dist/resend/resend.descriptor.d.ts.map +1 -0
- package/dist/resend/resend.descriptor.js +107 -0
- package/dist/resend/resend.descriptor.js.map +1 -0
- package/dist/resend/resend.impl.d.ts +24 -0
- package/dist/resend/resend.impl.d.ts.map +1 -0
- package/dist/resend/resend.impl.js +258 -0
- package/dist/resend/resend.impl.js.map +1 -0
- package/dist/resend.plugin.d.ts +19 -0
- package/dist/resend.plugin.d.ts.map +1 -0
- package/dist/resend.plugin.js +88 -0
- package/dist/resend.plugin.js.map +1 -0
- package/dist/schemas/resend.d.ts +421 -0
- package/dist/schemas/resend.d.ts.map +1 -0
- package/dist/schemas/resend.js +97 -0
- package/dist/schemas/resend.js.map +1 -0
- package/package.json +107 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend-webhook.descriptor.js","sourceRoot":"","sources":["../../src/resend/resend-webhook.descriptor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAGjE,eAAe,kBAAkB,CAAgC;IAChE,IAAI,EAAE,uBAAuB;IAC7B,EAAE,EAAE,0BAA0B;IAC9B,IAAI,EAAE,wBAAwB;IAC9B,WAAW,EACV,kFAAkF;IACnF,QAAQ,EAAE,QAAQ;IAClB,eAAe,EAAE,gBAAgB;IACjC,OAAO,EAAE,IAAI;IACb,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;CAC5C,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resend webhook handler implementation.
|
|
3
|
+
*
|
|
4
|
+
* Svix-style HMAC-SHA256 verification — no `svix` SDK dependency. The
|
|
5
|
+
* algorithm lives once in `./resend-signature.ts` (Web Crypto only, so the
|
|
6
|
+
* pack is universal scope); this handler adds the per-delivery secret lookup,
|
|
7
|
+
* the distinct failure messages, and the event normalization.
|
|
8
|
+
*/
|
|
9
|
+
import type { RuntimeRef } from '@vibesdotdev/kernel';
|
|
10
|
+
import type { EmailWebhookHandlerDescriptor, EmailWebhookHandlerImplementation } from '@vibesdotdev/email/schemas/email-webhook';
|
|
11
|
+
/**
|
|
12
|
+
* Factory: returns a verifier impl bound to the descriptor + the integration
|
|
13
|
+
* id whose `webhookSecret` credential we'll use to verify deliveries. The
|
|
14
|
+
* plugin wires this for the canonical Resend integration; multi-tenant apps
|
|
15
|
+
* with multiple Resend accounts register additional descriptors and call
|
|
16
|
+
* this factory for each.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createResendWebhookHandler(runtime: Pick<RuntimeRef, 'query'>, descriptor: EmailWebhookHandlerDescriptor, integrationId: string): EmailWebhookHandlerImplementation;
|
|
19
|
+
//# sourceMappingURL=resend-webhook.impl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend-webhook.impl.d.ts","sourceRoot":"","sources":["../../src/resend/resend-webhook.impl.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EACX,6BAA6B,EAC7B,iCAAiC,EAEjC,MAAM,0CAA0C,CAAC;AAqDlD;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACzC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EAClC,UAAU,EAAE,6BAA6B,EACzC,aAAa,EAAE,MAAM,GACnB,iCAAiC,CAsDnC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resend webhook handler implementation.
|
|
3
|
+
*
|
|
4
|
+
* Svix-style HMAC-SHA256 verification — no `svix` SDK dependency. The
|
|
5
|
+
* algorithm lives once in `./resend-signature.ts` (Web Crypto only, so the
|
|
6
|
+
* pack is universal scope); this handler adds the per-delivery secret lookup,
|
|
7
|
+
* the distinct failure messages, and the event normalization.
|
|
8
|
+
*/
|
|
9
|
+
import { CapabilityError, CaughtFailureSchema, valueFreeFailure } from '@vibesdotdev/kernel';
|
|
10
|
+
import { webhookPayloadSchema } from "../schemas/resend.js";
|
|
11
|
+
import { SVIX_REPLAY_WINDOW_SEC, computeSvixSignature, svixSignatureMatches } from "./resend-signature.js";
|
|
12
|
+
/** Maximum allowed clock-skew between the webhook timestamp and now. */
|
|
13
|
+
const REPLAY_WINDOW_MS = SVIX_REPLAY_WINDOW_SEC * 1000;
|
|
14
|
+
/**
|
|
15
|
+
* Map Resend's webhook event types to the substrate's canonical status enum.
|
|
16
|
+
* Keep in sync with `mapResendStatus` in `resend.impl.ts`.
|
|
17
|
+
*/
|
|
18
|
+
function mapEventStatus(type) {
|
|
19
|
+
switch (type) {
|
|
20
|
+
case 'email.sent':
|
|
21
|
+
return 'sent';
|
|
22
|
+
case 'email.delivered':
|
|
23
|
+
return 'delivered';
|
|
24
|
+
case 'email.bounced':
|
|
25
|
+
return 'bounced';
|
|
26
|
+
case 'email.complained':
|
|
27
|
+
return 'complained';
|
|
28
|
+
case 'email.opened':
|
|
29
|
+
return 'opened';
|
|
30
|
+
case 'email.clicked':
|
|
31
|
+
return 'clicked';
|
|
32
|
+
case 'email.delivery_delayed':
|
|
33
|
+
return 'queued';
|
|
34
|
+
case 'email.failed':
|
|
35
|
+
return 'failed';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the active Resend integration so we can read its `webhookSecret`
|
|
40
|
+
* credential. Webhooks arrive at the platform without per-call auth, so the
|
|
41
|
+
* secret has to come from the registered integration the descriptor refers
|
|
42
|
+
* to. The resolved impl satisfies `IntegrationProviderImplementation` —
|
|
43
|
+
* `getCredentials()` is the canonical path; no provider-specific casts.
|
|
44
|
+
*/
|
|
45
|
+
async function resolveWebhookSecret(runtime, integrationId) {
|
|
46
|
+
const impl = await runtime
|
|
47
|
+
.query('integration/provider')
|
|
48
|
+
.withId(integrationId)
|
|
49
|
+
.resolve();
|
|
50
|
+
const credentials = await impl.getCredentials();
|
|
51
|
+
const secret = credentials.webhookSecret;
|
|
52
|
+
return secret && secret.length > 0 ? secret : null;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Factory: returns a verifier impl bound to the descriptor + the integration
|
|
56
|
+
* id whose `webhookSecret` credential we'll use to verify deliveries. The
|
|
57
|
+
* plugin wires this for the canonical Resend integration; multi-tenant apps
|
|
58
|
+
* with multiple Resend accounts register additional descriptors and call
|
|
59
|
+
* this factory for each.
|
|
60
|
+
*/
|
|
61
|
+
export function createResendWebhookHandler(runtime, descriptor, integrationId) {
|
|
62
|
+
return {
|
|
63
|
+
descriptor,
|
|
64
|
+
async verify({ headers, rawBody }) {
|
|
65
|
+
const svixId = headers['svix-id'];
|
|
66
|
+
const svixTimestamp = headers['svix-timestamp'];
|
|
67
|
+
const svixSignature = headers['svix-signature'];
|
|
68
|
+
if (!svixId || !svixTimestamp || !svixSignature) {
|
|
69
|
+
throw new CapabilityError('Resend webhook missing required svix-id / svix-timestamp / svix-signature header.', 'INVALID_INPUT', { reason: 'resend-webhook-invalid' });
|
|
70
|
+
}
|
|
71
|
+
const timestampSec = Number(svixTimestamp);
|
|
72
|
+
if (!Number.isFinite(timestampSec)) {
|
|
73
|
+
throw new CapabilityError('Resend webhook svix-timestamp is not a number.', 'INVALID_INPUT', { reason: 'resend-webhook-invalid' });
|
|
74
|
+
}
|
|
75
|
+
const ageMs = Math.abs(Date.now() - timestampSec * 1000);
|
|
76
|
+
if (ageMs > REPLAY_WINDOW_MS) {
|
|
77
|
+
throw new CapabilityError(`Resend webhook timestamp outside ${REPLAY_WINDOW_MS / 1000}s replay window (age ${Math.round(ageMs / 1000)}s).`, 'PERMISSION_DENIED', { reason: 'resend-signature-invalid' });
|
|
78
|
+
}
|
|
79
|
+
const secret = await resolveWebhookSecret(runtime, integrationId);
|
|
80
|
+
if (!secret) {
|
|
81
|
+
throw new CapabilityError(`Resend webhook secret not configured on integration '${integrationId}'.`, 'AUTH_REQUIRED', { reason: 'resend-webhook-secret-missing' });
|
|
82
|
+
}
|
|
83
|
+
const expected = await computeSvixSignature(secret, svixId, svixTimestamp, rawBody);
|
|
84
|
+
const matched = svixSignatureMatches(svixSignature, expected);
|
|
85
|
+
if (!matched)
|
|
86
|
+
throw new CapabilityError('Resend webhook signature verification failed.', 'PERMISSION_DENIED', { reason: 'resend-signature-invalid' });
|
|
87
|
+
let body;
|
|
88
|
+
try {
|
|
89
|
+
body = JSON.parse(rawBody);
|
|
90
|
+
}
|
|
91
|
+
catch (cause) {
|
|
92
|
+
throw new CapabilityError('Resend webhook body is not valid JSON.', 'INVALID_INPUT', { reason: 'resend-webhook-invalid', details: { failure: valueFreeFailure(CaughtFailureSchema.parse(cause)) } });
|
|
93
|
+
}
|
|
94
|
+
const admitted = webhookPayloadSchema.safeParse(body);
|
|
95
|
+
if (!admitted.success)
|
|
96
|
+
throw new CapabilityError('Resend webhook payload missing `type` or `data.email_id`, or has an invalid event shape.', 'INVALID_INPUT', { reason: 'resend-webhook-invalid' });
|
|
97
|
+
const payload = admitted.data;
|
|
98
|
+
const event = {
|
|
99
|
+
provider: 'resend',
|
|
100
|
+
messageId: payload.data.email_id,
|
|
101
|
+
status: mapEventStatus(payload.type),
|
|
102
|
+
to: payload.data.to?.[0],
|
|
103
|
+
reason: payload.data.bounce?.message ?? payload.data.failed?.reason,
|
|
104
|
+
timestamp: payload.created_at,
|
|
105
|
+
raw: payload
|
|
106
|
+
};
|
|
107
|
+
return [event];
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=resend-webhook.impl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend-webhook.impl.js","sourceRoot":"","sources":["../../src/resend/resend-webhook.impl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resend `integration/provider` descriptor.
|
|
3
|
+
*
|
|
4
|
+
* Vendor pack for the Resend HTTP email API. Registers under
|
|
5
|
+
* `category: 'email'` so `packages/email`'s `EmailClient` picks it up at
|
|
6
|
+
* dispatch time alongside any other email integrations the app registers.
|
|
7
|
+
*
|
|
8
|
+
* Credentials:
|
|
9
|
+
* - `apiKey` Resend API key, scoped per org so multi-tenant apps can
|
|
10
|
+
* route different orgs through different Resend accounts.
|
|
11
|
+
* - `webhookSecret` Svix signing secret (Resend uses Svix for webhooks).
|
|
12
|
+
* Required for the `email/webhook-handler` to verify
|
|
13
|
+
* inbound delivery events.
|
|
14
|
+
*
|
|
15
|
+
* Config:
|
|
16
|
+
* - `defaultFrom` Optional fallback `from` address when the caller
|
|
17
|
+
* doesn't supply one on the `EmailSendInput`.
|
|
18
|
+
*
|
|
19
|
+
* Operations are the substrate canonical `send`/`check`/`receive`. Resend
|
|
20
|
+
* is webhook-push for inbound; `receive` is not supported and surfaces as
|
|
21
|
+
* `EmailReceiveNotSupportedError`.
|
|
22
|
+
*/
|
|
23
|
+
import { type IntegrationProviderDescriptorInput } from '@vibesdotdev/integrations/schemas/integration-descriptor';
|
|
24
|
+
import { type ResendHostOptions } from '../schemas/resend.ts';
|
|
25
|
+
export type { ResendHostOptions } from '../schemas/resend.ts';
|
|
26
|
+
type ResendDescriptor = IntegrationProviderDescriptorInput & Required<Pick<IntegrationProviderDescriptorInput, 'name' | 'provider' | 'config'>>;
|
|
27
|
+
declare const resendDescriptor: ResendDescriptor;
|
|
28
|
+
/**
|
|
29
|
+
* The Resend descriptor with the host's declared default sender. A descriptor
|
|
30
|
+
* that carries a host credential reference takes its config from the
|
|
31
|
+
* descriptor alone (never from tenant settings), so the host declares
|
|
32
|
+
* `defaultFrom` here, the same way `createHostSmtpDescriptor` declares SMTP's.
|
|
33
|
+
* Callers then send without a `from`: the provider fills it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createHostResendDescriptor(options: ResendHostOptions): ResendDescriptor;
|
|
36
|
+
export default resendDescriptor;
|
|
37
|
+
//# sourceMappingURL=resend.descriptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.descriptor.d.ts","sourceRoot":"","sources":["../../src/resend/resend.descriptor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,EACN,KAAK,kCAAkC,EACvC,MAAM,0DAA0D,CAAC;AAElE,OAAO,EAA2B,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACvF,YAAY,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,KAAK,gBAAgB,GAAG,kCAAkC,GACzD,QAAQ,CAAC,IAAI,CAAC,kCAAkC,EAAE,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAC,CAAC,CAAC;AAEpF,QAAA,MAAM,gBAAgB,kBAmEpB,CAAC;AAEH;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAMvF;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resend `integration/provider` descriptor.
|
|
3
|
+
*
|
|
4
|
+
* Vendor pack for the Resend HTTP email API. Registers under
|
|
5
|
+
* `category: 'email'` so `packages/email`'s `EmailClient` picks it up at
|
|
6
|
+
* dispatch time alongside any other email integrations the app registers.
|
|
7
|
+
*
|
|
8
|
+
* Credentials:
|
|
9
|
+
* - `apiKey` Resend API key, scoped per org so multi-tenant apps can
|
|
10
|
+
* route different orgs through different Resend accounts.
|
|
11
|
+
* - `webhookSecret` Svix signing secret (Resend uses Svix for webhooks).
|
|
12
|
+
* Required for the `email/webhook-handler` to verify
|
|
13
|
+
* inbound delivery events.
|
|
14
|
+
*
|
|
15
|
+
* Config:
|
|
16
|
+
* - `defaultFrom` Optional fallback `from` address when the caller
|
|
17
|
+
* doesn't supply one on the `EmailSendInput`.
|
|
18
|
+
*
|
|
19
|
+
* Operations are the substrate canonical `send`/`check`/`receive`. Resend
|
|
20
|
+
* is webhook-push for inbound; `receive` is not supported and surfaces as
|
|
21
|
+
* `EmailReceiveNotSupportedError`.
|
|
22
|
+
*/
|
|
23
|
+
import { createRuntimeAsset } from '@vibesdotdev/kernel/factory';
|
|
24
|
+
import {} from '@vibesdotdev/integrations/schemas/integration-descriptor';
|
|
25
|
+
import { emailCapabilityEgressSchema } from '@vibesdotdev/email/schemas/email-egress';
|
|
26
|
+
import { resendHostOptionsSchema } from "../schemas/resend.js";
|
|
27
|
+
const resendDescriptor = createRuntimeAsset({
|
|
28
|
+
kind: 'integration/provider',
|
|
29
|
+
id: 'integration.email.resend',
|
|
30
|
+
name: 'Resend',
|
|
31
|
+
description: 'Resend HTTP email API. Outbound send + status lookup; inbound delivery events via Svix-signed webhooks.',
|
|
32
|
+
provider: 'resend',
|
|
33
|
+
category: 'email',
|
|
34
|
+
enabled: true,
|
|
35
|
+
priority: 100,
|
|
36
|
+
baseUrl: 'https://api.resend.com',
|
|
37
|
+
configSettingKey: 'integrations.email.resend.config',
|
|
38
|
+
config: {
|
|
39
|
+
defaultFrom: '',
|
|
40
|
+
emailEgress: emailCapabilityEgressSchema.parse({ kind: 'fixed-https', host: 'api.resend.com', baseUrlKey: 'baseUrl' })
|
|
41
|
+
},
|
|
42
|
+
credentials: [
|
|
43
|
+
{
|
|
44
|
+
key: 'apiKey',
|
|
45
|
+
source: 'secret',
|
|
46
|
+
providerType: 'resend',
|
|
47
|
+
scope: 'org',
|
|
48
|
+
required: false
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: 'apiKey',
|
|
52
|
+
source: 'secret',
|
|
53
|
+
reference: { source: 'secret', key: 'RESEND_API_KEY' },
|
|
54
|
+
required: false
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: 'webhookSecret',
|
|
58
|
+
source: 'secret',
|
|
59
|
+
providerType: 'resend',
|
|
60
|
+
scope: 'org',
|
|
61
|
+
required: false
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
operations: [
|
|
65
|
+
{
|
|
66
|
+
id: 'get-received-attachment',
|
|
67
|
+
name: 'Download received attachment',
|
|
68
|
+
description: 'Retrieve received attachment bytes through the selected account and a fresh provider download URL.',
|
|
69
|
+
requiresOrg: false
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'send',
|
|
73
|
+
name: 'Send email',
|
|
74
|
+
description: 'POST /emails — deliver a message via the Resend HTTP API.',
|
|
75
|
+
requiresOrg: false
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'check',
|
|
79
|
+
name: 'Check status / health',
|
|
80
|
+
description: 'GET /emails/{id} for status lookup, or GET /domains as a health probe.',
|
|
81
|
+
requiresOrg: false
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'get-received',
|
|
85
|
+
name: 'Retrieve received email',
|
|
86
|
+
description: 'GET /emails/receiving/{emailId} — retrieve the complete body and headers with the selected account.',
|
|
87
|
+
requiresOrg: false
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
tags: ['email', 'resend', 'http'],
|
|
91
|
+
});
|
|
92
|
+
/**
|
|
93
|
+
* The Resend descriptor with the host's declared default sender. A descriptor
|
|
94
|
+
* that carries a host credential reference takes its config from the
|
|
95
|
+
* descriptor alone (never from tenant settings), so the host declares
|
|
96
|
+
* `defaultFrom` here, the same way `createHostSmtpDescriptor` declares SMTP's.
|
|
97
|
+
* Callers then send without a `from`: the provider fills it.
|
|
98
|
+
*/
|
|
99
|
+
export function createHostResendDescriptor(options) {
|
|
100
|
+
const { defaultFrom } = resendHostOptionsSchema.parse(options);
|
|
101
|
+
return createRuntimeAsset({
|
|
102
|
+
...resendDescriptor,
|
|
103
|
+
config: { ...resendDescriptor.config, defaultFrom }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
export default resendDescriptor;
|
|
107
|
+
//# sourceMappingURL=resend.descriptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.descriptor.js","sourceRoot":"","sources":["../../src/resend/resend.descriptor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAEN,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAE,2BAA2B,EAAE,MAAM,yCAAyC,CAAC;AACtF,OAAO,EAAE,uBAAuB,EAA0B,MAAM,sBAAsB,CAAC;AAMvF,MAAM,gBAAgB,GAAG,kBAAkB,CAAmB;IAC7D,IAAI,EAAE,sBAAsB;IAC5B,EAAE,EAAE,0BAA0B;IAC9B,IAAI,EAAE,QAAQ;IACd,WAAW,EACV,yGAAyG;IAC1G,QAAQ,EAAE,QAAQ;IAClB,QAAQ,EAAE,OAAO;IACjB,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,GAAG;IACb,OAAO,EAAE,wBAAwB;IACjC,gBAAgB,EAAE,kCAAkC;IACpD,MAAM,EAAE;QACP,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,2BAA2B,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;KACtH;IACD,WAAW,EAAE;QACZ;YACC,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE,QAAQ;YAChB,YAAY,EAAE,QAAQ;YACtB,KAAK,EAAE,KAAK;YACZ,QAAQ,EAAE,KAAK;SACf;QACD;YACC,GAAG,EAAE,QAAQ;YACb,MAAM,EAAE,QAAQ;YAChB,SAAS,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,gBAAgB,EAAE;YACtD,QAAQ,EAAE,KAAK;SACf;QACD;YACC,GAAG,EAAE,eAAe;YACpB,MAAM,EAAE,QAAQ;YAChB,YAAY,EAAE,QAAQ;YACtB,KAAK,EAAE,KAAK;YACZ,QAAQ,EAAE,KAAK;SACf;KACD;IACD,UAAU,EAAE;QACX;YACC,EAAE,EAAE,yBAAyB;YAC7B,IAAI,EAAE,8BAA8B;YACpC,WAAW,EAAE,oGAAoG;YACjH,WAAW,EAAE,KAAK;SAClB;QACD;YACC,EAAE,EAAE,MAAM;YACV,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,2DAA2D;YACxE,WAAW,EAAE,KAAK;SAClB;QACD;YACC,EAAE,EAAE,OAAO;YACX,IAAI,EAAE,uBAAuB;YAC7B,WAAW,EACV,wEAAwE;YACzE,WAAW,EAAE,KAAK;SAClB;QACD;YACC,EAAE,EAAE,cAAc;YAClB,IAAI,EAAE,yBAAyB;YAC/B,WAAW,EACV,qGAAqG;YACtG,WAAW,EAAE,KAAK;SAClB;KACD;IACD,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC;CACjC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,OAA0B;IACpE,MAAM,EAAE,WAAW,EAAE,GAAG,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/D,OAAO,kBAAkB,CAAmB;QAC3C,GAAG,gBAAgB;QACnB,MAAM,EAAE,EAAE,GAAG,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE;KACnD,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { EmailIntegrationImplementation } from '@vibesdotdev/email/services/email-integration';
|
|
2
|
+
import { BaseIntegrationProvider } from '@vibesdotdev/integrations/kinds/integration';
|
|
3
|
+
import type { IntegrationConnectionSelector, IntegrationOperationResult } from '@vibesdotdev/integrations/schemas/integration-descriptor';
|
|
4
|
+
import type { EmailSendInput, EmailSendResult, EmailStatusResult, EmailHealthResult } from '@vibesdotdev/email/schemas/email-data';
|
|
5
|
+
export declare class ResendEmailIntegration extends BaseIntegrationProvider implements EmailIntegrationImplementation {
|
|
6
|
+
invokeOperation<T = unknown>(opId: string, args?: import('../schemas/resend.ts').ResendOperationArgs): Promise<IntegrationOperationResult<T>>;
|
|
7
|
+
invokeOperationForConnection<T = unknown>(selector: Readonly<IntegrationConnectionSelector>, opId: string, args?: import('../schemas/resend.ts').ResendOperationArgs): Promise<IntegrationOperationResult<T>>;
|
|
8
|
+
/**
|
|
9
|
+
* Bearer-token POST to Resend's send endpoint. Network egress is the
|
|
10
|
+
* canonical work; everything else is shape translation + error mapping.
|
|
11
|
+
* Throws on non-2xx so the typed `send` contract returns either a valid
|
|
12
|
+
* `EmailSendResult` or a thrown error (which `EmailClient.send` and
|
|
13
|
+
* `invokeOperation` translate into the right surface for each caller).
|
|
14
|
+
*/
|
|
15
|
+
send(input: EmailSendInput, selector?: Readonly<IntegrationConnectionSelector>): Promise<EmailSendResult>;
|
|
16
|
+
/**
|
|
17
|
+
* `check(messageId)` performs a status lookup; `check()` performs a
|
|
18
|
+
* health probe by verifying the API key works against Resend's
|
|
19
|
+
* `GET /domains` endpoint.
|
|
20
|
+
*/
|
|
21
|
+
check(messageId?: string, selector?: Readonly<IntegrationConnectionSelector>): Promise<EmailStatusResult | EmailHealthResult>;
|
|
22
|
+
}
|
|
23
|
+
export default ResendEmailIntegration;
|
|
24
|
+
//# sourceMappingURL=resend.impl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.impl.d.ts","sourceRoot":"","sources":["../../src/resend/resend.impl.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,+CAA+C,CAAC;AACpG,OAAO,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAC;AACtF,OAAO,KAAK,EAAE,6BAA6B,EAAE,0BAA0B,EAAE,MAAM,0DAA0D,CAAC;AAG1I,OAAO,KAAK,EACX,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EAEjB,MAAM,uCAAuC,CAAC;AAiF/C,qBAAa,sBACZ,SAAQ,uBACR,YAAW,8BAA8B;IAE1B,eAAe,CAAC,CAAC,GAAG,OAAO,EACzC,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,sBAAsB,EAAE,mBAAmB,GACvD,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC;IAInC,4BAA4B,CAAC,CAAC,GAAG,OAAO,EAC7C,QAAQ,EAAE,QAAQ,CAAC,6BAA6B,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,sBAAsB,EAAE,mBAAmB,GACxH,OAAO,CAAC,0BAA0B,CAAC,CAAC,CAAC,CAAC;IA+CzC;;;;;;OAMG;IACG,IAAI,CAAC,KAAK,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,6BAA6B,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC;IAgD/G;;;;OAIG;IACG,KAAK,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,6BAA6B,CAAC,GAAG,OAAO,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;CA6CnI;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { readResendResponse } from "./resend-response.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resend integration implementation.
|
|
4
|
+
*
|
|
5
|
+
* Extends the Integrations-owned `BaseIntegrationProvider`, so live config and
|
|
6
|
+
* credentials stay runtime-backed while `send`, `check`, and optional
|
|
7
|
+
* `receive` are wired through the substrate's `EmailClient` and canonical
|
|
8
|
+
* `email` tool. Generic operations delegate to the Email-owned dispatcher.
|
|
9
|
+
*
|
|
10
|
+
* Resend's inbound surface is webhook-push only — there is no IMAP / poll
|
|
11
|
+
* endpoint. The dispatcher reports `EmailReceiveNotSupportedError` when
|
|
12
|
+
* `receive()` is absent; `EmailClient.receive()` catches that and returns `[]`
|
|
13
|
+
* so the typed surface stays uniform across packs.
|
|
14
|
+
*/
|
|
15
|
+
import { CapabilityError, CaughtFailureSchema, valueFreeFailure } from '@vibesdotdev/kernel';
|
|
16
|
+
import { emailSendInputSchema } from '@vibesdotdev/email/schemas/email-data';
|
|
17
|
+
import { attachmentInputSchema, attachmentReceiptSchema, receivedInputSchema, resendConfigSchema, sendResponseSchema, statusResponseSchema } from "../schemas/resend.js";
|
|
18
|
+
import { invokeEmailIntegrationOperation } from '@vibesdotdev/email/services/email-integration';
|
|
19
|
+
import { BaseIntegrationProvider } from '@vibesdotdev/integrations/kinds/integration';
|
|
20
|
+
import { retrieveResendReceivedEmail } from "./resend-received.js";
|
|
21
|
+
import { resolveResendBaseUrl } from "../config/resend.config.descriptor.js";
|
|
22
|
+
/**
|
|
23
|
+
* Map Resend's `last_event` strings to the substrate's canonical
|
|
24
|
+
* `EmailDeliveryStatus` enum. `created` collapses to `queued` since Resend
|
|
25
|
+
* has accepted the message but has not yet handed it to a downstream MTA.
|
|
26
|
+
* `delivery_delayed` also collapses to `queued` because the message hasn't
|
|
27
|
+
* reached its terminal state yet.
|
|
28
|
+
*/
|
|
29
|
+
function mapResendStatus(event) {
|
|
30
|
+
switch (event) {
|
|
31
|
+
case 'created':
|
|
32
|
+
case 'delivery_delayed':
|
|
33
|
+
return 'queued';
|
|
34
|
+
case 'sent':
|
|
35
|
+
return 'sent';
|
|
36
|
+
case 'delivered':
|
|
37
|
+
return 'delivered';
|
|
38
|
+
case 'bounced':
|
|
39
|
+
return 'bounced';
|
|
40
|
+
case 'complained':
|
|
41
|
+
return 'complained';
|
|
42
|
+
case 'opened':
|
|
43
|
+
return 'opened';
|
|
44
|
+
case 'clicked':
|
|
45
|
+
return 'clicked';
|
|
46
|
+
case 'failed':
|
|
47
|
+
return 'failed';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Translate the substrate's canonical `EmailSendInput` into Resend's request
|
|
52
|
+
* body. Drops fields Resend doesn't accept (`idempotencyKey`) and renames
|
|
53
|
+
* the ones it spells differently (`replyTo` → `reply_to`, attachment
|
|
54
|
+
* `disposition` → omitted because Resend infers from `contentId`).
|
|
55
|
+
*/
|
|
56
|
+
function toResendBody(input, fallbackFrom) {
|
|
57
|
+
const from = input.from ?? fallbackFrom;
|
|
58
|
+
if (!from) {
|
|
59
|
+
throw new CapabilityError('Resend send requires a `from` address. Pass one on the EmailSendInput or set the `defaultFrom` config on the integration.', 'INVALID_INPUT', { reason: 'resend-sender-missing' });
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
from, to: Array.isArray(input.to) ? input.to : [input.to],
|
|
63
|
+
cc: input.cc, bcc: input.bcc, reply_to: input.replyTo,
|
|
64
|
+
subject: input.subject, html: input.html, text: input.text,
|
|
65
|
+
headers: input.inReplyTo ? { ...input.headers, 'In-Reply-To': input.inReplyTo } : input.headers,
|
|
66
|
+
attachments: input.attachments?.map(a => ({
|
|
67
|
+
filename: a.filename, content: a.content, content_type: a.contentType, content_id: a.contentId
|
|
68
|
+
})),
|
|
69
|
+
tags: input.tags?.map(name => ({ name, value: 'true' }))
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
async function readAttachment(response, maxBytes) {
|
|
73
|
+
if (!response.body)
|
|
74
|
+
throw new CapabilityError('Attachment body is missing', 'UNAVAILABLE', { reason: 'resend-attachment-invalid' });
|
|
75
|
+
const reader = response.body.getReader();
|
|
76
|
+
const chunks = [];
|
|
77
|
+
let total = 0;
|
|
78
|
+
try {
|
|
79
|
+
while (true) {
|
|
80
|
+
const { done, value } = await reader.read();
|
|
81
|
+
if (done)
|
|
82
|
+
break;
|
|
83
|
+
total += value.byteLength;
|
|
84
|
+
if (total > maxBytes) {
|
|
85
|
+
await reader.cancel();
|
|
86
|
+
throw new CapabilityError('Attachment exceeds its allowed size', 'UNAVAILABLE', { reason: 'resend-attachment-invalid' });
|
|
87
|
+
}
|
|
88
|
+
chunks.push(value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
reader.releaseLock();
|
|
93
|
+
}
|
|
94
|
+
const bytes = new Uint8Array(total);
|
|
95
|
+
let offset = 0;
|
|
96
|
+
for (const chunk of chunks) {
|
|
97
|
+
bytes.set(chunk, offset);
|
|
98
|
+
offset += chunk.byteLength;
|
|
99
|
+
}
|
|
100
|
+
return bytes;
|
|
101
|
+
}
|
|
102
|
+
export class ResendEmailIntegration extends BaseIntegrationProvider {
|
|
103
|
+
async invokeOperation(opId, args) {
|
|
104
|
+
return invokeEmailIntegrationOperation(this, opId, args);
|
|
105
|
+
}
|
|
106
|
+
async invokeOperationForConnection(selector, opId, args) {
|
|
107
|
+
this.requireDeclaredOperation(opId);
|
|
108
|
+
try {
|
|
109
|
+
if (opId === 'get-received-attachment') {
|
|
110
|
+
const input = attachmentInputSchema.parse(args);
|
|
111
|
+
const { credentials } = await this.getResolvedConfiguration(selector);
|
|
112
|
+
if (!credentials.apiKey)
|
|
113
|
+
throw new CapabilityError('Selected account credential is missing', 'AUTH_REQUIRED', { reason: 'resend-credential-missing' });
|
|
114
|
+
const baseUrl = await resolveResendBaseUrl(this.context.runtime, this.descriptor.baseUrl);
|
|
115
|
+
const metadataResponse = await fetch(`${baseUrl.replace(/\/$/, '')}/emails/receiving/${encodeURIComponent(input.emailId)}/attachments/${encodeURIComponent(input.attachmentId)}`, {
|
|
116
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` }, redirect: 'error', signal: AbortSignal.timeout(15_000)
|
|
117
|
+
});
|
|
118
|
+
if (!metadataResponse.ok)
|
|
119
|
+
throw new CapabilityError('Resend attachment lookup was refused.', 'UNAVAILABLE', { reason: 'resend-upstream-refused', details: { status: metadataResponse.status } });
|
|
120
|
+
const receipt = attachmentReceiptSchema.safeParse(await readResendResponse(metadataResponse));
|
|
121
|
+
if (!receipt.success)
|
|
122
|
+
throw new CapabilityError('Invalid attachment receipt', 'UNAVAILABLE', { reason: 'resend-attachment-invalid' });
|
|
123
|
+
const metadata = receipt.data;
|
|
124
|
+
if (metadata.id !== input.attachmentId)
|
|
125
|
+
throw new CapabilityError('Invalid attachment receipt identity', 'UNAVAILABLE', { reason: 'resend-attachment-invalid' });
|
|
126
|
+
const url = new URL(metadata.download_url);
|
|
127
|
+
// The short-lived CDN URL is provider-signed. Never forward the account API key.
|
|
128
|
+
const response = await fetch(url, { redirect: 'error', signal: AbortSignal.timeout(20_000) });
|
|
129
|
+
if (!response.ok)
|
|
130
|
+
throw new CapabilityError('Resend attachment download was refused.', 'UNAVAILABLE', { reason: 'resend-upstream-refused', details: { status: response.status } });
|
|
131
|
+
const bytes = await readAttachment(response, metadata.size);
|
|
132
|
+
if (bytes.byteLength !== metadata.size)
|
|
133
|
+
throw new CapabilityError('Attachment size does not match its provider receipt', 'UNAVAILABLE', { reason: 'resend-attachment-invalid' });
|
|
134
|
+
let binary = '';
|
|
135
|
+
for (let offset = 0; offset < bytes.length; offset += 8192)
|
|
136
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + 8192));
|
|
137
|
+
// SAFETY: the legacy owner generic binds T at dispatch; this branch constructs the declared attachment result.
|
|
138
|
+
return { ok: true, data: { content: btoa(binary) } };
|
|
139
|
+
}
|
|
140
|
+
if (opId === 'get-received') {
|
|
141
|
+
const { emailId } = receivedInputSchema.parse(args);
|
|
142
|
+
const { credentials } = await this.getResolvedConfiguration(selector);
|
|
143
|
+
// SAFETY: the owner dispatch generic associates T with get-received; hydration validates the normalized result.
|
|
144
|
+
return { ok: true, data: await retrieveResendReceivedEmail({ emailId, apiKey: credentials.apiKey, baseUrl: await resolveResendBaseUrl(this.context.runtime, this.descriptor.baseUrl) }) };
|
|
145
|
+
}
|
|
146
|
+
if (opId === 'send' || opId === 'check') {
|
|
147
|
+
return invokeEmailIntegrationOperation({
|
|
148
|
+
descriptor: this.descriptor,
|
|
149
|
+
send: (input) => this.send(input, selector),
|
|
150
|
+
check: (messageId) => this.check(messageId, selector)
|
|
151
|
+
}, opId, args);
|
|
152
|
+
}
|
|
153
|
+
throw new Error(`Resend selected-account operation '${opId}' is not implemented.`);
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
const refusal = { ok: false, errors: ['Resend selected-account operation failed.'], failure: valueFreeFailure(CaughtFailureSchema.parse(error)) };
|
|
157
|
+
return refusal;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Bearer-token POST to Resend's send endpoint. Network egress is the
|
|
162
|
+
* canonical work; everything else is shape translation + error mapping.
|
|
163
|
+
* Throws on non-2xx so the typed `send` contract returns either a valid
|
|
164
|
+
* `EmailSendResult` or a thrown error (which `EmailClient.send` and
|
|
165
|
+
* `invokeOperation` translate into the right surface for each caller).
|
|
166
|
+
*/
|
|
167
|
+
async send(input, selector) {
|
|
168
|
+
const { config, credentials } = await this.getResolvedConfiguration(selector);
|
|
169
|
+
const apiKey = credentials.apiKey;
|
|
170
|
+
if (!apiKey) {
|
|
171
|
+
throw new CapabilityError(`Resend integration '${this.descriptor.id}' has no apiKey credential. ` +
|
|
172
|
+
`Connect the integration via the credentials API before calling send.`, 'AUTH_REQUIRED', { reason: 'resend-credential-missing' });
|
|
173
|
+
}
|
|
174
|
+
const baseUrl = await resolveResendBaseUrl(this.context.runtime, this.descriptor.baseUrl);
|
|
175
|
+
const defaultFrom = resendConfigSchema.parse(config).defaultFrom || undefined;
|
|
176
|
+
input = emailSendInputSchema.parse(input);
|
|
177
|
+
if (selector && (!defaultFrom || (input.from && input.from !== defaultFrom))) {
|
|
178
|
+
throw new CapabilityError('Selected Resend delivery requires the saved connection sender; an unrelated From address is not allowed.', 'PERMISSION_DENIED', { reason: 'resend-sender-mismatch' });
|
|
179
|
+
}
|
|
180
|
+
const body = toResendBody(input, defaultFrom);
|
|
181
|
+
const t0 = Date.now();
|
|
182
|
+
const response = await fetch(`${baseUrl}/emails`, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: {
|
|
185
|
+
Authorization: `Bearer ${apiKey}`,
|
|
186
|
+
'Content-Type': 'application/json',
|
|
187
|
+
...Object.fromEntries(input.idempotencyKey ? [['Idempotency-Key', input.idempotencyKey]] : [])
|
|
188
|
+
},
|
|
189
|
+
body: JSON.stringify(body)
|
|
190
|
+
});
|
|
191
|
+
const durationMs = Date.now() - t0;
|
|
192
|
+
if (!response.ok) {
|
|
193
|
+
throw new CapabilityError(`Resend send returned ${response.status}.`, 'UNAVAILABLE', {
|
|
194
|
+
reason: 'resend-upstream-refused', details: { status: response.status }
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const admitted = sendResponseSchema.safeParse(await readResendResponse(response));
|
|
198
|
+
if (!admitted.success)
|
|
199
|
+
throw new CapabilityError('Resend send response missing or invalid id field.', 'UNAVAILABLE', { reason: 'resend-result-invalid' });
|
|
200
|
+
const parsed = admitted.data;
|
|
201
|
+
return {
|
|
202
|
+
messageId: parsed.id,
|
|
203
|
+
providerId: this.descriptor.id,
|
|
204
|
+
durationMs
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* `check(messageId)` performs a status lookup; `check()` performs a
|
|
209
|
+
* health probe by verifying the API key works against Resend's
|
|
210
|
+
* `GET /domains` endpoint.
|
|
211
|
+
*/
|
|
212
|
+
async check(messageId, selector) {
|
|
213
|
+
const { credentials } = await this.getResolvedConfiguration(selector);
|
|
214
|
+
const apiKey = credentials.apiKey;
|
|
215
|
+
if (!apiKey) {
|
|
216
|
+
return {
|
|
217
|
+
ok: false,
|
|
218
|
+
providerId: this.descriptor.id,
|
|
219
|
+
error: 'Resend integration has no apiKey credential.'
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
const baseUrl = await resolveResendBaseUrl(this.context.runtime, this.descriptor.baseUrl);
|
|
223
|
+
if (messageId) {
|
|
224
|
+
const response = await fetch(`${baseUrl}/emails/${encodeURIComponent(messageId)}`, {
|
|
225
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
226
|
+
});
|
|
227
|
+
if (!response.ok) {
|
|
228
|
+
throw new CapabilityError('Resend status lookup was refused.', 'UNAVAILABLE', {
|
|
229
|
+
reason: 'resend-upstream-refused', details: { status: response.status }
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
const admitted = statusResponseSchema.safeParse(await readResendResponse(response));
|
|
233
|
+
if (!admitted.success || admitted.data.id !== messageId)
|
|
234
|
+
throw new CapabilityError('Invalid Resend status response identity or shape.', 'UNAVAILABLE', { reason: 'resend-result-invalid' });
|
|
235
|
+
const data = admitted.data;
|
|
236
|
+
return {
|
|
237
|
+
messageId: data.id,
|
|
238
|
+
status: mapResendStatus(data.last_event),
|
|
239
|
+
providerId: this.descriptor.id,
|
|
240
|
+
timestamp: data.created_at
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
// Health probe: any authenticated endpoint will do; /domains is cheap.
|
|
244
|
+
const response = await fetch(`${baseUrl}/domains`, {
|
|
245
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
246
|
+
});
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
return {
|
|
249
|
+
ok: false,
|
|
250
|
+
providerId: this.descriptor.id,
|
|
251
|
+
error: `Resend /domains returned ${response.status}.`
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
return { ok: true, providerId: this.descriptor.id };
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
export default ResendEmailIntegration;
|
|
258
|
+
//# sourceMappingURL=resend.impl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.impl.js","sourceRoot":"","sources":["../../src/resend/resend.impl.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime plugin for the Resend email integration vendor pack.
|
|
3
|
+
*
|
|
4
|
+
* Registers:
|
|
5
|
+
* 1. The `integration/provider` descriptor + `ResendEmailIntegration` loader.
|
|
6
|
+
* `packages/email`'s `EmailClient` picks this up automatically by
|
|
7
|
+
* filtering `category === 'email'` at dispatch time.
|
|
8
|
+
* 2. The `email/webhook-handler` descriptor + loader for the platform-app
|
|
9
|
+
* `+server.ts` route that ingests Resend webhooks.
|
|
10
|
+
*
|
|
11
|
+
* Also registers the account-aware consumer client and inbound agent source.
|
|
12
|
+
* Hosts register the owning kind plugins before this vendor plugin.
|
|
13
|
+
*/
|
|
14
|
+
import { type ResendHostOptions } from './resend/resend.descriptor.ts';
|
|
15
|
+
/** Omit options to leave the sender to callers; pass options to declare the host's default sender. */
|
|
16
|
+
export declare function createResendPlugin(options?: ResendHostOptions): import("@vibesdotdev/kernel").RuntimePlugin;
|
|
17
|
+
export declare const resendPlugin: import("@vibesdotdev/kernel").RuntimePlugin;
|
|
18
|
+
export default resendPlugin;
|
|
19
|
+
//# sourceMappingURL=resend.plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.plugin.d.ts","sourceRoot":"","sources":["../src/resend.plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,OAA6B,EAA8B,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AASzH,sGAAsG;AACtG,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,iBAAiB,+CA6D7D;AAED,eAAO,MAAM,YAAY,6CAAuB,CAAC;AAEjD,eAAe,YAAY,CAAC"}
|