n8n-nodes-jmap 0.1.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.
@@ -0,0 +1,10 @@
1
+ import { ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription, IPollFunctions } from 'n8n-workflow';
2
+ export declare class JmapTrigger implements INodeType {
3
+ description: INodeTypeDescription;
4
+ methods: {
5
+ loadOptions: {
6
+ getMailboxes(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
+ };
8
+ };
9
+ poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null>;
10
+ }
@@ -0,0 +1,236 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JmapTrigger = void 0;
4
+ const GenericFunctions_1 = require("./GenericFunctions");
5
+ class JmapTrigger {
6
+ constructor() {
7
+ this.description = {
8
+ displayName: 'JMAP Trigger',
9
+ name: 'jmapTrigger',
10
+ icon: 'file:jmap.svg',
11
+ group: ['trigger'],
12
+ version: 1,
13
+ subtitle: '={{$parameter["event"]}}',
14
+ description: 'Trigger workflow on new JMAP emails',
15
+ defaults: {
16
+ name: 'JMAP Trigger',
17
+ },
18
+ inputs: [],
19
+ outputs: ['main'],
20
+ credentials: [
21
+ {
22
+ name: 'jmapApi',
23
+ required: true,
24
+ displayOptions: {
25
+ show: {
26
+ authentication: ['jmapApi'],
27
+ },
28
+ },
29
+ },
30
+ {
31
+ name: 'jmapOAuth2Api',
32
+ required: true,
33
+ displayOptions: {
34
+ show: {
35
+ authentication: ['jmapOAuth2Api'],
36
+ },
37
+ },
38
+ },
39
+ ],
40
+ polling: true,
41
+ properties: [
42
+ // Authentication selection
43
+ {
44
+ displayName: 'Authentication',
45
+ name: 'authentication',
46
+ type: 'options',
47
+ options: [
48
+ {
49
+ name: 'OAuth2',
50
+ value: 'jmapOAuth2Api',
51
+ },
52
+ {
53
+ name: 'Basic Auth / Bearer Token',
54
+ value: 'jmapApi',
55
+ },
56
+ ],
57
+ default: 'jmapOAuth2Api',
58
+ description: 'Authentication method to use',
59
+ },
60
+ {
61
+ displayName: 'Event',
62
+ name: 'event',
63
+ type: 'options',
64
+ options: [
65
+ {
66
+ name: 'New Email',
67
+ value: 'newEmail',
68
+ description: 'Triggers when a new email is received',
69
+ },
70
+ {
71
+ name: 'New Email in Mailbox',
72
+ value: 'newEmailInMailbox',
73
+ description: 'Triggers when a new email is received in a specific mailbox',
74
+ },
75
+ ],
76
+ default: 'newEmail',
77
+ required: true,
78
+ },
79
+ {
80
+ displayName: 'Mailbox',
81
+ name: 'mailbox',
82
+ type: 'options',
83
+ typeOptions: {
84
+ loadOptionsMethod: 'getMailboxes',
85
+ },
86
+ displayOptions: {
87
+ show: {
88
+ event: ['newEmailInMailbox'],
89
+ },
90
+ },
91
+ default: '',
92
+ description: 'The mailbox to monitor for new emails',
93
+ },
94
+ {
95
+ displayName: 'Simple Output',
96
+ name: 'simple',
97
+ type: 'boolean',
98
+ default: true,
99
+ description: 'Whether to return a simplified version of the email data',
100
+ },
101
+ {
102
+ displayName: 'Options',
103
+ name: 'options',
104
+ type: 'collection',
105
+ placeholder: 'Add Option',
106
+ default: {},
107
+ options: [
108
+ {
109
+ displayName: 'Include Attachments Info',
110
+ name: 'includeAttachments',
111
+ type: 'boolean',
112
+ default: false,
113
+ description: 'Whether to include attachment information in the output',
114
+ },
115
+ {
116
+ displayName: 'Mark as Read',
117
+ name: 'markAsRead',
118
+ type: 'boolean',
119
+ default: false,
120
+ description: 'Whether to mark fetched emails as read',
121
+ },
122
+ ],
123
+ },
124
+ ],
125
+ };
126
+ this.methods = {
127
+ loadOptions: {
128
+ async getMailboxes() {
129
+ const accountId = await GenericFunctions_1.getPrimaryAccountId.call(this);
130
+ const mailboxes = await GenericFunctions_1.getMailboxes.call(this, accountId);
131
+ return mailboxes.map((mailbox) => ({
132
+ name: `${mailbox.name} (${mailbox.totalEmails} emails)`,
133
+ value: mailbox.id,
134
+ }));
135
+ },
136
+ },
137
+ };
138
+ }
139
+ async poll() {
140
+ const webhookData = this.getWorkflowStaticData('node');
141
+ const event = this.getNodeParameter('event');
142
+ const simple = this.getNodeParameter('simple');
143
+ const options = this.getNodeParameter('options');
144
+ // Get the last processed email timestamp
145
+ const lastProcessedTime = webhookData.lastProcessedTime;
146
+ // Get account ID
147
+ const accountId = await GenericFunctions_1.getPrimaryAccountId.call(this);
148
+ // Build filter
149
+ const filter = {};
150
+ if (event === 'newEmailInMailbox') {
151
+ const mailbox = this.getNodeParameter('mailbox');
152
+ filter.inMailbox = mailbox;
153
+ }
154
+ // If we have a last processed time, only get emails after that
155
+ if (lastProcessedTime) {
156
+ filter.after = lastProcessedTime;
157
+ }
158
+ // Query for new emails
159
+ const { ids } = await GenericFunctions_1.queryEmails.call(this, accountId, filter, [{ property: 'receivedAt', isAscending: false }], 100);
160
+ if (ids.length === 0) {
161
+ return null;
162
+ }
163
+ // Get full email data
164
+ let properties = [
165
+ 'id',
166
+ 'blobId',
167
+ 'threadId',
168
+ 'mailboxIds',
169
+ 'keywords',
170
+ 'receivedAt',
171
+ 'from',
172
+ 'to',
173
+ 'cc',
174
+ 'subject',
175
+ 'preview',
176
+ 'hasAttachment',
177
+ ];
178
+ if (!simple) {
179
+ properties = [
180
+ ...properties,
181
+ 'bodyValues',
182
+ 'textBody',
183
+ 'htmlBody',
184
+ 'bodyStructure',
185
+ ];
186
+ }
187
+ if (options.includeAttachments) {
188
+ properties.push('attachments');
189
+ }
190
+ const emails = await GenericFunctions_1.getEmails.call(this, accountId, ids, properties, !simple, !simple);
191
+ if (emails.length === 0) {
192
+ return null;
193
+ }
194
+ // Update the last processed time to the most recent email
195
+ const mostRecentEmail = emails[0];
196
+ webhookData.lastProcessedTime = mostRecentEmail.receivedAt;
197
+ // Filter out already processed emails if we had a lastProcessedTime
198
+ let newEmails = emails;
199
+ if (lastProcessedTime) {
200
+ newEmails = emails.filter((email) => {
201
+ const emailTime = new Date(email.receivedAt).getTime();
202
+ const lastTime = new Date(lastProcessedTime).getTime();
203
+ return emailTime > lastTime;
204
+ });
205
+ }
206
+ if (newEmails.length === 0) {
207
+ return null;
208
+ }
209
+ // Transform output
210
+ const returnData = newEmails.map((email) => {
211
+ var _a, _b;
212
+ let outputEmail;
213
+ if (simple) {
214
+ outputEmail = {
215
+ id: email.id,
216
+ threadId: email.threadId,
217
+ from: email.from,
218
+ to: email.to,
219
+ cc: email.cc,
220
+ subject: email.subject,
221
+ preview: email.preview,
222
+ receivedAt: email.receivedAt,
223
+ hasAttachment: email.hasAttachment,
224
+ isRead: !!((_a = email.keywords) === null || _a === void 0 ? void 0 : _a.$seen),
225
+ isFlagged: !!((_b = email.keywords) === null || _b === void 0 ? void 0 : _b.$flagged),
226
+ };
227
+ }
228
+ else {
229
+ outputEmail = email;
230
+ }
231
+ return { json: outputEmail };
232
+ });
233
+ return [returnData];
234
+ }
235
+ }
236
+ exports.JmapTrigger = JmapTrigger;
@@ -0,0 +1,14 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
2
+ <defs>
3
+ <linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" style="stop-color:#4A90D9;stop-opacity:1" />
5
+ <stop offset="100%" style="stop-color:#2E5B8E;stop-opacity:1" />
6
+ </linearGradient>
7
+ </defs>
8
+ <!-- Envelope shape -->
9
+ <rect x="4" y="14" width="56" height="40" rx="4" ry="4" fill="url(#grad1)"/>
10
+ <!-- Envelope flap -->
11
+ <path d="M4 18 L32 38 L60 18" fill="none" stroke="#ffffff" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
12
+ <!-- JMAP text -->
13
+ <text x="32" y="49" font-family="Arial, sans-serif" font-size="20" font-weight="bold" fill="#ffffff" text-anchor="middle">JMAP</text>
14
+ </svg>
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "n8n-nodes-jmap",
3
+ "version": "0.1.0",
4
+ "description": "n8n community node for JMAP email protocol (RFC 8620/8621) - Works with Apache James, Twake Mail, Fastmail, and other JMAP-compatible servers",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "jmap",
8
+ "email",
9
+ "apache-james",
10
+ "twake-mail",
11
+ "fastmail",
12
+ "rfc8620",
13
+ "rfc8621"
14
+ ],
15
+ "license": "AGPL-3.0",
16
+ "homepage": "https://github.com/mmaudet/n8n-nodes-jmap",
17
+ "author": {
18
+ "name": "Michel-Marie Maudet",
19
+ "url": "https://github.com/mmaudet"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/mmaudet/n8n-nodes-jmap.git"
24
+ },
25
+ "main": "index.js",
26
+ "scripts": {
27
+ "build": "tsc && gulp build:icons",
28
+ "dev": "tsc --watch",
29
+ "format": "prettier nodes credentials --write",
30
+ "lint": "eslint 'nodes/**/*.ts' 'credentials/**/*.ts'",
31
+ "lintfix": "eslint 'nodes/**/*.ts' 'credentials/**/*.ts' --fix",
32
+ "prepublishOnly": "npm run build && npm run lint -q"
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "n8n": {
38
+ "n8nNodesApiVersion": 1,
39
+ "credentials": [
40
+ "dist/credentials/JmapApi.credentials.js",
41
+ "dist/credentials/JmapOAuth2Api.credentials.js"
42
+ ],
43
+ "nodes": [
44
+ "dist/nodes/Jmap/Jmap.node.js",
45
+ "dist/nodes/Jmap/JmapTrigger.node.js"
46
+ ]
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^20.0.0",
50
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
51
+ "@typescript-eslint/parser": "^7.0.0",
52
+ "eslint": "^8.56.0",
53
+ "gulp": "^4.0.2",
54
+ "n8n-workflow": "*",
55
+ "prettier": "^3.2.0",
56
+ "typescript": "^5.3.0"
57
+ },
58
+ "peerDependencies": {
59
+ "n8n-workflow": "*"
60
+ }
61
+ }