ableton-js 3.1.8 → 3.2.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/index.js CHANGED
@@ -1,184 +1,101 @@
1
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 (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- if (typeof b !== "function" && b !== null)
11
- throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
- extendStatics(d, b);
13
- function __() { this.constructor = d; }
14
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
- };
16
- })();
17
- var __assign = (this && this.__assign) || function () {
18
- __assign = Object.assign || function(t) {
19
- for (var s, i = 1, n = arguments.length; i < n; i++) {
20
- s = arguments[i];
21
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
- t[p] = s[p];
23
- }
24
- return t;
25
- };
26
- return __assign.apply(this, arguments);
27
- };
28
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
29
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
30
- return new (P || (P = Promise))(function (resolve, reject) {
31
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
32
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
33
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
34
- step((generator = generator.apply(thisArg, _arguments || [])).next());
35
- });
36
- };
37
- var __generator = (this && this.__generator) || function (thisArg, body) {
38
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
39
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
40
- function verb(n) { return function (v) { return step([n, v]); }; }
41
- function step(op) {
42
- if (f) throw new TypeError("Generator is already executing.");
43
- while (_) try {
44
- 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;
45
- if (y = 0, t) op = [op[0] & 2, t.value];
46
- switch (op[0]) {
47
- case 0: case 1: t = op; break;
48
- case 4: _.label++; return { value: op[1], done: false };
49
- case 5: _.label++; y = op[1]; op = [0]; continue;
50
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
51
- default:
52
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
53
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
54
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
55
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
56
- if (t[2]) _.ops.pop();
57
- _.trys.pop(); continue;
58
- }
59
- op = body.call(thisArg, _);
60
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
61
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
62
- }
63
- };
64
- var __read = (this && this.__read) || function (o, n) {
65
- var m = typeof Symbol === "function" && o[Symbol.iterator];
66
- if (!m) return o;
67
- var i = m.call(o), r, ar = [], e;
68
- try {
69
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
70
- }
71
- catch (error) { e = { error: error }; }
72
- finally {
73
- try {
74
- if (r && !r.done && (m = i["return"])) m.call(i);
75
- }
76
- finally { if (e) throw e.error; }
77
- }
78
- return ar;
79
- };
80
- var __spreadArray = (this && this.__spreadArray) || function (to, from) {
81
- for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)
82
- to[j] = from[i];
83
- return to;
84
- };
85
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
86
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
87
4
  };
88
5
  Object.defineProperty(exports, "__esModule", { value: true });
89
6
  exports.getPackageVersion = exports.Ableton = exports.TimeoutError = void 0;
90
- var os_1 = __importDefault(require("os"));
91
- var path_1 = __importDefault(require("path"));
92
- var dgram_1 = __importDefault(require("dgram"));
93
- var events_1 = require("events");
94
- var uuid_1 = require("uuid");
95
- var semver_1 = __importDefault(require("semver"));
96
- var zlib_1 = require("zlib");
97
- var lru_cache_1 = __importDefault(require("lru-cache"));
98
- var fs_1 = require("fs");
99
- var promises_1 = require("fs/promises");
100
- var song_1 = require("./ns/song");
101
- var internal_1 = require("./ns/internal");
102
- var application_1 = require("./ns/application");
103
- var midi_1 = require("./ns/midi");
104
- var package_version_1 = require("./util/package-version");
105
- var cache_1 = require("./util/cache");
106
- var SERVER_PORT_FILE = "ableton-js-server.port";
107
- var CLIENT_PORT_FILE = "ableton-js-client.port";
108
- var TimeoutError = /** @class */ (function (_super) {
109
- __extends(TimeoutError, _super);
110
- function TimeoutError(message, payload) {
111
- var _this = _super.call(this, message) || this;
112
- _this.message = message;
113
- _this.payload = payload;
114
- return _this;
7
+ const os_1 = __importDefault(require("os"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const dgram_1 = __importDefault(require("dgram"));
10
+ const events_1 = require("events");
11
+ const uuid_1 = require("uuid");
12
+ const semver_1 = __importDefault(require("semver"));
13
+ const zlib_1 = require("zlib");
14
+ const lru_cache_1 = __importDefault(require("lru-cache"));
15
+ const fs_1 = require("fs");
16
+ const promises_1 = require("fs/promises");
17
+ const song_1 = require("./ns/song");
18
+ const internal_1 = require("./ns/internal");
19
+ const application_1 = require("./ns/application");
20
+ const midi_1 = require("./ns/midi");
21
+ const package_version_1 = require("./util/package-version");
22
+ const cache_1 = require("./util/cache");
23
+ const SERVER_PORT_FILE = "ableton-js-server.port";
24
+ const CLIENT_PORT_FILE = "ableton-js-client.port";
25
+ class TimeoutError extends Error {
26
+ message;
27
+ payload;
28
+ constructor(message, payload) {
29
+ super(message);
30
+ this.message = message;
31
+ this.payload = payload;
115
32
  }
116
- return TimeoutError;
117
- }(Error));
33
+ }
118
34
  exports.TimeoutError = TimeoutError;
119
- var Ableton = /** @class */ (function (_super) {
120
- __extends(Ableton, _super);
121
- function Ableton(options) {
122
- var _a, _b, _c, _d;
123
- var _this = _super.call(this) || this;
124
- _this.options = options;
125
- _this.msgMap = new Map();
126
- _this.eventListeners = new Map();
127
- _this._isConnected = false;
128
- _this.buffer = [];
129
- _this.latency = 0;
130
- _this.song = new song_1.Song(_this);
131
- _this.application = new application_1.Application(_this);
132
- _this.internal = new internal_1.Internal(_this);
133
- _this.midi = new midi_1.Midi(_this);
134
- _this.clientState = "closed";
135
- _this.cancelDisconnectEvent = false;
136
- _this.logger = options === null || options === void 0 ? void 0 : options.logger;
137
- _this.cache = new lru_cache_1.default(__assign({ max: 500, ttl: 1000 * 60 * 10 }, options === null || options === void 0 ? void 0 : options.cacheOptions));
138
- _this.clientPortFile = path_1.default.join(os_1.default.tmpdir(), (_b = (_a = _this.options) === null || _a === void 0 ? void 0 : _a.clientPortFile) !== null && _b !== void 0 ? _b : CLIENT_PORT_FILE);
139
- _this.serverPortFile = path_1.default.join(os_1.default.tmpdir(), (_d = (_c = _this.options) === null || _c === void 0 ? void 0 : _c.serverPortFile) !== null && _d !== void 0 ? _d : SERVER_PORT_FILE);
140
- return _this;
35
+ class Ableton extends events_1.EventEmitter {
36
+ options;
37
+ client;
38
+ msgMap = new Map();
39
+ eventListeners = new Map();
40
+ heartbeatInterval;
41
+ _isConnected = false;
42
+ buffer = [];
43
+ latency = 0;
44
+ serverPort;
45
+ cache;
46
+ song = new song_1.Song(this);
47
+ application = new application_1.Application(this);
48
+ internal = new internal_1.Internal(this);
49
+ midi = new midi_1.Midi(this);
50
+ clientPortFile;
51
+ serverPortFile;
52
+ logger;
53
+ clientState = "closed";
54
+ cancelDisconnectEvent = false;
55
+ constructor(options) {
56
+ super();
57
+ this.options = options;
58
+ this.logger = options?.logger;
59
+ this.cache = new lru_cache_1.default({
60
+ max: 500,
61
+ ttl: 1000 * 60 * 10,
62
+ ...options?.cacheOptions,
63
+ });
64
+ this.clientPortFile = path_1.default.join(os_1.default.tmpdir(), this.options?.clientPortFile ?? CLIENT_PORT_FILE);
65
+ this.serverPortFile = path_1.default.join(os_1.default.tmpdir(), this.options?.serverPortFile ?? SERVER_PORT_FILE);
141
66
  }
142
- Ableton.prototype.handleConnect = function (type) {
143
- var _a;
67
+ handleConnect(type) {
144
68
  if (!this._isConnected) {
145
69
  this._isConnected = true;
146
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Live connected", { type: type });
70
+ this.logger?.info("Live connected", { type });
147
71
  this.emit("connect", type);
148
72
  }
149
- };
150
- Ableton.prototype.handleDisconnect = function (type) {
151
- var _a;
73
+ }
74
+ handleDisconnect(type) {
152
75
  if (this._isConnected) {
153
76
  this._isConnected = false;
154
77
  this.eventListeners.clear();
155
- this.msgMap.forEach(function (msg) { return msg.clearTimeout(); });
78
+ this.msgMap.forEach((msg) => msg.clearTimeout());
156
79
  this.msgMap.clear();
157
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Live disconnected", { type: type });
80
+ this.logger?.info("Live disconnected", { type });
158
81
  this.emit("disconnect", type);
159
82
  }
160
- };
83
+ }
161
84
  /**
162
85
  * If connected, returns immediately. Otherwise,
163
86
  * it waits for a connection event before returning.
164
87
  */
165
- Ableton.prototype.waitForConnection = function () {
166
- return __awaiter(this, void 0, void 0, function () {
167
- var _this = this;
168
- return __generator(this, function (_a) {
169
- if (this._isConnected) {
170
- return [2 /*return*/];
171
- }
172
- else {
173
- return [2 /*return*/, Promise.race([
174
- new Promise(function (res) { return _this.once("connect", res); }),
175
- this.internal.get("ping").catch(function () { return new Promise(function () { }); }),
176
- ])];
177
- }
178
- return [2 /*return*/];
179
- });
180
- });
181
- };
88
+ async waitForConnection() {
89
+ if (this._isConnected) {
90
+ return;
91
+ }
92
+ else {
93
+ return Promise.race([
94
+ new Promise((res) => this.once("connect", res)),
95
+ this.internal.get("ping").catch(() => new Promise(() => { })),
96
+ ]);
97
+ }
98
+ }
182
99
  /**
183
100
  * Starts the server and waits for a connection with Live to be established.
184
101
  *
@@ -186,230 +103,142 @@ var Ableton = /** @class */ (function (_super) {
186
103
  * If set, the function will throw an error if it can't establish a connection
187
104
  * in the given time. Should be higher than 2000ms to avoid false positives.
188
105
  */
189
- Ableton.prototype.start = function (timeoutMs) {
190
- var _a, _b, _c, _d, _e, _f, _g, _h;
191
- return __awaiter(this, void 0, void 0, function () {
192
- var clientPort, port, error_1, connection, timeout, heartbeat;
193
- var _this = this;
194
- return __generator(this, function (_j) {
195
- switch (_j.label) {
196
- case 0:
197
- if (this.clientState !== "closed") {
198
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn("Tried calling start, but client is already " + this.clientState);
199
- return [2 /*return*/];
200
- }
201
- this.clientState = "starting";
202
- this.client = dgram_1.default.createSocket({ type: "udp4" });
203
- this.client.addListener("message", this.handleIncoming.bind(this));
204
- this.client.addListener("listening", function () { return __awaiter(_this, void 0, void 0, function () {
205
- var port;
206
- var _a, _b;
207
- return __generator(this, function (_c) {
208
- switch (_c.label) {
209
- case 0:
210
- port = (_a = this.client) === null || _a === void 0 ? void 0 : _a.address().port;
211
- (_b = this.logger) === null || _b === void 0 ? void 0 : _b.info("Client is bound and listening", { port: port });
212
- // Write used port to a file so Live can read from it
213
- return [4 /*yield*/, promises_1.writeFile(this.clientPortFile, String(port))];
214
- case 1:
215
- // Write used port to a file so Live can read from it
216
- _c.sent();
217
- return [2 /*return*/];
218
- }
219
- });
220
- }); });
221
- _j.label = 1;
222
- case 1:
223
- _j.trys.push([1, 3, , 4]);
224
- // Try binding to the port that was used last for better start performance
225
- (_b = this.logger) === null || _b === void 0 ? void 0 : _b.info("Checking if a stored port exists", {
226
- file: this.clientPortFile,
227
- });
228
- return [4 /*yield*/, promises_1.readFile(this.clientPortFile)];
229
- case 2:
230
- clientPort = _j.sent();
231
- port = Number(clientPort.toString());
232
- (_c = this.logger) === null || _c === void 0 ? void 0 : _c.info("Trying to bind to the most recent port", { port: port });
233
- this.client.bind(port, "127.0.0.1");
234
- return [3 /*break*/, 4];
235
- case 3:
236
- error_1 = _j.sent();
237
- (_d = this.logger) === null || _d === void 0 ? void 0 : _d.info("Couldn't bind to last port, binding to any free port instead", { error: error_1 });
238
- this.client.bind(undefined, "127.0.0.1");
239
- return [3 /*break*/, 4];
240
- case 4:
241
- // Wait for the server port file to exist
242
- return [4 /*yield*/, new Promise(function (res) { return __awaiter(_this, void 0, void 0, function () {
243
- var serverPort, e_1;
244
- var _this = this;
245
- var _a;
246
- return __generator(this, function (_b) {
247
- switch (_b.label) {
248
- case 0:
249
- _b.trys.push([0, 2, , 3]);
250
- return [4 /*yield*/, promises_1.readFile(this.serverPortFile)];
251
- case 1:
252
- serverPort = _b.sent();
253
- this.serverPort = Number(serverPort.toString());
254
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Server port:", { port: this.serverPort });
255
- res();
256
- return [3 /*break*/, 3];
257
- case 2:
258
- e_1 = _b.sent();
259
- return [3 /*break*/, 3];
260
- case 3:
261
- // Set up a watcher in case the server port changes
262
- fs_1.watchFile(this.serverPortFile, function (curr) { return __awaiter(_this, void 0, void 0, function () {
263
- var serverPort, newPort;
264
- var _a;
265
- return __generator(this, function (_b) {
266
- switch (_b.label) {
267
- case 0:
268
- if (!curr.isFile()) return [3 /*break*/, 2];
269
- return [4 /*yield*/, promises_1.readFile(this.serverPortFile)];
270
- case 1:
271
- serverPort = _b.sent();
272
- newPort = Number(serverPort.toString());
273
- if (!isNaN(newPort) && newPort !== this.serverPort) {
274
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Server port changed:", { port: newPort });
275
- this.serverPort = Number(serverPort.toString());
276
- }
277
- res();
278
- _b.label = 2;
279
- case 2: return [2 /*return*/];
280
- }
281
- });
282
- }); });
283
- return [2 /*return*/];
284
- }
285
- });
286
- }); })];
287
- case 5:
288
- // Wait for the server port file to exist
289
- _j.sent();
290
- (_e = this.logger) === null || _e === void 0 ? void 0 : _e.info("Checking connection...");
291
- connection = this.waitForConnection();
292
- if (!timeoutMs) return [3 /*break*/, 7];
293
- timeout = new Promise(function (_, rej) {
294
- return setTimeout(function () { return rej("Connection timed out."); }, timeoutMs);
295
- });
296
- return [4 /*yield*/, Promise.race([connection, timeout])];
297
- case 6:
298
- _j.sent();
299
- return [3 /*break*/, 9];
300
- case 7: return [4 /*yield*/, connection];
301
- case 8:
302
- _j.sent();
303
- _j.label = 9;
304
- case 9:
305
- (_f = this.logger) === null || _f === void 0 ? void 0 : _f.info("Got connection!");
306
- this.clientState = "started";
307
- this.handleConnect("start");
308
- heartbeat = function () { return __awaiter(_this, void 0, void 0, function () {
309
- var e_2;
310
- return __generator(this, function (_a) {
311
- switch (_a.label) {
312
- case 0:
313
- _a.trys.push([0, 2, 3, 4]);
314
- return [4 /*yield*/, this.internal.get("ping")];
315
- case 1:
316
- _a.sent();
317
- this.handleConnect("heartbeat");
318
- return [3 /*break*/, 4];
319
- case 2:
320
- e_2 = _a.sent();
321
- if (!this.cancelDisconnectEvent) {
322
- this.handleDisconnect("heartbeat");
323
- }
324
- return [3 /*break*/, 4];
325
- case 3:
326
- this.cancelDisconnectEvent = false;
327
- return [7 /*endfinally*/];
328
- case 4: return [2 /*return*/];
329
- }
330
- });
331
- }); };
332
- this.heartbeatInterval = setInterval(heartbeat, (_h = (_g = this.options) === null || _g === void 0 ? void 0 : _g.heartbeatInterval) !== null && _h !== void 0 ? _h : 2000);
333
- heartbeat();
334
- this.internal
335
- .get("version")
336
- .then(function (v) {
337
- var _a;
338
- var jsVersion = package_version_1.getPackageVersion();
339
- if (semver_1.default.lt(v, jsVersion)) {
340
- (_a = _this.logger) === null || _a === void 0 ? void 0 : _a.warn("The installed version of your AbletonJS plugin (" + v + ") is lower than the JS library (" + jsVersion + ").", "Please update your AbletonJS plugin to the latest version: https://git.io/JvaOu");
341
- }
342
- })
343
- .catch(function () { });
344
- return [2 /*return*/];
345
- }
346
- });
106
+ async start(timeoutMs) {
107
+ if (this.clientState !== "closed") {
108
+ this.logger?.warn("Tried calling start, but client is already " + this.clientState);
109
+ return;
110
+ }
111
+ this.clientState = "starting";
112
+ this.client = dgram_1.default.createSocket({ type: "udp4" });
113
+ this.client.addListener("message", this.handleIncoming.bind(this));
114
+ this.client.addListener("listening", async () => {
115
+ const port = this.client?.address().port;
116
+ this.logger?.info("Client is bound and listening", { port });
117
+ // Write used port to a file so Live can read from it
118
+ await (0, promises_1.writeFile)(this.clientPortFile, String(port));
347
119
  });
348
- };
349
- /** Closes the client */
350
- Ableton.prototype.close = function () {
351
- var _a, _b;
352
- return __awaiter(this, void 0, void 0, function () {
353
- var closePromise;
354
- var _this = this;
355
- return __generator(this, function (_c) {
356
- switch (_c.label) {
357
- case 0:
358
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.info("Closing the client");
359
- fs_1.unwatchFile(this.serverPortFile);
360
- if (this.heartbeatInterval) {
361
- clearInterval(this.heartbeatInterval);
362
- }
363
- if (!this.client) return [3 /*break*/, 2];
364
- closePromise = new Promise(function (res) { var _a; return (_a = _this.client) === null || _a === void 0 ? void 0 : _a.once("close", res); });
365
- this.client.close();
366
- return [4 /*yield*/, closePromise];
367
- case 1:
368
- _c.sent();
369
- _c.label = 2;
370
- case 2:
371
- this.clientState = "closed";
372
- this._isConnected = false;
373
- (_b = this.logger) === null || _b === void 0 ? void 0 : _b.info("Client closed");
374
- return [2 /*return*/];
120
+ try {
121
+ // Try binding to the port that was used last for better start performance
122
+ this.logger?.info("Checking if a stored port exists", {
123
+ file: this.clientPortFile,
124
+ });
125
+ const clientPort = await (0, promises_1.readFile)(this.clientPortFile);
126
+ const port = Number(clientPort.toString());
127
+ this.logger?.info("Trying to bind to the most recent port", { port });
128
+ this.client.bind(port, "127.0.0.1");
129
+ }
130
+ catch (error) {
131
+ this.logger?.info("Couldn't bind to last port, binding to any free port instead", { error });
132
+ this.client.bind(undefined, "127.0.0.1");
133
+ }
134
+ // Wait for the server port file to exist
135
+ await new Promise(async (res) => {
136
+ try {
137
+ const serverPort = await (0, promises_1.readFile)(this.serverPortFile);
138
+ this.serverPort = Number(serverPort.toString());
139
+ this.logger?.info("Server port:", { port: this.serverPort });
140
+ res();
141
+ }
142
+ catch (e) { }
143
+ // Set up a watcher in case the server port changes
144
+ (0, fs_1.watchFile)(this.serverPortFile, async (curr) => {
145
+ if (curr.isFile()) {
146
+ const serverPort = await (0, promises_1.readFile)(this.serverPortFile);
147
+ const newPort = Number(serverPort.toString());
148
+ if (!isNaN(newPort) && newPort !== this.serverPort) {
149
+ this.logger?.info("Server port changed:", { port: newPort });
150
+ this.serverPort = Number(serverPort.toString());
151
+ }
152
+ res();
375
153
  }
376
154
  });
377
155
  });
378
- };
156
+ this.logger?.info("Checking connection...");
157
+ const connection = this.waitForConnection();
158
+ if (timeoutMs) {
159
+ const timeout = new Promise((_, rej) => setTimeout(() => rej("Connection timed out."), timeoutMs));
160
+ await Promise.race([connection, timeout]);
161
+ }
162
+ else {
163
+ await connection;
164
+ }
165
+ this.logger?.info("Got connection!");
166
+ this.clientState = "started";
167
+ this.handleConnect("start");
168
+ const heartbeat = async () => {
169
+ try {
170
+ await this.internal.get("ping");
171
+ this.handleConnect("heartbeat");
172
+ }
173
+ catch (e) {
174
+ if (!this.cancelDisconnectEvent) {
175
+ this.handleDisconnect("heartbeat");
176
+ }
177
+ }
178
+ finally {
179
+ this.cancelDisconnectEvent = false;
180
+ }
181
+ };
182
+ this.heartbeatInterval = setInterval(heartbeat, this.options?.heartbeatInterval ?? 2000);
183
+ heartbeat();
184
+ this.internal
185
+ .get("version")
186
+ .then((v) => {
187
+ const jsVersion = (0, package_version_1.getPackageVersion)();
188
+ if (semver_1.default.lt(v, jsVersion)) {
189
+ this.logger?.warn(`The installed version of your AbletonJS plugin (${v}) is lower than the JS library (${jsVersion}).`, "Please update your AbletonJS plugin to the latest version: https://git.io/JvaOu");
190
+ }
191
+ })
192
+ .catch(() => { });
193
+ }
194
+ /** Closes the client */
195
+ async close() {
196
+ this.logger?.info("Closing the client");
197
+ (0, fs_1.unwatchFile)(this.serverPortFile);
198
+ if (this.heartbeatInterval) {
199
+ clearInterval(this.heartbeatInterval);
200
+ }
201
+ if (this.client) {
202
+ const closePromise = new Promise((res) => this.client?.once("close", res));
203
+ this.client.close();
204
+ await closePromise;
205
+ }
206
+ this.clientState = "closed";
207
+ this._isConnected = false;
208
+ this.logger?.info("Client closed");
209
+ }
379
210
  /**
380
211
  * Returns the latency between the last command and its response.
381
212
  * This is a rough measurement, so don't rely too much on it.
382
213
  */
383
- Ableton.prototype.getPing = function () {
214
+ getPing() {
384
215
  return this.latency;
385
- };
386
- Ableton.prototype.setPing = function (latency) {
216
+ }
217
+ setPing(latency) {
387
218
  this.latency = latency;
388
219
  this.emit("ping", this.latency);
389
- };
390
- Ableton.prototype.handleIncoming = function (msg, info) {
391
- var _a;
220
+ }
221
+ handleIncoming(msg, info) {
392
222
  try {
393
- var index = msg[0];
394
- var message = msg.slice(1);
223
+ const index = msg[0];
224
+ const message = msg.slice(1);
395
225
  this.buffer[index] = message;
396
226
  // 0xFF signals that the end of the buffer has been reached
397
227
  if (index === 255) {
398
- this.handleUncompressedMessage(zlib_1.unzipSync(Buffer.concat(this.buffer.filter(function (b) { return b; }))).toString());
228
+ this.handleUncompressedMessage((0, zlib_1.unzipSync)(Buffer.concat(this.buffer.filter((b) => b))).toString());
399
229
  this.buffer = [];
400
230
  }
401
231
  }
402
232
  catch (e) {
403
233
  this.buffer = [];
404
- (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn("Couldn't handle message:", { error: e });
234
+ this.logger?.warn("Couldn't handle message:", { error: e });
405
235
  this.emit("error", e);
406
236
  }
407
- };
408
- Ableton.prototype.handleUncompressedMessage = function (msg) {
409
- var _a, _b, _c, _d;
237
+ }
238
+ handleUncompressedMessage(msg) {
410
239
  this.emit("raw_message", msg);
411
- var data = JSON.parse(msg);
412
- var functionCallback = this.msgMap.get(data.uuid);
240
+ const data = JSON.parse(msg);
241
+ const functionCallback = this.msgMap.get(data.uuid);
413
242
  this.emit("message", data);
414
243
  if (data.event === "result" && functionCallback) {
415
244
  this.msgMap.delete(data.uuid);
@@ -426,215 +255,174 @@ var Ableton = /** @class */ (function (_super) {
426
255
  // If some heartbeat ping from the old connection is still pending,
427
256
  // cancel it to prevent a double disconnect/connect event.
428
257
  this.cancelDisconnectEvent = true;
429
- if (((_a = data.data) === null || _a === void 0 ? void 0 : _a.port) && ((_b = data.data) === null || _b === void 0 ? void 0 : _b.port) !== this.serverPort) {
430
- (_c = this.logger) === null || _c === void 0 ? void 0 : _c.info("Got new server port via connect:", {
258
+ if (data.data?.port && data.data?.port !== this.serverPort) {
259
+ this.logger?.info("Got new server port via connect:", {
431
260
  port: data.data.port,
432
261
  });
433
262
  this.serverPort = data.data.port;
434
263
  }
435
264
  return this.handleConnect(this.clientState === "starting" ? "start" : "realtime");
436
265
  }
437
- var eventCallback = this.eventListeners.get(data.event);
266
+ const eventCallback = this.eventListeners.get(data.event);
438
267
  if (eventCallback) {
439
- return eventCallback.forEach(function (cb) { return cb(data.data); });
268
+ return eventCallback.forEach((cb) => cb(data.data));
440
269
  }
441
270
  if (data.uuid) {
442
- (_d = this.logger) === null || _d === void 0 ? void 0 : _d.warn("Message could not be assigned to any request:", {
443
- msg: msg,
271
+ this.logger?.warn("Message could not be assigned to any request:", {
272
+ msg,
444
273
  });
445
274
  }
446
- };
275
+ }
447
276
  /**
448
277
  * Sends a raw command to Ableton. Usually, you won't need this.
449
278
  * A good starting point in general is the `song` prop.
450
279
  */
451
- Ableton.prototype.sendCommand = function (command, timeout) {
452
- if (timeout === void 0) { timeout = 2000; }
453
- return __awaiter(this, void 0, void 0, function () {
454
- var _this = this;
455
- return __generator(this, function (_a) {
456
- return [2 /*return*/, new Promise(function (res, rej) {
457
- var msgId = uuid_1.v4();
458
- var payload = __assign({ uuid: msgId }, command);
459
- var msg = JSON.stringify(payload);
460
- var timeoutId = setTimeout(function () {
461
- var arg = JSON.stringify(command.args);
462
- var cls = command.nsid
463
- ? command.ns + "(" + command.nsid + ")"
464
- : command.ns;
465
- rej(new TimeoutError([
466
- "The command " + cls + "." + command.name + "(" + arg + ") timed out after " + timeout + " ms.",
467
- "Please make sure that Ableton is running and that you have the latest",
468
- "version of AbletonJS' MIDI script installed and renamed to \"AbletonJS\".",
469
- ].join(" "), payload));
470
- }, timeout);
471
- var currentTimestamp = Date.now();
472
- _this.msgMap.set(msgId, {
473
- res: function (result) {
474
- _this.setPing(Date.now() - currentTimestamp);
475
- clearTimeout(timeoutId);
476
- res(result);
477
- },
478
- rej: rej,
479
- clearTimeout: function () {
480
- clearTimeout(timeoutId);
481
- },
482
- });
483
- _this.sendRaw(msg);
484
- })];
485
- });
486
- });
487
- };
488
- Ableton.prototype.sendCachedCommand = function (command, timeout) {
489
- var _a, _b;
490
- return __awaiter(this, void 0, void 0, function () {
491
- var args, cacheKey, cached, result;
492
- return __generator(this, function (_c) {
493
- switch (_c.label) {
494
- case 0:
495
- args = (_b = (_a = command.args) === null || _a === void 0 ? void 0 : _a.prop) !== null && _b !== void 0 ? _b : JSON.stringify(command.args);
496
- cacheKey = [command.ns, command.nsid, args].filter(Boolean).join("/");
497
- cached = this.cache.get(cacheKey);
498
- return [4 /*yield*/, this.sendCommand(__assign(__assign({}, command), { etag: cached === null || cached === void 0 ? void 0 : cached.etag, cache: true }), timeout)];
499
- case 1:
500
- result = _c.sent();
501
- if (cache_1.isCached(result)) {
502
- if (!cached) {
503
- throw new Error("Tried to get an object that isn't cached.");
504
- }
505
- else {
506
- return [2 /*return*/, cached.data];
507
- }
508
- }
509
- else {
510
- if (result.etag) {
511
- this.cache.set(cacheKey, result);
512
- }
513
- return [2 /*return*/, result.data];
514
- }
515
- return [2 /*return*/];
516
- }
280
+ async sendCommand(command) {
281
+ return new Promise((res, rej) => {
282
+ const msgId = (0, uuid_1.v4)();
283
+ const payload = {
284
+ uuid: msgId,
285
+ ...command,
286
+ };
287
+ const msg = JSON.stringify(payload);
288
+ const timeout = this.options?.commandTimeoutMs ?? 2000;
289
+ const timeoutId = setTimeout(() => {
290
+ const arg = JSON.stringify(command.args);
291
+ const cls = command.nsid
292
+ ? `${command.ns}(${command.nsid})`
293
+ : command.ns;
294
+ rej(new TimeoutError([
295
+ `The command ${cls}.${command.name}(${arg}) timed out after ${timeout} ms.`,
296
+ `Please make sure that Ableton is running and that you have the latest`,
297
+ `version of AbletonJS' MIDI script installed and renamed to "AbletonJS".`,
298
+ ].join(" "), payload));
299
+ }, timeout);
300
+ const currentTimestamp = Date.now();
301
+ this.msgMap.set(msgId, {
302
+ res: (result) => {
303
+ this.setPing(Date.now() - currentTimestamp);
304
+ clearTimeout(timeoutId);
305
+ res(result);
306
+ },
307
+ rej,
308
+ clearTimeout: () => {
309
+ clearTimeout(timeoutId);
310
+ },
517
311
  });
312
+ this.sendRaw(msg);
518
313
  });
519
- };
520
- Ableton.prototype.getProp = function (ns, nsid, prop, cache) {
521
- return __awaiter(this, void 0, void 0, function () {
522
- var params;
523
- return __generator(this, function (_a) {
524
- params = { ns: ns, nsid: nsid, name: "get_prop", args: { prop: prop } };
525
- if (cache) {
526
- return [2 /*return*/, this.sendCachedCommand(params)];
527
- }
528
- else {
529
- return [2 /*return*/, this.sendCommand(params)];
530
- }
531
- return [2 /*return*/];
532
- });
314
+ }
315
+ async sendCachedCommand(command) {
316
+ const args = command.args?.prop ?? JSON.stringify(command.args);
317
+ const cacheKey = [command.ns, command.nsid, args].filter(Boolean).join("/");
318
+ const cached = this.cache.get(cacheKey);
319
+ const result = await this.sendCommand({
320
+ ...command,
321
+ etag: cached?.etag,
322
+ cache: true,
533
323
  });
534
- };
535
- Ableton.prototype.setProp = function (ns, nsid, prop, value) {
536
- return __awaiter(this, void 0, void 0, function () {
537
- return __generator(this, function (_a) {
538
- return [2 /*return*/, this.sendCommand({
539
- ns: ns,
540
- nsid: nsid,
541
- name: "set_prop",
542
- args: { prop: prop, value: value },
543
- })];
544
- });
324
+ if ((0, cache_1.isCached)(result)) {
325
+ if (!cached) {
326
+ throw new Error("Tried to get an object that isn't cached.");
327
+ }
328
+ else {
329
+ return cached.data;
330
+ }
331
+ }
332
+ else {
333
+ if (result.etag) {
334
+ this.cache.set(cacheKey, result);
335
+ }
336
+ return result.data;
337
+ }
338
+ }
339
+ async getProp(ns, nsid, prop, cache) {
340
+ const params = { ns, nsid, name: "get_prop", args: { prop } };
341
+ if (cache) {
342
+ return this.sendCachedCommand(params);
343
+ }
344
+ else {
345
+ return this.sendCommand(params);
346
+ }
347
+ }
348
+ async setProp(ns, nsid, prop, value) {
349
+ return this.sendCommand({
350
+ ns,
351
+ nsid,
352
+ name: "set_prop",
353
+ args: { prop, value },
545
354
  });
546
- };
547
- Ableton.prototype.addPropListener = function (ns, nsid, prop, listener) {
548
- return __awaiter(this, void 0, void 0, function () {
549
- var eventId, result;
550
- var _this = this;
551
- return __generator(this, function (_a) {
552
- switch (_a.label) {
553
- case 0:
554
- eventId = uuid_1.v4();
555
- return [4 /*yield*/, this.sendCommand({
556
- ns: ns,
557
- nsid: nsid,
558
- name: "add_listener",
559
- args: { prop: prop, nsid: nsid, eventId: eventId },
560
- })];
561
- case 1:
562
- result = _a.sent();
563
- if (!this.eventListeners.has(result)) {
564
- this.eventListeners.set(result, [listener]);
565
- }
566
- else {
567
- this.eventListeners.set(result, __spreadArray(__spreadArray([], __read(this.eventListeners.get(result))), [
568
- listener,
569
- ]));
570
- }
571
- return [2 /*return*/, function () { return _this.removePropListener(ns, nsid, prop, result, listener); }];
572
- }
573
- });
355
+ }
356
+ async addPropListener(ns, nsid, prop, listener) {
357
+ const eventId = (0, uuid_1.v4)();
358
+ const result = await this.sendCommand({
359
+ ns,
360
+ nsid,
361
+ name: "add_listener",
362
+ args: { prop, nsid, eventId },
574
363
  });
575
- };
576
- Ableton.prototype.removePropListener = function (ns, nsid, prop, eventId, listener) {
577
- return __awaiter(this, void 0, void 0, function () {
578
- var listeners;
579
- return __generator(this, function (_a) {
580
- switch (_a.label) {
581
- case 0:
582
- listeners = this.eventListeners.get(eventId);
583
- if (!listeners) {
584
- return [2 /*return*/, false];
585
- }
586
- if (listeners.length > 1) {
587
- this.eventListeners.set(eventId, listeners.filter(function (l) { return l !== listener; }));
588
- return [2 /*return*/, true];
589
- }
590
- if (!(listeners.length === 1)) return [3 /*break*/, 2];
591
- this.eventListeners.delete(eventId);
592
- return [4 /*yield*/, this.sendCommand({
593
- ns: ns,
594
- nsid: nsid,
595
- name: "remove_listener",
596
- args: { prop: prop, nsid: nsid },
597
- })];
598
- case 1:
599
- _a.sent();
600
- return [2 /*return*/, true];
601
- case 2: return [2 /*return*/];
602
- }
364
+ if (!this.eventListeners.has(result)) {
365
+ this.eventListeners.set(result, [listener]);
366
+ }
367
+ else {
368
+ this.eventListeners.set(result, [
369
+ ...this.eventListeners.get(result),
370
+ listener,
371
+ ]);
372
+ }
373
+ return () => this.removePropListener(ns, nsid, prop, result, listener);
374
+ }
375
+ async removePropListener(ns, nsid, prop, eventId, listener) {
376
+ const listeners = this.eventListeners.get(eventId);
377
+ if (!listeners) {
378
+ return false;
379
+ }
380
+ if (listeners.length > 1) {
381
+ this.eventListeners.set(eventId, listeners.filter((l) => l !== listener));
382
+ return true;
383
+ }
384
+ if (listeners.length === 1) {
385
+ this.eventListeners.delete(eventId);
386
+ await this.sendCommand({
387
+ ns,
388
+ nsid,
389
+ name: "remove_listener",
390
+ args: { prop, nsid },
603
391
  });
604
- });
605
- };
392
+ return true;
393
+ }
394
+ }
606
395
  /**
607
396
  * Removes all event listeners that were attached to properties.
608
397
  * This is useful for clearing all listeners when Live
609
398
  * disconnects, for example.
610
399
  */
611
- Ableton.prototype.removeAllPropListeners = function () {
400
+ removeAllPropListeners() {
612
401
  this.eventListeners.clear();
613
- };
614
- Ableton.prototype.sendRaw = function (msg) {
402
+ }
403
+ sendRaw(msg) {
615
404
  if (!this.client || !this.serverPort) {
616
405
  throw new Error("The client hasn't been started yet. Please call start() first.");
617
406
  }
618
- var buffer = zlib_1.deflateSync(Buffer.from(msg));
407
+ const buffer = (0, zlib_1.deflateSync)(Buffer.from(msg));
619
408
  // Based on this thread, 7500 bytes seems like a safe value
620
409
  // https://stackoverflow.com/questions/22819214/udp-message-too-long
621
- var byteLimit = 7500;
622
- var chunks = Math.ceil(buffer.byteLength / byteLimit);
410
+ const byteLimit = 7500;
411
+ const chunks = Math.ceil(buffer.byteLength / byteLimit);
623
412
  // Split the message into chunks if it becomes too large
624
- for (var i = 0; i < chunks; i++) {
625
- var chunk = Buffer.concat([
413
+ for (let i = 0; i < chunks; i++) {
414
+ const chunk = Buffer.concat([
626
415
  // Add a counter to the message, the last message is always 255
627
416
  Buffer.alloc(1, i + 1 === chunks ? 255 : i),
628
417
  buffer.slice(i * byteLimit, i * byteLimit + byteLimit),
629
418
  ]);
630
419
  this.client.send(chunk, 0, chunk.length, this.serverPort, "127.0.0.1");
631
420
  }
632
- };
633
- Ableton.prototype.isConnected = function () {
421
+ }
422
+ isConnected() {
634
423
  return this._isConnected;
635
- };
636
- return Ableton;
637
- }(events_1.EventEmitter));
424
+ }
425
+ }
638
426
  exports.Ableton = Ableton;
639
427
  var package_version_2 = require("./util/package-version");
640
428
  Object.defineProperty(exports, "getPackageVersion", { enumerable: true, get: function () { return package_version_2.getPackageVersion; } });