n8n-nodes-resend 2.6.2 → 2.6.3
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/dist/credentials/ResendApi.credentials.d.ts +2 -2
- package/dist/credentials/ResendOAuth2Api.credentials.d.ts +2 -2
- package/dist/credentials/ResendWebhookSigningSecretApi.credentials.d.ts +1 -1
- package/dist/nodes/Resend/Resend.node.d.ts +2 -2
- package/dist/nodes/Resend/Resend.node.js +4 -4
- package/dist/nodes/Resend/ResendTrigger.node.d.ts +13 -1
- package/dist/nodes/Resend/ResendTrigger.node.js +111 -6
- package/dist/nodes/Resend/actions/account/index.js +1 -1
- package/dist/nodes/Resend/actions/domain/listTrackingDomains.operation.js +2 -2
- package/dist/nodes/Resend/actions/email/send.operation.js +1 -1
- package/dist/nodes/Resend/actions/email/sendAndWait.operation.d.ts +1 -1
- package/dist/nodes/Resend/actions/email/sendAndWait.operation.js +1 -1
- package/dist/nodes/Resend/actions/email/sendBatch.operation.js +17 -4
- package/dist/nodes/Resend/actions/router.js +2 -1
- package/dist/nodes/Resend/actions/workflow/listRunSteps.operation.js +2 -2
- package/dist/nodes/Resend/actions/workflow/listRuns.operation.js +2 -2
- package/dist/nodes/Resend/methods/index.js +6 -6
- package/dist/nodes/Resend/transport/index.d.ts +3 -2
- package/dist/nodes/Resend/transport/index.js +5 -1
- package/dist/nodes/Resend/utils/dynamicFields.d.ts +20 -20
- package/dist/nodes/Resend/utils/sendAndWait/utils.d.ts +2 -2
- package/dist/nodes/Resend/utils/sendAndWait/utils.js +19 -15
- package/package.json +5 -2
|
@@ -5,8 +5,8 @@ declare class ResendApi implements ICredentialType {
|
|
|
5
5
|
displayName: string;
|
|
6
6
|
documentationUrl: string;
|
|
7
7
|
icon: {
|
|
8
|
-
readonly light:
|
|
9
|
-
readonly dark:
|
|
8
|
+
readonly light: "file:resend-icon-black.svg";
|
|
9
|
+
readonly dark: "file:resend-icon-white.svg";
|
|
10
10
|
};
|
|
11
11
|
properties: INodeProperties[];
|
|
12
12
|
authenticate: IAuthenticateGeneric;
|
|
@@ -6,8 +6,8 @@ declare class ResendOAuth2Api implements ICredentialType {
|
|
|
6
6
|
displayName: string;
|
|
7
7
|
documentationUrl: string;
|
|
8
8
|
icon: {
|
|
9
|
-
readonly light:
|
|
10
|
-
readonly dark:
|
|
9
|
+
readonly light: "file:resend-icon-black.svg";
|
|
10
|
+
readonly dark: "file:resend-icon-white.svg";
|
|
11
11
|
};
|
|
12
12
|
properties: INodeProperties[];
|
|
13
13
|
}
|
|
@@ -37,7 +37,6 @@ declare class Resend implements INodeType {
|
|
|
37
37
|
webhookResponse?: undefined;
|
|
38
38
|
workflowData?: undefined;
|
|
39
39
|
} | {
|
|
40
|
-
noWebhookResponse?: undefined;
|
|
41
40
|
webhookResponse: undefined;
|
|
42
41
|
workflowData: {
|
|
43
42
|
json: {
|
|
@@ -46,8 +45,8 @@ declare class Resend implements INodeType {
|
|
|
46
45
|
};
|
|
47
46
|
};
|
|
48
47
|
}[][];
|
|
49
|
-
} | {
|
|
50
48
|
noWebhookResponse?: undefined;
|
|
49
|
+
} | {
|
|
51
50
|
webhookResponse: undefined;
|
|
52
51
|
workflowData: {
|
|
53
52
|
json: {
|
|
@@ -56,6 +55,7 @@ declare class Resend implements INodeType {
|
|
|
56
55
|
};
|
|
57
56
|
};
|
|
58
57
|
}[][];
|
|
58
|
+
noWebhookResponse?: undefined;
|
|
59
59
|
}>;
|
|
60
60
|
execute: typeof router;
|
|
61
61
|
}
|
|
@@ -54,13 +54,13 @@ var Resend = class {
|
|
|
54
54
|
name: "authentication",
|
|
55
55
|
type: "options",
|
|
56
56
|
options: [{
|
|
57
|
-
name: "OAuth2 (Recommended)",
|
|
58
|
-
value: "oAuth2",
|
|
59
|
-
description: "Sign in with your Resend account — n8n registers itself as an OAuth client automatically, no API key needed"
|
|
60
|
-
}, {
|
|
61
57
|
name: "API Key",
|
|
62
58
|
value: "apiKey",
|
|
63
59
|
description: "Manually paste a Resend API key"
|
|
60
|
+
}, {
|
|
61
|
+
name: "OAuth2 (Recommended)",
|
|
62
|
+
value: "oAuth2",
|
|
63
|
+
description: "Sign in with your Resend account — n8n registers itself as an OAuth client automatically, no API key needed"
|
|
64
64
|
}],
|
|
65
65
|
default: "oAuth2"
|
|
66
66
|
},
|
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
import { INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from "n8n-workflow";
|
|
1
|
+
import { ICredentialTestFunctions, ICredentialsDecrypted, IHookFunctions, INodeCredentialTestResult, INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from "n8n-workflow";
|
|
2
2
|
//#region nodes/Resend/ResendTrigger.node.d.ts
|
|
3
3
|
declare class ResendTrigger implements INodeType {
|
|
4
4
|
description: INodeTypeDescription;
|
|
5
|
+
methods: {
|
|
6
|
+
credentialTest: {
|
|
7
|
+
resendWebhookSigningSecretTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult>;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
webhookMethods: {
|
|
11
|
+
default: {
|
|
12
|
+
checkExists(this: IHookFunctions): Promise<boolean>;
|
|
13
|
+
create(this: IHookFunctions): Promise<boolean>;
|
|
14
|
+
delete(this: IHookFunctions): Promise<boolean>;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
5
17
|
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
6
18
|
}
|
|
7
19
|
//#endregion
|
|
@@ -32,6 +32,31 @@ async function verifySvixSignature(payload, svixId, svixTimestamp, svixSignature
|
|
|
32
32
|
}
|
|
33
33
|
throw new n8n_workflow.NodeOperationError(node, "Invalid webhook signature");
|
|
34
34
|
}
|
|
35
|
+
async function resendApiRequest(method, endpoint, body) {
|
|
36
|
+
return await this.helpers.httpRequestWithAuthentication.call(this, "resendApi", {
|
|
37
|
+
url: `https://api.resend.com${endpoint}`,
|
|
38
|
+
method,
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
"User-Agent": "n8n-nodes-resend"
|
|
42
|
+
},
|
|
43
|
+
json: true,
|
|
44
|
+
...body ? { body } : {}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function isNotFoundError(error) {
|
|
48
|
+
var _ref, _e$httpCode, _e$response;
|
|
49
|
+
const e = error;
|
|
50
|
+
const code = (_ref = (_e$httpCode = e === null || e === void 0 ? void 0 : e.httpCode) !== null && _e$httpCode !== void 0 ? _e$httpCode : e === null || e === void 0 ? void 0 : e.statusCode) !== null && _ref !== void 0 ? _ref : e === null || e === void 0 || (_e$response = e.response) === null || _e$response === void 0 ? void 0 : _e$response.status;
|
|
51
|
+
return String(code) === "404";
|
|
52
|
+
}
|
|
53
|
+
async function hasResendApiCredential(context) {
|
|
54
|
+
try {
|
|
55
|
+
return Boolean(await context.getCredentials("resendApi"));
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
35
60
|
var ResendTrigger = class {
|
|
36
61
|
constructor() {
|
|
37
62
|
this.description = {
|
|
@@ -48,10 +73,14 @@ var ResendTrigger = class {
|
|
|
48
73
|
defaults: { name: "Resend Trigger" },
|
|
49
74
|
credentials: [{
|
|
50
75
|
name: "resendWebhookSigningSecretApi",
|
|
51
|
-
required:
|
|
76
|
+
required: false,
|
|
77
|
+
testedBy: "resendWebhookSigningSecretTest"
|
|
78
|
+
}, {
|
|
79
|
+
name: "resendApi",
|
|
80
|
+
required: false
|
|
52
81
|
}],
|
|
53
82
|
triggerPanel: {
|
|
54
|
-
header: "
|
|
83
|
+
header: "Add a Resend API credential to register this webhook automatically, or copy the webhook URL below and paste it into your Resend dashboard webhook configuration.",
|
|
55
84
|
executionsHelp: {
|
|
56
85
|
inactive: "Webhooks have two modes: test and production.<br><br><b>Use test mode while you build your workflow</b>. Click the \"Listen for test event\" button, then paste the test URL into your Resend webhook configuration. The webhook executions will show up in the editor.<br><br><b>Use production mode to run your workflow automatically</b>. Activate the workflow, then paste the production URL into your Resend webhook configuration. These executions will show up in the executions list, but not in the editor.",
|
|
57
86
|
active: "Webhooks have two modes: test and production.<br><br><b>Use test mode while you build your workflow</b>. Click the \"Listen for test event\" button, then paste the test URL into your Resend webhook configuration. The webhook executions will show up in the editor.<br><br><b>Use production mode to run your workflow automatically</b>. Since the workflow is activated, you can paste the production URL into your Resend webhook configuration. These executions will show up in the executions list, but not in the editor."
|
|
@@ -152,16 +181,92 @@ var ResendTrigger = class {
|
|
|
152
181
|
}
|
|
153
182
|
],
|
|
154
183
|
description: "Select the Resend event types to listen for"
|
|
155
|
-
}]
|
|
184
|
+
}],
|
|
185
|
+
usableAsTool: true
|
|
156
186
|
};
|
|
187
|
+
this.methods = { credentialTest: { async resendWebhookSigningSecretTest(credential) {
|
|
188
|
+
var _credential$data;
|
|
189
|
+
const secret = typeof ((_credential$data = credential.data) === null || _credential$data === void 0 ? void 0 : _credential$data.webhookSigningSecret) === "string" ? credential.data.webhookSigningSecret.trim() : "";
|
|
190
|
+
if (!secret.startsWith("whsec_")) return {
|
|
191
|
+
status: "Error",
|
|
192
|
+
message: "Signing secret must start with \"whsec_\". Copy it from the webhook settings page in your Resend dashboard."
|
|
193
|
+
};
|
|
194
|
+
const encoded = secret.slice(6);
|
|
195
|
+
if (!encoded || Buffer.from(encoded, "base64").length === 0) return {
|
|
196
|
+
status: "Error",
|
|
197
|
+
message: "Signing secret is not valid base64"
|
|
198
|
+
};
|
|
199
|
+
return {
|
|
200
|
+
status: "OK",
|
|
201
|
+
message: "Signing secret format is valid"
|
|
202
|
+
};
|
|
203
|
+
} } };
|
|
204
|
+
this.webhookMethods = { default: {
|
|
205
|
+
async checkExists() {
|
|
206
|
+
if (!await hasResendApiCredential(this)) return true;
|
|
207
|
+
const webhookData = this.getWorkflowStaticData("node");
|
|
208
|
+
if (!webhookData.webhookId) return false;
|
|
209
|
+
try {
|
|
210
|
+
await resendApiRequest.call(this, "GET", `/webhooks/${webhookData.webhookId}`);
|
|
211
|
+
return true;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if (isNotFoundError(error)) {
|
|
214
|
+
delete webhookData.webhookId;
|
|
215
|
+
delete webhookData.webhookSigningSecret;
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
throw new n8n_workflow.NodeApiError(this.getNode(), error);
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
async create() {
|
|
222
|
+
var _ref2, _response$data, _ref3, _response$data2;
|
|
223
|
+
if (!await hasResendApiCredential(this)) return true;
|
|
224
|
+
const webhookUrl = this.getNodeWebhookUrl("default");
|
|
225
|
+
const events = this.getNodeParameter("events");
|
|
226
|
+
const response = await resendApiRequest.call(this, "POST", "/webhooks", {
|
|
227
|
+
endpoint: webhookUrl,
|
|
228
|
+
events
|
|
229
|
+
});
|
|
230
|
+
const webhookId = (_ref2 = response.id) !== null && _ref2 !== void 0 ? _ref2 : (_response$data = response.data) === null || _response$data === void 0 ? void 0 : _response$data.id;
|
|
231
|
+
if (!webhookId) throw new n8n_workflow.NodeOperationError(this.getNode(), "Resend did not return a webhook ID when creating the webhook");
|
|
232
|
+
const signingSecret = (_ref3 = response.signing_secret) !== null && _ref3 !== void 0 ? _ref3 : (_response$data2 = response.data) === null || _response$data2 === void 0 ? void 0 : _response$data2.signing_secret;
|
|
233
|
+
const webhookData = this.getWorkflowStaticData("node");
|
|
234
|
+
webhookData.webhookId = webhookId;
|
|
235
|
+
if (signingSecret) webhookData.webhookSigningSecret = signingSecret;
|
|
236
|
+
return true;
|
|
237
|
+
},
|
|
238
|
+
async delete() {
|
|
239
|
+
const webhookData = this.getWorkflowStaticData("node");
|
|
240
|
+
if (!webhookData.webhookId) return true;
|
|
241
|
+
if (await hasResendApiCredential(this)) try {
|
|
242
|
+
await resendApiRequest.call(this, "DELETE", `/webhooks/${webhookData.webhookId}`);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
if (!isNotFoundError(error)) throw new n8n_workflow.NodeApiError(this.getNode(), error);
|
|
245
|
+
}
|
|
246
|
+
delete webhookData.webhookId;
|
|
247
|
+
delete webhookData.webhookSigningSecret;
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
} };
|
|
157
251
|
}
|
|
158
252
|
async webhook() {
|
|
159
253
|
const bodyData = this.getBodyData();
|
|
160
254
|
const headers = this.getHeaderData();
|
|
161
255
|
const request = this.getRequestObject();
|
|
162
256
|
const subscribedEvents = this.getNodeParameter("events");
|
|
163
|
-
const
|
|
164
|
-
|
|
257
|
+
const webhookData = this.getWorkflowStaticData("node");
|
|
258
|
+
let webhookSigningSecret = typeof webhookData.webhookSigningSecret === "string" ? webhookData.webhookSigningSecret : "";
|
|
259
|
+
if (!webhookSigningSecret) try {
|
|
260
|
+
var _ref4;
|
|
261
|
+
webhookSigningSecret = (_ref4 = (await this.getCredentials("resendWebhookSigningSecretApi")).webhookSigningSecret) !== null && _ref4 !== void 0 ? _ref4 : "";
|
|
262
|
+
} catch {
|
|
263
|
+
webhookSigningSecret = "";
|
|
264
|
+
}
|
|
265
|
+
if (!webhookSigningSecret || webhookSigningSecret.trim() === "") {
|
|
266
|
+
this.getResponseObject().status(401).json({ error: "Webhook signing secret is not configured" });
|
|
267
|
+
return { noWebhookResponse: true };
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
165
270
|
var _rawBody;
|
|
166
271
|
const rawBody = (_rawBody = request.rawBody) !== null && _rawBody !== void 0 ? _rawBody : request.body;
|
|
167
272
|
const payload = typeof rawBody === "string" ? rawBody : Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : JSON.stringify(bodyData !== null && bodyData !== void 0 ? bodyData : {});
|
|
@@ -173,7 +278,7 @@ var ResendTrigger = class {
|
|
|
173
278
|
return { noWebhookResponse: true };
|
|
174
279
|
}
|
|
175
280
|
await verifySvixSignature(payload, svixId, svixTimestamp, svixSignature, webhookSigningSecret, this.getNode());
|
|
176
|
-
} catch
|
|
281
|
+
} catch {
|
|
177
282
|
this.getResponseObject().status(401).json({ error: "Invalid webhook signature" });
|
|
178
283
|
return { noWebhookResponse: true };
|
|
179
284
|
}
|
|
@@ -19,7 +19,7 @@ const operations = [{
|
|
|
19
19
|
name: "Disconnect",
|
|
20
20
|
value: "disconnect",
|
|
21
21
|
description: "Revoke the connected Resend OAuth2 grant, ending the connection to your Resend account",
|
|
22
|
-
action: "Disconnect the
|
|
22
|
+
action: "Disconnect the resend account"
|
|
23
23
|
}],
|
|
24
24
|
default: "disconnect"
|
|
25
25
|
}];
|
|
@@ -20,10 +20,10 @@ const description = [require_nodes_Resend_utils_dynamicFields.createDynamicIdFie
|
|
|
20
20
|
} }
|
|
21
21
|
})];
|
|
22
22
|
async function execute(index) {
|
|
23
|
-
var
|
|
23
|
+
var _ref;
|
|
24
24
|
const domainId = require_nodes_Resend_utils_dynamicFields.resolveDynamicIdValue(this, "domainId", index);
|
|
25
25
|
const response = await require_nodes_Resend_transport_index.apiRequest.call(this, "GET", `/domains/${encodeURIComponent(domainId)}/tracking-domains`);
|
|
26
|
-
const executionData = ((
|
|
26
|
+
const executionData = ((_ref = response.data) !== null && _ref !== void 0 ? _ref : []).map((item) => ({
|
|
27
27
|
json: item,
|
|
28
28
|
pairedItem: { item: index }
|
|
29
29
|
}));
|
|
@@ -461,7 +461,7 @@ async function execute(index) {
|
|
|
461
461
|
if (additionalOptions.idempotencyKey) requestHeaders["Idempotency-Key"] = additionalOptions.idempotencyKey;
|
|
462
462
|
let response;
|
|
463
463
|
try {
|
|
464
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
464
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
465
465
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/emails`,
|
|
466
466
|
method: "POST",
|
|
467
467
|
headers: requestHeaders,
|
|
@@ -3,6 +3,6 @@ declare namespace sendAndWait_operation_d_exports {
|
|
|
3
3
|
export { SEND_AND_WAIT_OPERATION, description, execute };
|
|
4
4
|
}
|
|
5
5
|
declare const description: INodeProperties[];
|
|
6
|
-
declare function execute(this: IExecuteFunctions
|
|
6
|
+
declare function execute(this: IExecuteFunctions): Promise<INodeExecutionData[]>;
|
|
7
7
|
//#endregion
|
|
8
8
|
export { SEND_AND_WAIT_OPERATION, description, execute, sendAndWait_operation_d_exports };
|
|
@@ -29,7 +29,7 @@ const description = require_nodes_Resend_utils_sendAndWait_utils.getSendAndWaitP
|
|
|
29
29
|
defaultApproveLabel: "Approve",
|
|
30
30
|
defaultDisapproveLabel: "Decline"
|
|
31
31
|
});
|
|
32
|
-
async function execute(
|
|
32
|
+
async function execute() {
|
|
33
33
|
const email = require_nodes_Resend_utils_sendAndWait_utils.createEmail(this);
|
|
34
34
|
await require_nodes_Resend_utils_sendAndWait_utils.sendResendEmail(this, email);
|
|
35
35
|
const waitTill = require_nodes_Resend_utils_sendAndWait_utils.configureWaitTillDate(this);
|
|
@@ -48,7 +48,8 @@ const description = [{
|
|
|
48
48
|
displayName: "Headers",
|
|
49
49
|
name: "headers",
|
|
50
50
|
type: "fixedCollection",
|
|
51
|
-
default: {},
|
|
51
|
+
default: { headers: [] },
|
|
52
|
+
typeOptions: { multipleValues: true },
|
|
52
53
|
options: [{
|
|
53
54
|
name: "headers",
|
|
54
55
|
displayName: "Header",
|
|
@@ -74,11 +75,20 @@ const description = [{
|
|
|
74
75
|
default: "",
|
|
75
76
|
description: "Email address where replies should be sent. Can be different from the sender address."
|
|
76
77
|
},
|
|
78
|
+
{
|
|
79
|
+
displayName: "Scheduled At",
|
|
80
|
+
name: "scheduledAt",
|
|
81
|
+
type: "string",
|
|
82
|
+
default: "",
|
|
83
|
+
placeholder: "in 1 hour",
|
|
84
|
+
description: "Schedule this email to be sent at a future time. Accepts natural language like \"in 1 hour\" or ISO 8601 format \"2024-12-25T09:00:00Z\". Each email in the batch can be scheduled independently."
|
|
85
|
+
},
|
|
77
86
|
{
|
|
78
87
|
displayName: "Tags",
|
|
79
88
|
name: "tags",
|
|
80
89
|
type: "fixedCollection",
|
|
81
|
-
default: {},
|
|
90
|
+
default: { tags: [] },
|
|
91
|
+
typeOptions: { multipleValues: true },
|
|
82
92
|
options: [{
|
|
83
93
|
name: "tags",
|
|
84
94
|
displayName: "Tag",
|
|
@@ -170,7 +180,8 @@ const description = [{
|
|
|
170
180
|
displayName: "Template Variables",
|
|
171
181
|
name: "templateVariables",
|
|
172
182
|
type: "fixedCollection",
|
|
173
|
-
default: {},
|
|
183
|
+
default: { variables: [] },
|
|
184
|
+
typeOptions: { multipleValues: true },
|
|
174
185
|
description: "Key-value pairs to replace template placeholders. Keys must match the template variable names.",
|
|
175
186
|
options: [{
|
|
176
187
|
name: "variables",
|
|
@@ -298,6 +309,8 @@ async function execute(index) {
|
|
|
298
309
|
});
|
|
299
310
|
const topicId = additionalOptions.topicId;
|
|
300
311
|
if (topicId) emailObj.topic_id = topicId;
|
|
312
|
+
const scheduledAt = additionalOptions.scheduledAt;
|
|
313
|
+
if (scheduledAt) emailObj.scheduled_at = scheduledAt;
|
|
301
314
|
if (contentType === "template") {
|
|
302
315
|
if (!email.templateId) throw new n8n_workflow.NodeOperationError(this.getNode(), "Template Name or ID is required for batch emails when using templates.", { itemIndex: index });
|
|
303
316
|
if (email.html || email.text) throw new n8n_workflow.NodeOperationError(this.getNode(), "HTML/Text Content cannot be used when sending batch emails with templates.", { itemIndex: index });
|
|
@@ -322,7 +335,7 @@ async function execute(index) {
|
|
|
322
335
|
if (batchOptions.idempotency_key) headers["Idempotency-Key"] = batchOptions.idempotency_key;
|
|
323
336
|
let response;
|
|
324
337
|
try {
|
|
325
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
338
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
326
339
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/emails/batch`,
|
|
327
340
|
method: "POST",
|
|
328
341
|
headers,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_nodes_Resend_transport_index = require("../transport/index.js");
|
|
2
3
|
const require_nodes_Resend_actions_account_index = require("./account/index.js");
|
|
3
4
|
const require_nodes_Resend_actions_broadcast_index = require("./broadcast/index.js");
|
|
4
5
|
const require_nodes_Resend_actions_contact_index = require("./contact/index.js");
|
|
@@ -54,7 +55,7 @@ async function router() {
|
|
|
54
55
|
});
|
|
55
56
|
continue;
|
|
56
57
|
}
|
|
57
|
-
|
|
58
|
+
require_nodes_Resend_transport_index.handleResendApiError(this.getNode(), error, i);
|
|
58
59
|
}
|
|
59
60
|
return [returnData];
|
|
60
61
|
}
|
|
@@ -32,11 +32,11 @@ const description = [{
|
|
|
32
32
|
description: "The unique identifier of the workflow run"
|
|
33
33
|
}];
|
|
34
34
|
async function execute(index) {
|
|
35
|
-
var
|
|
35
|
+
var _ref;
|
|
36
36
|
const workflowId = this.getNodeParameter("workflowId", index);
|
|
37
37
|
const runId = this.getNodeParameter("runId", index);
|
|
38
38
|
const response = await require_nodes_Resend_transport_index.apiRequest.call(this, "GET", `/workflows/${encodeURIComponent(workflowId)}/runs/${encodeURIComponent(runId)}/steps`);
|
|
39
|
-
const executionData = ((
|
|
39
|
+
const executionData = ((_ref = response.data) !== null && _ref !== void 0 ? _ref : []).map((item) => ({
|
|
40
40
|
json: item,
|
|
41
41
|
pairedItem: { item: index }
|
|
42
42
|
}));
|
|
@@ -20,10 +20,10 @@ const description = [{
|
|
|
20
20
|
description: "The unique identifier of the workflow to list runs for"
|
|
21
21
|
}];
|
|
22
22
|
async function execute(index) {
|
|
23
|
-
var
|
|
23
|
+
var _ref;
|
|
24
24
|
const workflowId = this.getNodeParameter("workflowId", index);
|
|
25
25
|
const response = await require_nodes_Resend_transport_index.apiRequest.call(this, "GET", `/workflows/${encodeURIComponent(workflowId)}/runs`);
|
|
26
|
-
const executionData = ((
|
|
26
|
+
const executionData = ((_ref = response.data) !== null && _ref !== void 0 ? _ref : []).map((item) => ({
|
|
27
27
|
json: item,
|
|
28
28
|
pairedItem: { item: index }
|
|
29
29
|
}));
|
|
@@ -9,7 +9,7 @@ async function loadDropdownOptions(loadOptionsFunctions, endpoint) {
|
|
|
9
9
|
var _response$data;
|
|
10
10
|
let response;
|
|
11
11
|
try {
|
|
12
|
-
response = await loadOptionsFunctions.helpers.httpRequestWithAuthentication.call(loadOptionsFunctions,
|
|
12
|
+
response = await loadOptionsFunctions.helpers.httpRequestWithAuthentication.call(loadOptionsFunctions, require_nodes_Resend_transport_index.getCredentialType(loadOptionsFunctions), {
|
|
13
13
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}${endpoint}`,
|
|
14
14
|
method: "GET",
|
|
15
15
|
qs: { limit: 100 },
|
|
@@ -50,7 +50,7 @@ async function getTemplateVariables() {
|
|
|
50
50
|
if (normalizedTemplateId.startsWith("={{") || normalizedTemplateId.includes("{{")) return [];
|
|
51
51
|
let response;
|
|
52
52
|
try {
|
|
53
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
53
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
54
54
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/templates/${encodeURIComponent(normalizedTemplateId)}`,
|
|
55
55
|
method: "GET",
|
|
56
56
|
json: true
|
|
@@ -85,7 +85,7 @@ async function getContacts() {
|
|
|
85
85
|
var _response$data2;
|
|
86
86
|
let response;
|
|
87
87
|
try {
|
|
88
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
88
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
89
89
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/contacts`,
|
|
90
90
|
method: "GET",
|
|
91
91
|
qs: { limit: 100 },
|
|
@@ -114,7 +114,7 @@ async function getWebhooks() {
|
|
|
114
114
|
var _response$data3;
|
|
115
115
|
let response;
|
|
116
116
|
try {
|
|
117
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
117
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
118
118
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/webhooks`,
|
|
119
119
|
method: "GET",
|
|
120
120
|
qs: { limit: 100 },
|
|
@@ -142,7 +142,7 @@ async function getEmails() {
|
|
|
142
142
|
var _response$data4;
|
|
143
143
|
let response;
|
|
144
144
|
try {
|
|
145
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
145
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
146
146
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/emails`,
|
|
147
147
|
method: "GET",
|
|
148
148
|
qs: { limit: 100 },
|
|
@@ -174,7 +174,7 @@ async function getReceivedEmails() {
|
|
|
174
174
|
var _response$data5;
|
|
175
175
|
let response;
|
|
176
176
|
try {
|
|
177
|
-
response = await this.helpers.httpRequestWithAuthentication.call(this,
|
|
177
|
+
response = await this.helpers.httpRequestWithAuthentication.call(this, require_nodes_Resend_transport_index.getCredentialType(this), {
|
|
178
178
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/emails/receiving`,
|
|
179
179
|
method: "GET",
|
|
180
180
|
qs: { limit: 100 },
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { IDataObject, IExecuteFunctions, IHttpRequestMethods, INode, INodeExecutionData } from "n8n-workflow";
|
|
1
|
+
import { IDataObject, IExecuteFunctions, IHttpRequestMethods, ILoadOptionsFunctions, INode, INodeExecutionData } from "n8n-workflow";
|
|
2
2
|
//#region nodes/Resend/transport/index.d.ts
|
|
3
3
|
declare function handleResendApiError(node: INode, error: unknown, itemIndex?: number): never;
|
|
4
4
|
declare const RESEND_API_BASE = "https://api.resend.com";
|
|
5
|
+
declare function getCredentialType(context: IExecuteFunctions | ILoadOptionsFunctions): string;
|
|
5
6
|
declare function apiRequest(this: IExecuteFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject | IDataObject[], qs?: IDataObject): Promise<IDataObject>;
|
|
6
7
|
declare function requestList(this: IExecuteFunctions, endpoint: string, extraQs?: IDataObject): Promise<IDataObject[]>;
|
|
7
8
|
declare function normalizeEmailList(value: string | string[] | undefined): string[];
|
|
@@ -31,4 +32,4 @@ interface ListOperation {
|
|
|
31
32
|
}
|
|
32
33
|
declare function createOperationRouter(itemOps: Record<string, ItemOperation>, listOps?: Record<string, ListOperation>): (this: IExecuteFunctions, index: number, operation: string) => Promise<INodeExecutionData[]>;
|
|
33
34
|
//#endregion
|
|
34
|
-
export { RESEND_API_BASE, apiRequest, assertHttpsEndpoint, buildTemplateSendVariables, createListExecutionData, createOperationRouter, handleResendApiError, normalizeEmailList, parseTemplateVariables, requestList };
|
|
35
|
+
export { RESEND_API_BASE, apiRequest, assertHttpsEndpoint, buildTemplateSendVariables, createListExecutionData, createOperationRouter, getCredentialType, handleResendApiError, normalizeEmailList, parseTemplateVariables, requestList };
|
|
@@ -133,7 +133,10 @@ function handleResendApiError(node, error, itemIndex) {
|
|
|
133
133
|
}
|
|
134
134
|
const RESEND_API_BASE = "https://api.resend.com";
|
|
135
135
|
function getCredentialType(context) {
|
|
136
|
-
|
|
136
|
+
let authentication;
|
|
137
|
+
if ("getInputData" in context) authentication = context.getNodeParameter("authentication", 0, "apiKey");
|
|
138
|
+
else authentication = context.getNodeParameter("authentication", "apiKey");
|
|
139
|
+
return authentication === "oAuth2" ? "resendOAuth2Api" : "resendApi";
|
|
137
140
|
}
|
|
138
141
|
async function apiRequest(method, endpoint, body, qs) {
|
|
139
142
|
const options = {
|
|
@@ -265,6 +268,7 @@ exports.assertHttpsEndpoint = assertHttpsEndpoint;
|
|
|
265
268
|
exports.buildTemplateSendVariables = buildTemplateSendVariables;
|
|
266
269
|
exports.createListExecutionData = createListExecutionData;
|
|
267
270
|
exports.createOperationRouter = createOperationRouter;
|
|
271
|
+
exports.getCredentialType = getCredentialType;
|
|
268
272
|
exports.handleResendApiError = handleResendApiError;
|
|
269
273
|
exports.normalizeEmailList = normalizeEmailList;
|
|
270
274
|
exports.parseTemplateVariables = parseTemplateVariables;
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
import { IExecuteFunctions, INodeProperties } from "n8n-workflow";
|
|
2
2
|
//#region nodes/Resend/utils/dynamicFields.d.ts
|
|
3
3
|
declare const RESOURCE_METHOD_MAP: {
|
|
4
|
-
readonly broadcast:
|
|
5
|
-
readonly contact:
|
|
6
|
-
readonly domain:
|
|
7
|
-
readonly webhook:
|
|
8
|
-
readonly contactProperty:
|
|
9
|
-
readonly email:
|
|
10
|
-
readonly receivedEmail:
|
|
11
|
-
readonly segment:
|
|
12
|
-
readonly template:
|
|
13
|
-
readonly topic:
|
|
4
|
+
readonly broadcast: "getBroadcasts";
|
|
5
|
+
readonly contact: "getContacts";
|
|
6
|
+
readonly domain: "getDomains";
|
|
7
|
+
readonly webhook: "getWebhooks";
|
|
8
|
+
readonly contactProperty: "getContactProperties";
|
|
9
|
+
readonly email: "getEmails";
|
|
10
|
+
readonly receivedEmail: "getReceivedEmails";
|
|
11
|
+
readonly segment: "getSegments";
|
|
12
|
+
readonly template: "getTemplates";
|
|
13
|
+
readonly topic: "getTopics";
|
|
14
14
|
};
|
|
15
15
|
declare const RESOURCE_DISPLAY_MAP: {
|
|
16
|
-
readonly broadcast:
|
|
17
|
-
readonly contact:
|
|
18
|
-
readonly domain:
|
|
19
|
-
readonly webhook:
|
|
20
|
-
readonly contactProperty:
|
|
21
|
-
readonly email:
|
|
22
|
-
readonly receivedEmail:
|
|
23
|
-
readonly segment:
|
|
24
|
-
readonly template:
|
|
25
|
-
readonly topic:
|
|
16
|
+
readonly broadcast: "Broadcast";
|
|
17
|
+
readonly contact: "Contact";
|
|
18
|
+
readonly domain: "Domain";
|
|
19
|
+
readonly webhook: "Webhook";
|
|
20
|
+
readonly contactProperty: "Contact Property";
|
|
21
|
+
readonly email: "Email";
|
|
22
|
+
readonly receivedEmail: "Received Email";
|
|
23
|
+
readonly segment: "Segment";
|
|
24
|
+
readonly template: "Template";
|
|
25
|
+
readonly topic: "Topic";
|
|
26
26
|
};
|
|
27
27
|
interface DynamicIdFieldOptions {
|
|
28
28
|
fieldName: string;
|
|
@@ -12,7 +12,6 @@ declare function sendAndWaitWebhook(this: IWebhookFunctions): Promise<{
|
|
|
12
12
|
webhookResponse?: undefined;
|
|
13
13
|
workflowData?: undefined;
|
|
14
14
|
} | {
|
|
15
|
-
noWebhookResponse?: undefined;
|
|
16
15
|
webhookResponse: undefined;
|
|
17
16
|
workflowData: {
|
|
18
17
|
json: {
|
|
@@ -21,8 +20,8 @@ declare function sendAndWaitWebhook(this: IWebhookFunctions): Promise<{
|
|
|
21
20
|
};
|
|
22
21
|
};
|
|
23
22
|
}[][];
|
|
24
|
-
} | {
|
|
25
23
|
noWebhookResponse?: undefined;
|
|
24
|
+
} | {
|
|
26
25
|
webhookResponse: undefined;
|
|
27
26
|
workflowData: {
|
|
28
27
|
json: {
|
|
@@ -31,6 +30,7 @@ declare function sendAndWaitWebhook(this: IWebhookFunctions): Promise<{
|
|
|
31
30
|
};
|
|
32
31
|
};
|
|
33
32
|
}[][];
|
|
33
|
+
noWebhookResponse?: undefined;
|
|
34
34
|
}>;
|
|
35
35
|
declare function getSendAndWaitConfig(context: IExecuteFunctions): SendAndWaitConfig;
|
|
36
36
|
declare function createButton(url: string, label: string, style: string): string;
|
|
@@ -36,6 +36,22 @@ function getSendAndWaitProperties(targetProperties, resource = "email", addition
|
|
|
36
36
|
value: "secondary"
|
|
37
37
|
}]
|
|
38
38
|
};
|
|
39
|
+
const approveLabelProperty = {
|
|
40
|
+
displayName: "Approve Button Label",
|
|
41
|
+
name: "approveLabel",
|
|
42
|
+
type: "string",
|
|
43
|
+
default: "Approve",
|
|
44
|
+
displayOptions: { show: { approvalType: ["single", "double"] } }
|
|
45
|
+
};
|
|
46
|
+
if (options === null || options === void 0 ? void 0 : options.defaultApproveLabel) approveLabelProperty.default = options.defaultApproveLabel;
|
|
47
|
+
const disapproveLabelProperty = {
|
|
48
|
+
displayName: "Disapprove Button Label",
|
|
49
|
+
name: "disapproveLabel",
|
|
50
|
+
type: "string",
|
|
51
|
+
default: "Decline",
|
|
52
|
+
displayOptions: { show: { approvalType: ["double"] } }
|
|
53
|
+
};
|
|
54
|
+
if (options === null || options === void 0 ? void 0 : options.defaultDisapproveLabel) disapproveLabelProperty.default = options.defaultDisapproveLabel;
|
|
39
55
|
const approvalOptionsValues = [
|
|
40
56
|
{
|
|
41
57
|
displayName: "Type of Approval",
|
|
@@ -51,26 +67,14 @@ function getSendAndWaitProperties(targetProperties, resource = "email", addition
|
|
|
51
67
|
value: "double"
|
|
52
68
|
}]
|
|
53
69
|
},
|
|
54
|
-
|
|
55
|
-
displayName: "Approve Button Label",
|
|
56
|
-
name: "approveLabel",
|
|
57
|
-
type: "string",
|
|
58
|
-
default: (options === null || options === void 0 ? void 0 : options.defaultApproveLabel) || "Approve",
|
|
59
|
-
displayOptions: { show: { approvalType: ["single", "double"] } }
|
|
60
|
-
},
|
|
70
|
+
approveLabelProperty,
|
|
61
71
|
...[(options === null || options === void 0 ? void 0 : options.noButtonStyle) ? {} : {
|
|
62
72
|
...buttonStyle,
|
|
63
73
|
displayName: "Approve Button Style",
|
|
64
74
|
name: "buttonApprovalStyle",
|
|
65
75
|
displayOptions: { show: { approvalType: ["single", "double"] } }
|
|
66
76
|
}],
|
|
67
|
-
|
|
68
|
-
displayName: "Disapprove Button Label",
|
|
69
|
-
name: "disapproveLabel",
|
|
70
|
-
type: "string",
|
|
71
|
-
default: (options === null || options === void 0 ? void 0 : options.defaultDisapproveLabel) || "Decline",
|
|
72
|
-
displayOptions: { show: { approvalType: ["double"] } }
|
|
73
|
-
},
|
|
77
|
+
disapproveLabelProperty,
|
|
74
78
|
...[(options === null || options === void 0 ? void 0 : options.noButtonStyle) ? {} : {
|
|
75
79
|
...buttonStyle,
|
|
76
80
|
displayName: "Disapprove Button Style",
|
|
@@ -456,7 +460,7 @@ async function sendResendEmail(context, email) {
|
|
|
456
460
|
if (email.bcc) requestBody.bcc = email.bcc;
|
|
457
461
|
if (email.replyTo) requestBody.reply_to = email.replyTo;
|
|
458
462
|
try {
|
|
459
|
-
await context.helpers.httpRequestWithAuthentication.call(context,
|
|
463
|
+
await context.helpers.httpRequestWithAuthentication.call(context, require_nodes_Resend_transport_index.getCredentialType(context), {
|
|
460
464
|
url: `${require_nodes_Resend_transport_index.RESEND_API_BASE}/emails`,
|
|
461
465
|
method: "POST",
|
|
462
466
|
headers: { "Content-Type": "application/json" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-resend",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.3",
|
|
4
4
|
"description": "Resend integration for n8n: Email, Broadcasts, Templates, Domains, Contacts, Segments, Topics, Webhooks, Workflows, Events, and more",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|
|
@@ -48,12 +48,14 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@biomejs/biome": "~2.5.3",
|
|
51
|
+
"@n8n/node-cli": "^0.39.3",
|
|
51
52
|
"@types/jest": "^30.0.0",
|
|
52
53
|
"@types/node": "^26.1.1",
|
|
53
54
|
"auto-changelog": "^2.6.0",
|
|
55
|
+
"eslint": "9.29.0",
|
|
54
56
|
"rimraf": "^6.1.3",
|
|
55
57
|
"tsdown": "^0.22.4",
|
|
56
|
-
"typescript": "
|
|
58
|
+
"typescript": "~5.9.3",
|
|
57
59
|
"unrun": "^0.3.1"
|
|
58
60
|
},
|
|
59
61
|
"peerDependencies": {
|
|
@@ -66,6 +68,7 @@
|
|
|
66
68
|
"format": "biome format --write nodes credentials",
|
|
67
69
|
"lint": "biome check",
|
|
68
70
|
"lint:fix": "biome check --write",
|
|
71
|
+
"lint:n8n": "n8n-node lint",
|
|
69
72
|
"typecheck": "tsc --noEmit"
|
|
70
73
|
}
|
|
71
74
|
}
|