New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

ableton-js

Package Overview
Dependencies
Maintainers
1
Versions
118
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ableton-js - npm Package Compare versions

Comparing version 2.6.0 to 2.7.0

util/cache.d.ts

8

CHANGELOG.md

@@ -7,4 +7,12 @@ ### Changelog

#### [v2.7.0](https://github.com/leolabs/ableton.js/compare/v2.6.0...v2.7.0)
- :sparkles: Add automatic client-side caching of potentially large props [`68c3edd`](https://github.com/leolabs/ableton.js/commit/68c3edda00918c769afa7c88e54a2a566a0e1f23)
- :art: Clean up the code a bit [`c183cda`](https://github.com/leolabs/ableton.js/commit/c183cda10deb81e176992ed40624d3f57015a703)
- :memo: Add some docs for caching [`778da44`](https://github.com/leolabs/ableton.js/commit/778da44ab960cb5074b46e4783abf3d0c57d5a9f)
#### [v2.6.0](https://github.com/leolabs/ableton.js/compare/v2.5.4...v2.6.0)
> 1 November 2022
- :sparkles: Make IDs stable over the lifespan of Live [`0bfcf90`](https://github.com/leolabs/ableton.js/commit/0bfcf904b54d42377877a2e9fab0796986f6aa1a)

@@ -11,0 +19,0 @@ - :sparkles: Add create_track methods [`a7dd6bc`](https://github.com/leolabs/ableton.js/commit/a7dd6bc53e2f49fb133b2eea2a1fe6eb1ff0aac6)

25

index.d.ts
/// <reference types="node" />
import { EventEmitter } from "events";
import LruCache from "lru-cache";
import { Song } from "./ns/song";

@@ -7,2 +8,3 @@ import { Internal } from "./ns/internal";

import { Midi } from "./ns/midi";
import { Cache } from "./util/cache";
interface Command {

@@ -13,2 +15,4 @@ uuid: string;

name: string;
etag?: string;
cache?: boolean;
args?: {

@@ -36,6 +40,10 @@ [k: string]: any;

}
export interface AbletonOptions {
host?: string;
sendPort?: number;
listenPort?: number;
heartbeatInterval?: number;
cacheOptions?: LruCache.Options<string, any>;
}
export declare class Ableton extends EventEmitter implements ConnectionEventEmitter {
private host;
private sendPort;
private listenPort;
private client;

@@ -49,2 +57,6 @@ private msgMap;

private latency;
private host;
private sendPort;
private listenPort;
cache: Cache;
song: Song;

@@ -54,3 +66,3 @@ application: Application;

midi: Midi;
constructor(host?: string, sendPort?: number, listenPort?: number, heartbeatInterval?: number);
constructor(options?: AbletonOptions);
close(): void;

@@ -69,4 +81,5 @@ /**

*/
sendCommand(ns: string, nsid: string | undefined, name: string, args?: Record<string, any> | any[], timeout?: number): Promise<any>;
getProp(ns: string, nsid: string | undefined, prop: string): Promise<any>;
sendCommand(command: Omit<Command, "uuid">, timeout?: number): Promise<any>;
sendCachedCommand(command: Omit<Command, "uuid" | "cache">, timeout?: number): Promise<any>;
getProp(ns: string, nsid: string | undefined, prop: string, cache?: boolean): Promise<any>;
setProp(ns: string, nsid: string | undefined, prop: string, value: any): Promise<any>;

@@ -73,0 +86,0 @@ addPropListener(ns: string, nsid: string | undefined, prop: string, listener: (data: any) => any): Promise<() => Promise<boolean | undefined>>;

@@ -17,2 +17,13 @@ "use strict";

})();
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -54,2 +65,18 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __spreadArray = (this && this.__spreadArray) || function (to, from) {

@@ -70,2 +97,3 @@ for (var i = 0, il = from.length, j = to.length; i < il; i++, j++)

var zlib_1 = require("zlib");
var lru_cache_1 = __importDefault(require("lru-cache"));
var song_1 = require("./ns/song");

@@ -76,2 +104,3 @@ var internal_1 = require("./ns/internal");

var package_version_1 = require("./util/package-version");
var cache_1 = require("./util/cache");
var TimeoutError = /** @class */ (function (_super) {

@@ -90,11 +119,5 @@ __extends(TimeoutError, _super);

__extends(Ableton, _super);
function Ableton(host, sendPort, listenPort, heartbeatInterval) {
if (host === void 0) { host = "127.0.0.1"; }
if (sendPort === void 0) { sendPort = 39041; }
if (listenPort === void 0) { listenPort = 39031; }
if (heartbeatInterval === void 0) { heartbeatInterval = 2000; }
function Ableton(options) {
var _a, _b, _c, _d;
var _this = _super.call(this) || this;
_this.host = host;
_this.sendPort = sendPort;
_this.listenPort = listenPort;
_this.msgMap = new Map();

@@ -110,5 +133,9 @@ _this.eventListeners = new Map();

_this.midi = new midi_1.Midi(_this);
_this.host = (_a = options === null || options === void 0 ? void 0 : options.host) !== null && _a !== void 0 ? _a : "127.0.0.1";
_this.sendPort = (_b = options === null || options === void 0 ? void 0 : options.sendPort) !== null && _b !== void 0 ? _b : 39041;
_this.listenPort = (_c = options === null || options === void 0 ? void 0 : options.listenPort) !== null && _c !== void 0 ? _c : 39031;
_this.client = dgram_1.default.createSocket({ type: "udp4" });
_this.client.bind(_this.listenPort, host);
_this.client.bind(_this.listenPort, _this.host);
_this.client.addListener("message", _this.handleIncoming.bind(_this));
_this.cache = new lru_cache_1.default(__assign({ max: 500, ttl: 1000 * 60 * 10 }, options === null || options === void 0 ? void 0 : options.cacheOptions));
var heartbeat = function () { return __awaiter(_this, void 0, void 0, function () {

@@ -145,3 +172,3 @@ var e_1;

}); };
_this.heartbeatInterval = setInterval(heartbeat, heartbeatInterval);
_this.heartbeatInterval = setInterval(heartbeat, (_d = options === null || options === void 0 ? void 0 : options.heartbeatInterval) !== null && _d !== void 0 ? _d : 2000);
heartbeat();

@@ -235,3 +262,3 @@ _this.internal

*/
Ableton.prototype.sendCommand = function (ns, nsid, name, args, timeout) {
Ableton.prototype.sendCommand = function (command, timeout) {
if (timeout === void 0) { timeout = 2000; }

@@ -243,13 +270,9 @@ return __awaiter(this, void 0, void 0, function () {

var msgId = uuid_1.v4();
var payload = {
uuid: msgId,
ns: ns,
nsid: nsid,
name: name,
args: args,
};
var payload = __assign({ uuid: msgId }, command);
var msg = JSON.stringify(payload);
var timeoutId = setTimeout(function () {
var arg = JSON.stringify(args);
var cls = nsid ? ns + "(" + nsid + ")" : ns;
var arg = JSON.stringify(command.args);
var cls = command.nsid
? command.ns + "(" + command.nsid + ")"
: command.ns;
rej(new TimeoutError([

@@ -264,6 +287,6 @@ "The command " + cls + "." + name + "(" + arg + ") timed out after " + timeout + " ms.",

_this.msgMap.set(msgId, {
res: function (data) {
res: function (result) {
_this.setPing(Date.now() - currentTimestamp);
clearTimeout(timeoutId);
res(data);
res(result);
},

@@ -280,6 +303,48 @@ rej: rej,

};
Ableton.prototype.getProp = function (ns, nsid, prop) {
Ableton.prototype.sendCachedCommand = function (command, timeout) {
var _a, _b;
return __awaiter(this, void 0, void 0, function () {
var args, cacheKey, cached, result;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
args = (_b = (_a = command.args) === null || _a === void 0 ? void 0 : _a.prop) !== null && _b !== void 0 ? _b : JSON.stringify(command.args);
cacheKey = [command.ns, command.nsid, args].filter(Boolean).join("/");
cached = this.cache.get(cacheKey);
return [4 /*yield*/, this.sendCommand(__assign(__assign({}, command), { etag: cached === null || cached === void 0 ? void 0 : cached.etag, cache: true }), timeout)];
case 1:
result = _c.sent();
if (cache_1.isCached(result)) {
if (!cached) {
throw new Error("Tried to get an object that isn't cached.");
}
else {
console.log("Using cached entry:", { cached: cached });
return [2 /*return*/, cached.data];
}
}
else {
if (result.etag) {
console.log("Setting cached entry:", { cacheKey: cacheKey, result: result });
this.cache.set(cacheKey, result);
}
return [2 /*return*/, result.data];
}
return [2 /*return*/];
}
});
});
};
Ableton.prototype.getProp = function (ns, nsid, prop, cache) {
return __awaiter(this, void 0, void 0, function () {
var params;
return __generator(this, function (_a) {
return [2 /*return*/, this.sendCommand(ns, nsid, "get_prop", { prop: prop })];
params = { ns: ns, nsid: nsid, name: "get_prop", args: { prop: prop } };
if (cache) {
return [2 /*return*/, this.sendCachedCommand(params)];
}
else {
return [2 /*return*/, this.sendCommand(params)];
}
return [2 /*return*/];
});

@@ -291,3 +356,8 @@ });

return __generator(this, function (_a) {
return [2 /*return*/, this.sendCommand(ns, nsid, "set_prop", { prop: prop, value: value })];
return [2 /*return*/, this.sendCommand({
ns: ns,
nsid: nsid,
name: "set_prop",
args: { prop: prop, value: value },
})];
});

@@ -304,6 +374,7 @@ });

eventId = uuid_1.v4();
return [4 /*yield*/, this.sendCommand(ns, nsid, "add_listener", {
prop: prop,
return [4 /*yield*/, this.sendCommand({
ns: ns,
nsid: nsid,
eventId: eventId,
name: "add_listener",
args: { prop: prop, nsid: nsid, eventId: eventId },
})];

@@ -316,3 +387,3 @@ case 1:

else {
this.eventListeners.set(result, __spreadArray(__spreadArray([], this.eventListeners.get(result)), [
this.eventListeners.set(result, __spreadArray(__spreadArray([], __read(this.eventListeners.get(result))), [
listener,

@@ -342,3 +413,8 @@ ]));

this.eventListeners.delete(eventId);
return [4 /*yield*/, this.sendCommand(ns, nsid, "remove_listener", { prop: prop, nsid: nsid })];
return [4 /*yield*/, this.sendCommand({
ns: ns,
nsid: nsid,
name: "remove_listener",
args: { prop: prop, nsid: nsid },
})];
case 1:

@@ -345,0 +421,0 @@ _a.sent();

@@ -40,2 +40,5 @@ "use strict";

};
_this.cachedProps = {
clip: true,
};
return _this;

@@ -42,0 +45,0 @@ }

@@ -34,4 +34,7 @@ "use strict";

_this.transformers = {
parameters: function (ps) { return ps.map(function (p) { return new device_parameter_1.DeviceParameter(_this.ableton, p); }); },
parameters: function (ps) { return ps.map(function (p) { return new device_parameter_1.DeviceParameter(ableton, p); }); },
};
_this.cachedProps = {
parameters: true,
};
return _this;

@@ -38,0 +41,0 @@ }

@@ -6,6 +6,9 @@ import { Ableton } from "..";

protected nsid?: string | undefined;
constructor(ableton: Ableton, ns: string, nsid?: string | undefined);
protected transformers: Partial<{
[T in Extract<keyof GP, keyof TP>]: (val: GP[T]) => TP[T];
}>;
protected cachedProps: Partial<{
[T in keyof GP]: boolean;
}>;
constructor(ableton: Ableton, ns: string, nsid?: string | undefined);
get<T extends keyof GP>(prop: T): Promise<T extends keyof TP ? TP[T] : GP[T]>;

@@ -20,3 +23,10 @@ set<T extends keyof SP>(prop: T, value: SP[T]): Promise<null>;

[k: string]: any;
}, etag?: string, timeout?: number): Promise<any>;
/**
* Sends a raw function invocation to Ableton and expects the
* result to be a CacheResponse with `data` and an `etag`.
*/
protected sendCachedCommand(name: string, args?: {
[k: string]: any;
}, timeout?: number): Promise<any>;
}

@@ -46,9 +46,12 @@ "use strict";

this.transformers = {};
this.cachedProps = {};
}
Namespace.prototype.get = function (prop) {
return __awaiter(this, void 0, void 0, function () {
var res, transformer;
var cache, res, transformer;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.ableton.getProp(this.ns, this.nsid, String(prop))];
case 0:
cache = !!this.cachedProps[prop];
return [4 /*yield*/, this.ableton.getProp(this.ns, this.nsid, String(prop), cache)];
case 1:

@@ -95,11 +98,22 @@ res = _a.sent();

*/
Namespace.prototype.sendCommand = function (name, args, timeout) {
Namespace.prototype.sendCommand = function (name, args, etag, timeout) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.ableton.sendCommand(this.ns, this.nsid, name, args, timeout)];
return [2 /*return*/, this.ableton.sendCommand({ ns: this.ns, nsid: this.nsid, name: name, args: args, etag: etag }, timeout)];
});
});
};
/**
* Sends a raw function invocation to Ableton and expects the
* result to be a CacheResponse with `data` and an `etag`.
*/
Namespace.prototype.sendCachedCommand = function (name, args, timeout) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.ableton.sendCachedCommand({ ns: this.ns, nsid: this.nsid, name: name, args: args }, timeout)];
});
});
};
return Namespace;
}());
exports.Namespace = Namespace;

@@ -1,3 +0,3 @@

import { Namespace } from "./index";
import { Ableton } from "../index";
import { Ableton } from "..";
import { Namespace } from ".";
export declare enum MidiCommand {

@@ -4,0 +4,0 @@ NoteOn = 128,

@@ -19,3 +19,3 @@ "use strict";

exports.Midi = exports.MidiMessage = exports.MidiCommand = void 0;
var index_1 = require("./index");
var _1 = require(".");
var MidiCommand;

@@ -74,7 +74,8 @@ (function (MidiCommand) {

controller: this.parameter1,
value: this.parameter2
value: this.parameter2,
};
};
MidiMessage.prototype.toNote = function () {
if (this.command !== MidiCommand.NoteOn && this.command !== MidiCommand.NoteOff) {
if (this.command !== MidiCommand.NoteOn &&
this.command !== MidiCommand.NoteOff) {
throw "not a midi note message";

@@ -85,3 +86,3 @@ }

key: this.parameter1,
velocity: this.parameter2
velocity: this.parameter2,
};

@@ -97,3 +98,3 @@ };

_this.transformers = {
midi: function (msg) { return new MidiMessage(msg); }
midi: function (msg) { return new MidiMessage(msg); },
};

@@ -103,3 +104,3 @@ return _this;

return Midi;
}(index_1.Namespace));
}(_1.Namespace));
exports.Midi = Midi;

@@ -31,2 +31,5 @@ "use strict";

};
_this.cachedProps = {
clip_slots: true,
};
return _this;

@@ -33,0 +36,0 @@ }

@@ -65,7 +65,13 @@ "use strict";

_this.transformers = {
selected_parameter: function (param) { return new device_parameter_1.DeviceParameter(_this.ableton, param); },
selected_track: function (track) { return new track_1.Track(_this.ableton, track); },
selected_scene: function (scene) { return new scene_1.Scene(_this.ableton, scene); },
highlighted_clip_slot: function (clipSlot) { return new clip_slot_1.ClipSlot(_this.ableton, clipSlot); },
selected_parameter: function (param) { return new device_parameter_1.DeviceParameter(ableton, param); },
selected_track: function (track) { return new track_1.Track(ableton, track); },
selected_scene: function (scene) { return new scene_1.Scene(ableton, scene); },
highlighted_clip_slot: function (slot) { return new clip_slot_1.ClipSlot(ableton, slot); },
};
_this.cachedProps = {
selected_parameter: true,
selected_track: true,
selected_scene: true,
highlighted_clip_slot: true,
};
return _this;

@@ -76,4 +82,6 @@ }

return __generator(this, function (_a) {
return [2 /*return*/, this.ableton.sendCommand(this.ns, undefined, "select_device", {
device_id: device.raw.id,
return [2 /*return*/, this.ableton.sendCommand({
ns: this.ns,
name: "select_device",
args: { device_id: device.raw.id },
})];

@@ -80,0 +88,0 @@ });

@@ -104,9 +104,17 @@ "use strict";

_this.transformers = {
cue_points: function (points) { return points.map(function (c) { return new cue_point_1.CuePoint(_this.ableton, c); }); },
master_track: function (track) { return new track_1.Track(_this.ableton, track); },
return_tracks: function (tracks) { return tracks.map(function (t) { return new track_1.Track(_this.ableton, t); }); },
tracks: function (tracks) { return tracks.map(function (t) { return new track_1.Track(_this.ableton, t); }); },
visible_tracks: function (tracks) { return tracks.map(function (t) { return new track_1.Track(_this.ableton, t); }); },
scenes: function (scenes) { return scenes.map(function (s) { return new scene_1.Scene(_this.ableton, s); }); },
cue_points: function (points) { return points.map(function (c) { return new cue_point_1.CuePoint(ableton, c); }); },
master_track: function (track) { return new track_1.Track(ableton, track); },
return_tracks: function (tracks) { return tracks.map(function (t) { return new track_1.Track(ableton, t); }); },
tracks: function (tracks) { return tracks.map(function (t) { return new track_1.Track(ableton, t); }); },
visible_tracks: function (tracks) { return tracks.map(function (t) { return new track_1.Track(ableton, t); }); },
scenes: function (scenes) { return scenes.map(function (s) { return new scene_1.Scene(ableton, s); }); },
};
_this.cachedProps = {
cue_points: true,
master_track: true,
return_tracks: true,
tracks: true,
visible_tracks: true,
scenes: true,
};
return _this;

@@ -201,3 +209,3 @@ }

return __generator(this, function (_a) {
return [2 /*return*/, this.sendCommand("get_data", { key: key })];
return [2 /*return*/, this.sendCachedCommand("get_data", { key: key })];
});

@@ -204,0 +212,0 @@ });

@@ -39,2 +39,7 @@ "use strict";

};
_this.cachedProps = {
arrangement_clips: true,
devices: true,
clip_slots: true,
};
return _this;

@@ -41,0 +46,0 @@ }

{
"name": "ableton-js",
"version": "2.6.0",
"version": "2.7.0",
"description": "Control Ableton Live from Node",

@@ -49,2 +49,3 @@ "main": "index.js",

"dependencies": {
"lru-cache": "^7.14.0",
"semver": "^7.3.5",

@@ -51,0 +52,0 @@ "uuid": "^8.3.2"

@@ -61,3 +61,3 @@ # Ableton.js

console.log(tempo);
// Set the tempo

@@ -107,2 +107,13 @@ await ableton.song.set("tempo", 85);

### Caching
Certain props are cached on the client to reduce the bandwidth over UDP. To do
this, the Ableton plugin generates an MD5 hash of the prop, called ETag, and
sends it to the client along with the data.
The client stores both the ETag and the data in an LRU cache and sends the
latest stored ETag to the plugin the next time the same prop is requested. If
the data still matches the ETag, the plugin responds with a placeholder object
and the client returns the cached data.
### Commands

@@ -118,3 +129,5 @@

"name": "get_prop", // Command name
"args": { "prop": "current_song_time" } // Command arguments
"args": { "prop": "current_song_time" }, // Command arguments
"etag": "4e0794e44c7eb58bdbbbf7268e8237b4", // MD5 hash of the data if it might be cached locally
"cache": true // If this is true, the plugin will calculate an etag and return a placeholder if it matches the provided one
}

@@ -129,6 +142,26 @@ ```

"event": "result", // This can be 'result' or 'error'
"uuid": "a20f25a0-83e2-11e9-bbe1-bd3a580ef903"
"uuid": "a20f25a0-83e2-11e9-bbe1-bd3a580ef903" // The same UUID that was used to send the command
}
```
If you're getting a cached prop, the JSON object could look like this:
```js
{
"data": { "data": 0.0, "etag": "4e0794e44c7eb58bdbbbf7268e8237b4" },
"event": "result", // This can be 'result' or 'error'
"uuid": "a20f25a0-83e2-11e9-bbe1-bd3a580ef903" // The same UUID that was used to send the command
}
```
Or, if the data hasn't changed, it looks like this:
```js
{
"data": { "__cached": true },
"event": "result", // This can be 'result' or 'error'
"uuid": "a20f25a0-83e2-11e9-bbe1-bd3a580ef903" // The same UUID that was used to send the command
}
```
### Events

@@ -135,0 +168,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc