nylas 6.4.2 → 7.0.0-canary.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/config.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { WebhookTriggers } from './models/webhook';
2
+ import ExpressBinding from './server-bindings/express-binding';
1
3
  export declare let apiServer: string | null;
2
4
  export declare function setApiServer(newApiServer: string | null): void;
3
5
  export declare let clientSecret: string;
@@ -15,3 +17,52 @@ export declare type AuthenticateUrlConfig = {
15
17
  provider?: string;
16
18
  scopes?: string[];
17
19
  };
20
+ export declare enum Region {
21
+ Us = "us",
22
+ Canada = "canada",
23
+ Ireland = "ireland",
24
+ Australia = "australia",
25
+ Staging = "staging"
26
+ }
27
+ export declare const DEFAULT_REGION = Region.Us;
28
+ export declare const regionConfig: {
29
+ us: {
30
+ nylasAPIUrl: string;
31
+ dashboardApiUrl: string;
32
+ callbackDomain: string;
33
+ websocketDomain: string;
34
+ telemetryApiUrl: string;
35
+ };
36
+ canada: {
37
+ nylasAPIUrl: string;
38
+ dashboardApiUrl: string;
39
+ callbackDomain: string;
40
+ websocketDomain: string;
41
+ telemetryApiUrl: string;
42
+ };
43
+ ireland: {
44
+ nylasAPIUrl: string;
45
+ dashboardApiUrl: string;
46
+ callbackDomain: string;
47
+ websocketDomain: string;
48
+ telemetryApiUrl: string;
49
+ };
50
+ australia: {
51
+ nylasAPIUrl: string;
52
+ dashboardApiUrl: string;
53
+ callbackDomain: string;
54
+ websocketDomain: string;
55
+ telemetryApiUrl: string;
56
+ };
57
+ staging: {
58
+ nylasAPIUrl: string;
59
+ dashboardApiUrl: string;
60
+ callbackDomain: string;
61
+ websocketDomain: string;
62
+ telemetryApiUrl: string;
63
+ };
64
+ };
65
+ export declare const DEFAULT_WEBHOOK_TRIGGERS: WebhookTriggers[];
66
+ export declare const ServerBindings: {
67
+ express: typeof ExpressBinding;
68
+ };
package/lib/config.js CHANGED
@@ -1,5 +1,11 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ var _a;
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ var webhook_1 = require("./models/webhook");
8
+ var express_binding_1 = __importDefault(require("./server-bindings/express-binding"));
3
9
  exports.apiServer = null;
4
10
  function setApiServer(newApiServer) {
5
11
  exports.apiServer = newApiServer;
@@ -10,3 +16,53 @@ function setClientSecret(newClientSecret) {
10
16
  exports.clientSecret = newClientSecret;
11
17
  }
12
18
  exports.setClientSecret = setClientSecret;
19
+ var Region;
20
+ (function (Region) {
21
+ Region["Us"] = "us";
22
+ Region["Canada"] = "canada";
23
+ Region["Ireland"] = "ireland";
24
+ Region["Australia"] = "australia";
25
+ Region["Staging"] = "staging";
26
+ })(Region = exports.Region || (exports.Region = {}));
27
+ exports.DEFAULT_REGION = Region.Us;
28
+ exports.regionConfig = (_a = {},
29
+ _a[Region.Us] = {
30
+ nylasAPIUrl: 'https://api.nylas.com',
31
+ dashboardApiUrl: 'https://dashboard-api.nylas.com',
32
+ callbackDomain: 'cb.nylas.com',
33
+ websocketDomain: 'tunnel.nylas.com',
34
+ telemetryApiUrl: 'https://cli.nylas.com',
35
+ },
36
+ _a[Region.Canada] = {
37
+ nylasAPIUrl: 'https://canada.api.nylas.com',
38
+ dashboardApiUrl: 'https://canada.dashboard.nylas.com',
39
+ callbackDomain: 'cb.nylas.com',
40
+ websocketDomain: 'tunnel.nylas.com',
41
+ telemetryApiUrl: 'https://cli.nylas.com',
42
+ },
43
+ _a[Region.Ireland] = {
44
+ nylasAPIUrl: 'https://ireland.api.nylas.com',
45
+ dashboardApiUrl: 'https://ireland.dashboard.nylas.com',
46
+ callbackDomain: 'cb.nylas.com',
47
+ websocketDomain: 'tunnel.nylas.com',
48
+ telemetryApiUrl: 'https://cli.nylas.com',
49
+ },
50
+ _a[Region.Australia] = {
51
+ nylasAPIUrl: 'https://australia.api.nylas.com',
52
+ dashboardApiUrl: 'https://australia.dashboard.nylas.com',
53
+ callbackDomain: 'cb.nylas.com',
54
+ websocketDomain: 'tunnel.nylas.com',
55
+ telemetryApiUrl: 'https://cli.nylas.com',
56
+ },
57
+ _a[Region.Staging] = {
58
+ nylasAPIUrl: 'https://api-staging.nylas.com',
59
+ dashboardApiUrl: 'https://staging-dashboard.nylas.com',
60
+ callbackDomain: 'cb.nylas.com',
61
+ websocketDomain: 'tunnel.nylas.com',
62
+ telemetryApiUrl: 'https://cli.nylas.com',
63
+ },
64
+ _a);
65
+ exports.DEFAULT_WEBHOOK_TRIGGERS = Object.values(webhook_1.WebhookTriggers);
66
+ exports.ServerBindings = {
67
+ express: express_binding_1.default,
68
+ };
@@ -8,6 +8,7 @@ export declare enum WebhookTriggers {
8
8
  AccountStopped = "account.stopped",
9
9
  AccountInvalid = "account.invalid",
10
10
  AccountSyncError = "account.sync_error",
11
+ MessageBounced = "message.bounced",
11
12
  MessageCreated = "message.created",
12
13
  MessageOpened = "message.opened",
13
14
  MessageUpdated = "message.updated",
@@ -25,6 +25,7 @@ var WebhookTriggers;
25
25
  WebhookTriggers["AccountStopped"] = "account.stopped";
26
26
  WebhookTriggers["AccountInvalid"] = "account.invalid";
27
27
  WebhookTriggers["AccountSyncError"] = "account.sync_error";
28
+ WebhookTriggers["MessageBounced"] = "message.bounced";
28
29
  WebhookTriggers["MessageCreated"] = "message.created";
29
30
  WebhookTriggers["MessageOpened"] = "message.opened";
30
31
  WebhookTriggers["MessageUpdated"] = "message.updated";
package/lib/nylas.d.ts CHANGED
@@ -9,24 +9,49 @@ import { AuthenticateUrlConfig, NylasConfig } from './config';
9
9
  import AccessToken from './models/access-token';
10
10
  import ApplicationDetails, { ApplicationDetailsProperties } from './models/application-details';
11
11
  declare class Nylas {
12
- static clientId: string;
13
- static get clientSecret(): string;
14
- static set clientSecret(newClientSecret: string);
15
- static get apiServer(): string | null;
16
- static set apiServer(newApiServer: string | null);
17
- static accounts: ManagementModelCollection<ManagementAccount> | RestfulModelCollection<Account>;
18
- static connect: Connect;
19
- static webhooks: ManagementModelCollection<Webhook>;
20
- static config(config: NylasConfig): Nylas;
21
- static clientCredentials(): boolean;
22
- static with(accessToken: string): NylasConnection;
23
- static application(options?: ApplicationDetailsProperties): Promise<ApplicationDetails>;
24
- static exchangeCodeForToken(code: string, callback?: (error: Error | null, accessToken?: string) => void): Promise<AccessToken>;
25
- static urlForAuthentication(options: AuthenticateUrlConfig): string;
12
+ clientId: string;
13
+ get clientSecret(): string;
14
+ set clientSecret(newClientSecret: string);
15
+ get apiServer(): string | null;
16
+ set apiServer(newApiServer: string | null);
17
+ accounts: ManagementModelCollection<ManagementAccount> | RestfulModelCollection<Account>;
18
+ connect: Connect;
19
+ webhooks: ManagementModelCollection<Webhook>;
20
+ constructor(config: NylasConfig);
21
+ /**
22
+ * Checks if the Nylas instance has been configured with credentials
23
+ * @return True if the Nylas instance has been configured with credentials
24
+ */
25
+ clientCredentials(): boolean;
26
+ /**
27
+ * Configure a NylasConnection instance to access a user's resources
28
+ * @param accessToken The access token to access the user's resources
29
+ * @return The configured NylasConnection instance
30
+ */
31
+ with(accessToken: string): NylasConnection;
32
+ /**
33
+ * Return information about a Nylas application
34
+ * @param options Application details to overwrite
35
+ * @return Information about the Nylas application
36
+ */
37
+ application(options?: ApplicationDetailsProperties): Promise<ApplicationDetails>;
38
+ /**
39
+ * Exchange an authorization code for an access token
40
+ * @param code Application details to overwrite
41
+ * @param callback Application details to overwrite
42
+ * @return Information about the Nylas application
43
+ */
44
+ exchangeCodeForToken(code: string, callback?: (error: Error | null, accessToken?: string) => void): Promise<AccessToken>;
45
+ /**
46
+ * Build the URL for authenticating users to your application via Hosted Authentication
47
+ * @param options Configuration for the authentication process
48
+ * @return The URL for hosted authentication
49
+ */
50
+ urlForAuthentication(options: AuthenticateUrlConfig): string;
26
51
  /**
27
52
  * Revoke a single access token
28
53
  * @param accessToken The access token to revoke
29
54
  */
30
- static revoke(accessToken: string): Promise<void>;
55
+ revoke(accessToken: string): Promise<void>;
31
56
  }
32
57
  export = Nylas;
package/lib/nylas.js CHANGED
@@ -23,29 +23,8 @@ var webhook_1 = __importDefault(require("./models/webhook"));
23
23
  var access_token_1 = __importDefault(require("./models/access-token"));
24
24
  var application_details_1 = __importDefault(require("./models/application-details"));
25
25
  var Nylas = /** @class */ (function () {
26
- function Nylas() {
27
- }
28
- Object.defineProperty(Nylas, "clientSecret", {
29
- get: function () {
30
- return config.clientSecret;
31
- },
32
- set: function (newClientSecret) {
33
- config.setClientSecret(newClientSecret);
34
- },
35
- enumerable: true,
36
- configurable: true
37
- });
38
- Object.defineProperty(Nylas, "apiServer", {
39
- get: function () {
40
- return config.apiServer;
41
- },
42
- set: function (newApiServer) {
43
- config.setApiServer(newApiServer);
44
- },
45
- enumerable: true,
46
- configurable: true
47
- });
48
- Nylas.config = function (config) {
26
+ function Nylas(config) {
27
+ this.clientId = '';
49
28
  if (config.apiServer && config.apiServer.indexOf('://') === -1) {
50
29
  throw new Error('Please specify a fully qualified URL for the API Server.');
51
30
  }
@@ -73,17 +52,51 @@ var Nylas = /** @class */ (function () {
73
52
  this.accounts = new restful_model_collection_1.default(account_1.default, conn);
74
53
  }
75
54
  return this;
76
- };
77
- Nylas.clientCredentials = function () {
55
+ }
56
+ Object.defineProperty(Nylas.prototype, "clientSecret", {
57
+ get: function () {
58
+ return config.clientSecret;
59
+ },
60
+ set: function (newClientSecret) {
61
+ config.setClientSecret(newClientSecret);
62
+ },
63
+ enumerable: true,
64
+ configurable: true
65
+ });
66
+ Object.defineProperty(Nylas.prototype, "apiServer", {
67
+ get: function () {
68
+ return config.apiServer;
69
+ },
70
+ set: function (newApiServer) {
71
+ config.setApiServer(newApiServer);
72
+ },
73
+ enumerable: true,
74
+ configurable: true
75
+ });
76
+ /**
77
+ * Checks if the Nylas instance has been configured with credentials
78
+ * @return True if the Nylas instance has been configured with credentials
79
+ */
80
+ Nylas.prototype.clientCredentials = function () {
78
81
  return this.clientId != null && this.clientSecret != null;
79
82
  };
80
- Nylas.with = function (accessToken) {
83
+ /**
84
+ * Configure a NylasConnection instance to access a user's resources
85
+ * @param accessToken The access token to access the user's resources
86
+ * @return The configured NylasConnection instance
87
+ */
88
+ Nylas.prototype.with = function (accessToken) {
81
89
  if (!accessToken) {
82
90
  throw new Error('This function requires an access token');
83
91
  }
84
92
  return new nylas_connection_1.default(accessToken, { clientId: this.clientId });
85
93
  };
86
- Nylas.application = function (options) {
94
+ /**
95
+ * Return information about a Nylas application
96
+ * @param options Application details to overwrite
97
+ * @return Information about the Nylas application
98
+ */
99
+ Nylas.prototype.application = function (options) {
87
100
  if (!this.clientId) {
88
101
  throw new Error('This function requires a clientId');
89
102
  }
@@ -106,7 +119,13 @@ var Nylas = /** @class */ (function () {
106
119
  return new application_details_1.default().fromJSON(res);
107
120
  });
108
121
  };
109
- Nylas.exchangeCodeForToken = function (code, callback) {
122
+ /**
123
+ * Exchange an authorization code for an access token
124
+ * @param code Application details to overwrite
125
+ * @param callback Application details to overwrite
126
+ * @return Information about the Nylas application
127
+ */
128
+ Nylas.prototype.exchangeCodeForToken = function (code, callback) {
110
129
  if (!this.clientId || !this.clientSecret) {
111
130
  throw new Error('exchangeCodeForToken() cannot be called until you provide a clientId and secret via config()');
112
131
  }
@@ -143,7 +162,12 @@ var Nylas = /** @class */ (function () {
143
162
  throw newError;
144
163
  });
145
164
  };
146
- Nylas.urlForAuthentication = function (options) {
165
+ /**
166
+ * Build the URL for authenticating users to your application via Hosted Authentication
167
+ * @param options Configuration for the authentication process
168
+ * @return The URL for hosted authentication
169
+ */
170
+ Nylas.prototype.urlForAuthentication = function (options) {
147
171
  if (!this.clientId) {
148
172
  throw new Error('urlForAuthentication() cannot be called until you provide a clientId via config()');
149
173
  }
@@ -172,15 +196,14 @@ var Nylas = /** @class */ (function () {
172
196
  * Revoke a single access token
173
197
  * @param accessToken The access token to revoke
174
198
  */
175
- Nylas.revoke = function (accessToken) {
176
- return Nylas.with(accessToken)
199
+ Nylas.prototype.revoke = function (accessToken) {
200
+ return this.with(accessToken)
177
201
  .request({
178
202
  method: 'POST',
179
203
  path: '/oauth/revoke',
180
204
  })
181
205
  .catch(function (err) { return Promise.reject(err); });
182
206
  };
183
- Nylas.clientId = '';
184
207
  return Nylas;
185
208
  }());
186
209
  module.exports = Nylas;
@@ -0,0 +1,18 @@
1
+ import Nylas from '../nylas';
2
+ import { RequestHandler, Router } from 'express';
3
+ import { ServerBindingOptions, ServerBinding } from './server-binding';
4
+ export default class ExpressBinding extends ServerBinding {
5
+ constructor(nylasClient: Nylas, options: ServerBindingOptions);
6
+ /**
7
+ * Middleware for the webhook endpoint so we can verify that Nylas is sending the events
8
+ */
9
+ webhookVerificationMiddleware(): RequestHandler;
10
+ /**
11
+ * Build middleware for an Express app with routes for:
12
+ * 1. '/webhook': Receiving webhook events, verifying its authenticity, and emitting webhook objects
13
+ * 2. '/generate-auth-url': Building the URL for authenticating users to your application via Hosted Authentication
14
+ * 3. Exchange an authorization code for an access token
15
+ * @return The routes packaged as Express middleware
16
+ */
17
+ buildMiddleware(): Router;
18
+ }
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ extendStatics(d, b);
11
+ function __() { this.constructor = d; }
12
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
+ };
14
+ })();
15
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
16
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
17
+ return new (P || (P = Promise))(function (resolve, reject) {
18
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
19
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
20
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
21
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
22
+ });
23
+ };
24
+ var __generator = (this && this.__generator) || function (thisArg, body) {
25
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
26
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
27
+ function verb(n) { return function (v) { return step([n, v]); }; }
28
+ function step(op) {
29
+ if (f) throw new TypeError("Generator is already executing.");
30
+ while (_) try {
31
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
32
+ if (y = 0, t) op = [op[0] & 2, t.value];
33
+ switch (op[0]) {
34
+ case 0: case 1: t = op; break;
35
+ case 4: _.label++; return { value: op[1], done: false };
36
+ case 5: _.label++; y = op[1]; op = [0]; continue;
37
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
38
+ default:
39
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
40
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
41
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
42
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
43
+ if (t[2]) _.ops.pop();
44
+ _.trys.pop(); continue;
45
+ }
46
+ op = body.call(thisArg, _);
47
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
48
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
49
+ }
50
+ };
51
+ var __importDefault = (this && this.__importDefault) || function (mod) {
52
+ return (mod && mod.__esModule) ? mod : { "default": mod };
53
+ };
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ var express_1 = __importDefault(require("express"));
56
+ var server_binding_1 = require("./server-binding");
57
+ var body_parser_1 = __importDefault(require("body-parser"));
58
+ var webhook_notification_1 = require("../models/webhook-notification");
59
+ var ExpressBinding = /** @class */ (function (_super) {
60
+ __extends(ExpressBinding, _super);
61
+ function ExpressBinding(nylasClient, options) {
62
+ return _super.call(this, nylasClient, options) || this;
63
+ }
64
+ /**
65
+ * Middleware for the webhook endpoint so we can verify that Nylas is sending the events
66
+ */
67
+ ExpressBinding.prototype.webhookVerificationMiddleware = function () {
68
+ var _this = this;
69
+ return function (req, res, next) {
70
+ var isVerified = _this.verifyWebhookSignature(req.header(server_binding_1.ServerBinding.NYLAS_SIGNATURE_HEADER), req.body);
71
+ if (!isVerified) {
72
+ return res
73
+ .status(401)
74
+ .send('X-Nylas-Signature failed verification 🚷 ');
75
+ }
76
+ next();
77
+ };
78
+ };
79
+ /**
80
+ * Build middleware for an Express app with routes for:
81
+ * 1. '/webhook': Receiving webhook events, verifying its authenticity, and emitting webhook objects
82
+ * 2. '/generate-auth-url': Building the URL for authenticating users to your application via Hosted Authentication
83
+ * 3. Exchange an authorization code for an access token
84
+ * @return The routes packaged as Express middleware
85
+ */
86
+ ExpressBinding.prototype.buildMiddleware = function () {
87
+ var _this = this;
88
+ var router = express_1.default.Router();
89
+ var webhookRoute = '/webhook';
90
+ // For the Nylas webhook endpoint, we should get the raw body to use for verification
91
+ router.use(webhookRoute, body_parser_1.default.raw({ inflate: true, type: 'application/json' }));
92
+ router.use(express_1.default.json(), body_parser_1.default.urlencoded({ limit: '5mb', extended: true }) // support encoded bodies
93
+ );
94
+ router.post(webhookRoute, this.webhookVerificationMiddleware(), function (req, res) {
95
+ var deltas = req.body.deltas || [];
96
+ deltas.forEach(function (d) {
97
+ return _this.handleDeltaEvent(new webhook_notification_1.WebhookDelta().fromJSON(d));
98
+ });
99
+ res.status(200).send('ok');
100
+ });
101
+ router.post('/generate-auth-url', function (req, res) {
102
+ var authUrl = _this.nylasClient.urlForAuthentication({
103
+ loginHint: req.body.email_address,
104
+ redirectURI: (_this.clientUri || '') + req.body.success_url,
105
+ scopes: _this.defaultScopes,
106
+ });
107
+ res.status(200).send(authUrl);
108
+ });
109
+ router.post('/exchange-mailbox-token', function (req, res) { return __awaiter(_this, void 0, void 0, function () {
110
+ var accessTokenObj, e_1;
111
+ return __generator(this, function (_a) {
112
+ switch (_a.label) {
113
+ case 0:
114
+ _a.trys.push([0, 2, , 3]);
115
+ return [4 /*yield*/, this.nylasClient.exchangeCodeForToken(req.body.token)];
116
+ case 1:
117
+ accessTokenObj = _a.sent();
118
+ this.emit(server_binding_1.ServerEvents.TokenExchange, {
119
+ accessTokenObj: accessTokenObj,
120
+ res: res,
121
+ });
122
+ // If the callback event already sent a response then we don't need to do anything
123
+ if (!res.writableEnded) {
124
+ res.status(200).send('success');
125
+ }
126
+ return [3 /*break*/, 3];
127
+ case 2:
128
+ e_1 = _a.sent();
129
+ res.status(500).send(e_1.message);
130
+ return [3 /*break*/, 3];
131
+ case 3: return [2 /*return*/];
132
+ }
133
+ });
134
+ }); });
135
+ return router;
136
+ };
137
+ return ExpressBinding;
138
+ }(server_binding_1.ServerBinding));
139
+ exports.default = ExpressBinding;
@@ -0,0 +1,54 @@
1
+ /// <reference types="node" />
2
+ import { Response, Router } from 'express';
3
+ import { Scope } from '../models/connect';
4
+ import { EventEmitter } from 'events';
5
+ import Webhook, { WebhookTriggers } from '../models/webhook';
6
+ import { WebhookDelta } from '../models/webhook-notification';
7
+ import AccessToken from '../models/access-token';
8
+ import Nylas from '../nylas';
9
+ import { OpenWebhookTunnelOptions } from '../services/tunnel';
10
+ declare type Middleware = Router;
11
+ export declare enum ServerEvents {
12
+ TokenExchange = "token-exchange"
13
+ }
14
+ export declare type ServerBindingOptions = {
15
+ defaultScopes: Scope[];
16
+ clientUri?: string;
17
+ };
18
+ export declare abstract class ServerBinding extends EventEmitter implements ServerBindingOptions {
19
+ nylasClient: Nylas;
20
+ defaultScopes: Scope[];
21
+ clientUri?: string;
22
+ static NYLAS_SIGNATURE_HEADER: string;
23
+ private _untypedOn;
24
+ private _untypedEmit;
25
+ protected constructor(nylasClient: Nylas, options: ServerBindingOptions);
26
+ abstract buildMiddleware(): Middleware;
27
+ on: <K extends WebhookTriggers | ServerEvents>(event: K, listener: (payload: K extends WebhookTriggers ? WebhookDelta : {
28
+ accessTokenObj: AccessToken;
29
+ res: Response<any, Record<string, any>>;
30
+ }) => void) => this;
31
+ emit: <K extends WebhookTriggers | ServerEvents>(event: K, payload: K extends WebhookTriggers ? WebhookDelta : {
32
+ accessTokenObj: AccessToken;
33
+ res: Response<any, Record<string, any>>;
34
+ }) => boolean;
35
+ /**
36
+ * Verify incoming webhook signature came from Nylas
37
+ * @param xNylasSignature The signature to verify
38
+ * @param rawBody The raw body from the payload
39
+ * @return true if the webhook signature was verified from Nylas
40
+ */
41
+ verifyWebhookSignature(xNylasSignature: string, rawBody: Buffer): boolean;
42
+ /**
43
+ * Start a local development websocket to get webhook events
44
+ * @param webhookTunnelConfig Optional configuration for setting region, triggers, and overriding callbacks
45
+ * @return The webhook details response from the API
46
+ */
47
+ startDevelopmentWebsocket(webhookTunnelConfig?: Partial<OpenWebhookTunnelOptions>): Promise<Webhook>;
48
+ /**
49
+ * Delta event processor to be used either by websocket or webhook
50
+ * @param d The delta event
51
+ */
52
+ protected handleDeltaEvent: (d: WebhookDelta) => void;
53
+ }
54
+ export {};
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ extendStatics(d, b);
11
+ function __() { this.constructor = d; }
12
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
+ };
14
+ })();
15
+ var __importDefault = (this && this.__importDefault) || function (mod) {
16
+ return (mod && mod.__esModule) ? mod : { "default": mod };
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ var crypto_1 = __importDefault(require("crypto"));
20
+ var events_1 = require("events");
21
+ var tunnel_1 = require("../services/tunnel");
22
+ var ServerEvents;
23
+ (function (ServerEvents) {
24
+ ServerEvents["TokenExchange"] = "token-exchange";
25
+ })(ServerEvents = exports.ServerEvents || (exports.ServerEvents = {}));
26
+ var ServerBinding = /** @class */ (function (_super) {
27
+ __extends(ServerBinding, _super);
28
+ function ServerBinding(nylasClient, options) {
29
+ var _this = _super.call(this) || this;
30
+ _this._untypedOn = _this.on;
31
+ _this._untypedEmit = _this.emit;
32
+ // Taken from the best StackOverflow answer of all time https://stackoverflow.com/a/56228127
33
+ _this.on = function (event, listener) { return _this._untypedOn(event, listener); };
34
+ _this.emit = function (event, payload) { return _this._untypedEmit(event, payload); };
35
+ /**
36
+ * Delta event processor to be used either by websocket or webhook
37
+ * @param d The delta event
38
+ */
39
+ _this.handleDeltaEvent = function (d) {
40
+ d.type && _this.emit(d.type, d);
41
+ };
42
+ _this.nylasClient = nylasClient;
43
+ _this.defaultScopes = options.defaultScopes;
44
+ _this.clientUri = options.clientUri;
45
+ return _this;
46
+ }
47
+ /**
48
+ * Verify incoming webhook signature came from Nylas
49
+ * @param xNylasSignature The signature to verify
50
+ * @param rawBody The raw body from the payload
51
+ * @return true if the webhook signature was verified from Nylas
52
+ */
53
+ ServerBinding.prototype.verifyWebhookSignature = function (xNylasSignature, rawBody) {
54
+ var digest = crypto_1.default
55
+ .createHmac('sha256', this.nylasClient.clientSecret)
56
+ .update(rawBody)
57
+ .digest('hex');
58
+ return digest === xNylasSignature;
59
+ };
60
+ /**
61
+ * Start a local development websocket to get webhook events
62
+ * @param webhookTunnelConfig Optional configuration for setting region, triggers, and overriding callbacks
63
+ * @return The webhook details response from the API
64
+ */
65
+ ServerBinding.prototype.startDevelopmentWebsocket = function (webhookTunnelConfig) {
66
+ /* eslint-disable no-console */
67
+ var defaultOnClose = function () {
68
+ return console.log('Nylas websocket client connection closed');
69
+ };
70
+ var defaultOnConnectFail = function (e) {
71
+ return console.log('Failed to connect Nylas websocket client', e.message);
72
+ };
73
+ var defaultOnError = function (e) {
74
+ return console.log('Error in Nylas websocket client', e.message);
75
+ };
76
+ var defaultOnConnect = function () {
77
+ return console.log('Nylas websocket client connected');
78
+ };
79
+ /* eslint-enable no-console */
80
+ return tunnel_1.openWebhookTunnel(this.nylasClient, {
81
+ onMessage: (webhookTunnelConfig === null || webhookTunnelConfig === void 0 ? void 0 : webhookTunnelConfig.onMessage) || this.handleDeltaEvent,
82
+ onClose: (webhookTunnelConfig === null || webhookTunnelConfig === void 0 ? void 0 : webhookTunnelConfig.onClose) || defaultOnClose,
83
+ onConnectFail: (webhookTunnelConfig === null || webhookTunnelConfig === void 0 ? void 0 : webhookTunnelConfig.onConnectFail) || defaultOnConnectFail,
84
+ onError: (webhookTunnelConfig === null || webhookTunnelConfig === void 0 ? void 0 : webhookTunnelConfig.onError) || defaultOnError,
85
+ onConnect: (webhookTunnelConfig === null || webhookTunnelConfig === void 0 ? void 0 : webhookTunnelConfig.onConnect) || defaultOnConnect,
86
+ region: webhookTunnelConfig === null || webhookTunnelConfig === void 0 ? void 0 : webhookTunnelConfig.region,
87
+ triggers: webhookTunnelConfig === null || webhookTunnelConfig === void 0 ? void 0 : webhookTunnelConfig.triggers,
88
+ });
89
+ };
90
+ ServerBinding.NYLAS_SIGNATURE_HEADER = 'x-nylas-signature';
91
+ return ServerBinding;
92
+ }(events_1.EventEmitter));
93
+ exports.ServerBinding = ServerBinding;
@@ -0,0 +1,29 @@
1
+ import { client as WebSocketClient } from 'websocket';
2
+ import { Region } from '../config';
3
+ import Nylas from '../nylas';
4
+ import Webhook, { WebhookTriggers } from '../models/webhook';
5
+ import { WebhookDelta } from '../models/webhook-notification';
6
+ export interface OpenWebhookTunnelOptions {
7
+ onMessage: (msg: WebhookDelta) => void;
8
+ onConnectFail?: (error: Error) => void;
9
+ onError?: (error: Error) => void;
10
+ onClose?: (wsClient: WebSocketClient) => void;
11
+ onConnect?: (wsClient: WebSocketClient) => void;
12
+ region?: Region;
13
+ triggers?: WebhookTriggers[];
14
+ }
15
+ /**
16
+ * Open a webhook tunnel and register it with the Nylas API
17
+ * 1. Creates a UUID
18
+ * 2. Opens a websocket connection to Nylas' webhook forwarding service,
19
+ * with the UUID as a header
20
+ * 3. Creates a new webhook pointed at the forwarding service with the UUID as the path
21
+ *
22
+ * When an event is received by the forwarding service, it will push directly to this websocket
23
+ * connection
24
+ *
25
+ * @param nylasClient The configured Nylas application
26
+ * @param config Configuration for the webhook tunnel, including callback functions, region, and events to subscribe to
27
+ * @return The webhook details response from the API
28
+ */
29
+ export declare const openWebhookTunnel: (nylasClient: Nylas, config: OpenWebhookTunnelOptions) => Promise<Webhook>;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var websocket_1 = require("websocket");
4
+ var uuid_1 = require("uuid");
5
+ var config_1 = require("../config");
6
+ var webhook_notification_1 = require("../models/webhook-notification");
7
+ /**
8
+ * Deletes the Nylas webhook
9
+ * @param nylasClient The Nylas application configured with the webhook
10
+ * @param nylasWebhook The Nylas webhook details
11
+ */
12
+ var deleteTunnelWebhook = function (nylasClient, nylasWebhook) {
13
+ if (nylasWebhook && nylasWebhook.id) {
14
+ /* eslint-disable no-console */
15
+ console.log('Shutting down the webhook tunnel and deleting id: ' + nylasWebhook.id);
16
+ /* eslint-enable no-console */
17
+ nylasClient.webhooks.delete(nylasWebhook).then(function () { return process.exit(); });
18
+ }
19
+ };
20
+ /**
21
+ * Create a webhook to the Nylas forwarding service which will pass messages to our websocket
22
+ * @param nylasClient The configured Nylas application
23
+ * @param callbackDomain The domain name of the callback
24
+ * @param tunnelPath The path to the tunnel
25
+ * @param triggers The list of triggers to subscribe to
26
+ * @return The webhook details response from the API
27
+ */
28
+ var buildTunnelWebhook = function (nylasClient, callbackDomain, tunnelPath, triggers) {
29
+ var callbackUrl = "https://" + callbackDomain + "/" + tunnelPath;
30
+ return nylasClient.webhooks
31
+ .build({
32
+ callbackUrl: callbackUrl,
33
+ state: 'active',
34
+ test: true,
35
+ triggers: triggers,
36
+ })
37
+ .save()
38
+ .then(function (webhook) {
39
+ // Ensure that, upon all exits, delete the webhook on the Nylas application
40
+ process.on('SIGINT', function () { return deleteTunnelWebhook(nylasClient, webhook); });
41
+ process.on('SIGTERM', function () { return deleteTunnelWebhook(nylasClient, webhook); });
42
+ process.on('SIGBREAK', function () { return deleteTunnelWebhook(nylasClient, webhook); });
43
+ return webhook;
44
+ });
45
+ };
46
+ /**
47
+ * Open a webhook tunnel and register it with the Nylas API
48
+ * 1. Creates a UUID
49
+ * 2. Opens a websocket connection to Nylas' webhook forwarding service,
50
+ * with the UUID as a header
51
+ * 3. Creates a new webhook pointed at the forwarding service with the UUID as the path
52
+ *
53
+ * When an event is received by the forwarding service, it will push directly to this websocket
54
+ * connection
55
+ *
56
+ * @param nylasClient The configured Nylas application
57
+ * @param config Configuration for the webhook tunnel, including callback functions, region, and events to subscribe to
58
+ * @return The webhook details response from the API
59
+ */
60
+ exports.openWebhookTunnel = function (nylasClient, config) {
61
+ var triggers = config.triggers || config_1.DEFAULT_WEBHOOK_TRIGGERS;
62
+ var region = config.region || config_1.DEFAULT_REGION;
63
+ var _a = config_1.regionConfig[region], websocketDomain = _a.websocketDomain, callbackDomain = _a.callbackDomain;
64
+ // This UUID will map our websocket to a webhook in the forwarding server
65
+ var tunnelId = uuid_1.v4();
66
+ var client = new websocket_1.client({ closeTimeout: 60000 });
67
+ client.on('connectFailed', function (error) {
68
+ config.onConnectFail && config.onConnectFail(error);
69
+ });
70
+ client.on('connect', function (connection) {
71
+ config.onConnect && config.onConnect(client);
72
+ connection.on('error', function (error) {
73
+ config.onError && config.onError(error);
74
+ });
75
+ connection.on('close', function () {
76
+ config.onClose && config.onClose(client);
77
+ });
78
+ connection.on('message', function (message) {
79
+ // This shouldn't happen. If any of these are seen, open an issue
80
+ if (message.type === 'binary') {
81
+ config.onError &&
82
+ config.onError(new Error('Unknown binary message received'));
83
+ return;
84
+ }
85
+ try {
86
+ var req = JSON.parse(message.utf8Data);
87
+ var deltas = JSON.parse(req.body).deltas;
88
+ deltas.forEach(function (delta) {
89
+ return config.onMessage(new webhook_notification_1.WebhookDelta().fromJSON(delta));
90
+ });
91
+ }
92
+ catch (e) {
93
+ config.onError &&
94
+ config.onError(new Error("Error converting Nylas websocket event to JSON: " + (e &&
95
+ e.message)));
96
+ }
97
+ });
98
+ });
99
+ client.connect("wss://" + websocketDomain, undefined, undefined, {
100
+ 'Client-Id': nylasClient.clientId,
101
+ 'Client-Secret': nylasClient.clientSecret,
102
+ 'Tunnel-Id': tunnelId,
103
+ Region: region,
104
+ });
105
+ return buildTunnelWebhook(nylasClient, callbackDomain, tunnelId, triggers);
106
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nylas",
3
- "version": "6.4.2",
3
+ "version": "7.0.0-canary.2",
4
4
  "description": "A NodeJS wrapper for the Nylas REST API for email, contacts, and calendar.",
5
5
  "main": "lib/nylas.js",
6
6
  "types": "lib/nylas.d.ts",
@@ -46,7 +46,11 @@
46
46
  "backoff": "^2.5.0",
47
47
  "form-data": "^4.0.0",
48
48
  "JSONStream": "^1.3.5",
49
- "node-fetch": "^2.6.1"
49
+ "node-fetch": "^2.6.1",
50
+ "express": "^4.17.13",
51
+ "body-parser": "^1.20.0",
52
+ "uuid": "^8.3.2",
53
+ "websocket": "^1.0.34"
50
54
  },
51
55
  "devDependencies": {
52
56
  "@babel/cli": "^7.13.16",
@@ -56,6 +60,9 @@
56
60
  "@types/backoff": "^2.5.1",
57
61
  "@types/jest": "^25.1.4",
58
62
  "@types/node-fetch": "^2.5.8",
63
+ "@types/uuid": "^8.3.4",
64
+ "@types/websocket": "^1.0.5",
65
+ "@types/express": "^4.17.13",
59
66
  "@typescript-eslint/eslint-plugin": "^2.25.0",
60
67
  "@typescript-eslint/parser": "^2.25.0",
61
68
  "babel-eslint": "^10.0.1",