@lowdefy/connection-sendgrid 0.0.0-experimental-20260720072521 → 0.0.0-experimental-20260720135014

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.
@@ -12,8 +12,8 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import schema from './schema.js';
16
- import SendGridMailSend from './SendGridMailSend/SendGridMailSend.js';
15
+ */ import SendGridMailSend from './SendGridMailSend/SendGridMailSend.js';
16
+ import schema from './schema.js';
17
17
  export default {
18
18
  schema,
19
19
  requests: {
@@ -12,24 +12,34 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import { type } from '@lowdefy/helpers';
16
- import send from '../send.js';
15
+ */ import sendgrid from '@sendgrid/mail';
16
+ import { type } from '@lowdefy/helpers';
17
17
  import schema from './schema.js';
18
+ // https://sendgrid.api-docs.io/v3.0/how-to-use-the-sendgrid-v3-api/api-authentication
19
+ // https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/README.md#email-use-cases
18
20
  async function SendGridMailSend({ request, connection }) {
19
- const messages = type.isArray(request) ? request : [
21
+ const { apiKey, from, templateId, mailSettings } = connection;
22
+ sendgrid.setApiKey(apiKey);
23
+ const messages = (type.isArray(request) ? request : [
20
24
  request
21
- ];
22
- // Send per message so the connection mail filter is enforced in one place.
23
- const results = [];
24
- for (const mail of messages){
25
- results.push(await send({
26
- connection,
27
- mail
25
+ ]).map((msg)=>({
26
+ ...msg,
27
+ from,
28
+ templateId,
29
+ mailSettings
28
30
  }));
31
+ try {
32
+ await sendgrid.send(messages);
33
+ } catch (error) {
34
+ if (error.response) {
35
+ throw new Error('SendGrid request failed.', {
36
+ cause: error
37
+ });
38
+ }
39
+ throw error;
29
40
  }
30
41
  return {
31
- response: 'Mail sent successfully',
32
- results
42
+ response: 'Mail sent successfully'
33
43
  };
34
44
  }
35
45
  SendGridMailSend.schema = schema;
@@ -100,10 +100,6 @@
100
100
  type: 'SendGridMail connection property "apiKey" should be a string.'
101
101
  }
102
102
  },
103
- replyTo: {
104
- $ref: '#/definitions/emails',
105
- description: 'Default email address to reply to.'
106
- },
107
103
  templateId: {
108
104
  type: 'string',
109
105
  description: 'SendGrid email template ID to render email when sending.',
@@ -111,55 +107,6 @@
111
107
  type: 'SendGridMail connection property "templateId" should be a string.'
112
108
  }
113
109
  },
114
- filter: {
115
- type: [
116
- 'object',
117
- 'null'
118
- ],
119
- description: 'Filter to restrict or redirect the recipients of outgoing mail.',
120
- additionalProperties: false,
121
- properties: {
122
- replaceAddress: {
123
- type: [
124
- 'string',
125
- 'null'
126
- ],
127
- description: 'Replace all "to" recipients with this address and drop cc and bcc.',
128
- errorMessage: {
129
- type: 'SendGridMail connection property "filter.replaceAddress" should be a string.'
130
- }
131
- },
132
- allowlist: {
133
- type: [
134
- 'array',
135
- 'null'
136
- ],
137
- description: 'List of domains allowed to receive mail.',
138
- items: {
139
- type: 'string',
140
- errorMessage: {
141
- type: 'SendGridMail connection property "filter.allowlist" should be a list of domain strings.'
142
- }
143
- },
144
- errorMessage: {
145
- type: 'SendGridMail connection property "filter.allowlist" should be an array.'
146
- }
147
- },
148
- regex: {
149
- type: [
150
- 'string',
151
- 'null'
152
- ],
153
- description: 'Regular expression an email address must match to receive mail.',
154
- errorMessage: {
155
- type: 'SendGridMail connection property "filter.regex" should be a string.'
156
- }
157
- }
158
- },
159
- errorMessage: {
160
- type: 'SendGridMail connection property "filter" should be an object.'
161
- }
162
- },
163
110
  mailSettings: {
164
111
  type: 'object',
165
112
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/connection-sendgrid",
3
- "version": "0.0.0-experimental-20260720072521",
3
+ "version": "0.0.0-experimental-20260720135014",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -35,14 +35,14 @@
35
35
  "dist/*"
36
36
  ],
37
37
  "dependencies": {
38
- "@lowdefy/helpers": "0.0.0-experimental-20260720072521",
38
+ "@lowdefy/helpers": "0.0.0-experimental-20260720135014",
39
39
  "@sendgrid/mail": "7.7.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@jest/globals": "28.1.3",
43
- "@lowdefy/ajv": "0.0.0-experimental-20260720072521",
44
- "@swc/cli": "0.8.1",
45
- "@swc/core": "1.15.32",
43
+ "@lowdefy/ajv": "0.0.0-experimental-20260720135014",
44
+ "@swc/cli": "0.8.0",
45
+ "@swc/core": "1.15.18",
46
46
  "@swc/jest": "0.2.39",
47
47
  "jest": "28.1.3"
48
48
  },
@@ -1,81 +0,0 @@
1
- /*
2
- Copyright 2020-2026 Lowdefy, Inc
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License");
5
- you may not use this file except in compliance with the License.
6
- You may obtain a copy of the License at
7
-
8
- http://www.apache.org/licenses/LICENSE-2.0
9
-
10
- Unless required by applicable law or agreed to in writing, software
11
- distributed under the License is distributed on an "AS IS" BASIS,
12
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- See the License for the specific language governing permissions and
14
- limitations under the License.
15
- */ import { type } from '@lowdefy/helpers';
16
- function applyMailFilter({ filter, mail }) {
17
- if (type.isNone(filter)) {
18
- return mail;
19
- }
20
- const { replaceAddress, allowlist, regex } = filter;
21
- if (type.isNone(replaceAddress) && type.isNone(allowlist) && type.isNone(regex)) {
22
- return mail;
23
- }
24
- if (!type.isNone(replaceAddress)) {
25
- return {
26
- ...mail,
27
- to: replaceAddress,
28
- cc: undefined,
29
- bcc: undefined
30
- };
31
- }
32
- const allowedDomains = type.isNone(allowlist) ? null : allowlist.map((domain)=>domain.toLowerCase());
33
- const addressRegex = type.isNone(regex) ? null : new RegExp(regex);
34
- function getBareAddress(recipient) {
35
- if (type.isObject(recipient)) {
36
- return recipient.email.toLowerCase();
37
- }
38
- const match = recipient.match(/<([^>]+)>/);
39
- if (match) {
40
- return match[1].toLowerCase();
41
- }
42
- return recipient.toLowerCase();
43
- }
44
- function isAllowed(recipient) {
45
- const address = getBareAddress(recipient);
46
- if (allowedDomains !== null) {
47
- const domain = address.slice(address.lastIndexOf('@') + 1);
48
- if (!allowedDomains.includes(domain)) {
49
- return false;
50
- }
51
- }
52
- if (addressRegex !== null && !addressRegex.test(address)) {
53
- return false;
54
- }
55
- return true;
56
- }
57
- function filterRecipients(recipients) {
58
- if (type.isNone(recipients)) {
59
- return undefined;
60
- }
61
- if (!type.isArray(recipients)) {
62
- return isAllowed(recipients) ? recipients : undefined;
63
- }
64
- const allowed = recipients.filter(isAllowed);
65
- if (allowed.length === 0) {
66
- return undefined;
67
- }
68
- return allowed;
69
- }
70
- const to = filterRecipients(mail.to);
71
- if (type.isUndefined(to)) {
72
- return null;
73
- }
74
- return {
75
- ...mail,
76
- to,
77
- cc: filterRecipients(mail.cc),
78
- bcc: filterRecipients(mail.bcc)
79
- };
80
- }
81
- export default applyMailFilter;
@@ -1,59 +0,0 @@
1
- /*
2
- Copyright 2020-2026 Lowdefy, Inc
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License");
5
- you may not use this file except in compliance with the License.
6
- You may obtain a copy of the License at
7
-
8
- http://www.apache.org/licenses/LICENSE-2.0
9
-
10
- Unless required by applicable law or agreed to in writing, software
11
- distributed under the License is distributed on an "AS IS" BASIS,
12
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- See the License for the specific language governing permissions and
14
- limitations under the License.
15
- */ import sendgrid from '@sendgrid/mail';
16
- import applyMailFilter from './applyMailFilter.js';
17
- // https://sendgrid.api-docs.io/v3.0/how-to-use-the-sendgrid-v3-api/api-authentication
18
- // https://github.com/sendgrid/sendgrid-nodejs/blob/master/docs/use-cases/README.md#email-use-cases
19
- async function send({ connection, mail }) {
20
- const { apiKey, from, replyTo, templateId, mailSettings, filter } = connection;
21
- sendgrid.setApiKey(apiKey);
22
- const filtered = applyMailFilter({
23
- filter,
24
- mail
25
- });
26
- if (filtered === null) {
27
- return {
28
- messageId: null,
29
- to: null,
30
- filtered: true
31
- };
32
- }
33
- const message = {
34
- ...filtered,
35
- from,
36
- replyTo: filtered.replyTo ?? replyTo,
37
- // Fall back to the mail templateId so an undefined connection templateId
38
- // does not clobber a template set on the mail itself.
39
- templateId: templateId ?? filtered.templateId,
40
- mailSettings
41
- };
42
- try {
43
- const [response] = await sendgrid.send(message);
44
- // Return the post-filter to so callers can record where mail actually went
45
- // when a connection filter (replaceAddress/allowlist/regex) rewrites it.
46
- return {
47
- messageId: response?.headers?.['x-message-id'] ?? null,
48
- to: filtered.to
49
- };
50
- } catch (error) {
51
- if (error.response) {
52
- throw new Error('SendGrid request failed.', {
53
- cause: error
54
- });
55
- }
56
- throw error;
57
- }
58
- }
59
- export default send;