--target bundler generates static WASM ESM imports that only work through a bundler (vite-plugin-wasm). --target web generates fetch-based WASM loading via import.meta.url which browsers handle natively. - Change wasm-pack build target from bundler to web - Call wasmModule.default() (init) after import in maybeLoadWasm - Restore rollupOptions.external so WASM stays a separate fetch - Removes the need for vite-plugin-wasm in any consumer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1395 lines
40 KiB
JavaScript
1395 lines
40 KiB
JavaScript
function getDefaultExportFromCjs (x) {
|
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
}
|
|
|
|
var eventemitter3 = {exports: {}};
|
|
|
|
var hasRequiredEventemitter3;
|
|
|
|
function requireEventemitter3 () {
|
|
if (hasRequiredEventemitter3) return eventemitter3.exports;
|
|
hasRequiredEventemitter3 = 1;
|
|
(function (module) {
|
|
|
|
var has = Object.prototype.hasOwnProperty
|
|
, prefix = '~';
|
|
|
|
/**
|
|
* Constructor to create a storage for our `EE` objects.
|
|
* An `Events` instance is a plain object whose properties are event names.
|
|
*
|
|
* @constructor
|
|
* @private
|
|
*/
|
|
function Events() {}
|
|
|
|
//
|
|
// We try to not inherit from `Object.prototype`. In some engines creating an
|
|
// instance in this way is faster than calling `Object.create(null)` directly.
|
|
// If `Object.create(null)` is not supported we prefix the event names with a
|
|
// character to make sure that the built-in object properties are not
|
|
// overridden or used as an attack vector.
|
|
//
|
|
if (Object.create) {
|
|
Events.prototype = Object.create(null);
|
|
|
|
//
|
|
// This hack is needed because the `__proto__` property is still inherited in
|
|
// some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
|
|
//
|
|
if (!new Events().__proto__) prefix = false;
|
|
}
|
|
|
|
/**
|
|
* Representation of a single event listener.
|
|
*
|
|
* @param {Function} fn The listener function.
|
|
* @param {*} context The context to invoke the listener with.
|
|
* @param {Boolean} [once=false] Specify if the listener is a one-time listener.
|
|
* @constructor
|
|
* @private
|
|
*/
|
|
function EE(fn, context, once) {
|
|
this.fn = fn;
|
|
this.context = context;
|
|
this.once = once || false;
|
|
}
|
|
|
|
/**
|
|
* Add a listener for a given event.
|
|
*
|
|
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
|
|
* @param {(String|Symbol)} event The event name.
|
|
* @param {Function} fn The listener function.
|
|
* @param {*} context The context to invoke the listener with.
|
|
* @param {Boolean} once Specify if the listener is a one-time listener.
|
|
* @returns {EventEmitter}
|
|
* @private
|
|
*/
|
|
function addListener(emitter, event, fn, context, once) {
|
|
if (typeof fn !== 'function') {
|
|
throw new TypeError('The listener must be a function');
|
|
}
|
|
|
|
var listener = new EE(fn, context || emitter, once)
|
|
, evt = prefix ? prefix + event : event;
|
|
|
|
if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++;
|
|
else if (!emitter._events[evt].fn) emitter._events[evt].push(listener);
|
|
else emitter._events[evt] = [emitter._events[evt], listener];
|
|
|
|
return emitter;
|
|
}
|
|
|
|
/**
|
|
* Clear event by name.
|
|
*
|
|
* @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
|
|
* @param {(String|Symbol)} evt The Event name.
|
|
* @private
|
|
*/
|
|
function clearEvent(emitter, evt) {
|
|
if (--emitter._eventsCount === 0) emitter._events = new Events();
|
|
else delete emitter._events[evt];
|
|
}
|
|
|
|
/**
|
|
* Minimal `EventEmitter` interface that is molded against the Node.js
|
|
* `EventEmitter` interface.
|
|
*
|
|
* @constructor
|
|
* @public
|
|
*/
|
|
function EventEmitter() {
|
|
this._events = new Events();
|
|
this._eventsCount = 0;
|
|
}
|
|
|
|
/**
|
|
* Return an array listing the events for which the emitter has registered
|
|
* listeners.
|
|
*
|
|
* @returns {Array}
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.eventNames = function eventNames() {
|
|
var names = []
|
|
, events
|
|
, name;
|
|
|
|
if (this._eventsCount === 0) return names;
|
|
|
|
for (name in (events = this._events)) {
|
|
if (has.call(events, name)) names.push(prefix ? name.slice(1) : name);
|
|
}
|
|
|
|
if (Object.getOwnPropertySymbols) {
|
|
return names.concat(Object.getOwnPropertySymbols(events));
|
|
}
|
|
|
|
return names;
|
|
};
|
|
|
|
/**
|
|
* Return the listeners registered for a given event.
|
|
*
|
|
* @param {(String|Symbol)} event The event name.
|
|
* @returns {Array} The registered listeners.
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.listeners = function listeners(event) {
|
|
var evt = prefix ? prefix + event : event
|
|
, handlers = this._events[evt];
|
|
|
|
if (!handlers) return [];
|
|
if (handlers.fn) return [handlers.fn];
|
|
|
|
for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
|
|
ee[i] = handlers[i].fn;
|
|
}
|
|
|
|
return ee;
|
|
};
|
|
|
|
/**
|
|
* Return the number of listeners listening to a given event.
|
|
*
|
|
* @param {(String|Symbol)} event The event name.
|
|
* @returns {Number} The number of listeners.
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.listenerCount = function listenerCount(event) {
|
|
var evt = prefix ? prefix + event : event
|
|
, listeners = this._events[evt];
|
|
|
|
if (!listeners) return 0;
|
|
if (listeners.fn) return 1;
|
|
return listeners.length;
|
|
};
|
|
|
|
/**
|
|
* Calls each of the listeners registered for a given event.
|
|
*
|
|
* @param {(String|Symbol)} event The event name.
|
|
* @returns {Boolean} `true` if the event had listeners, else `false`.
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
|
|
var evt = prefix ? prefix + event : event;
|
|
|
|
if (!this._events[evt]) return false;
|
|
|
|
var listeners = this._events[evt]
|
|
, len = arguments.length
|
|
, args
|
|
, i;
|
|
|
|
if (listeners.fn) {
|
|
if (listeners.once) this.removeListener(event, listeners.fn, undefined, true);
|
|
|
|
switch (len) {
|
|
case 1: return listeners.fn.call(listeners.context), true;
|
|
case 2: return listeners.fn.call(listeners.context, a1), true;
|
|
case 3: return listeners.fn.call(listeners.context, a1, a2), true;
|
|
case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true;
|
|
case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true;
|
|
case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true;
|
|
}
|
|
|
|
for (i = 1, args = new Array(len -1); i < len; i++) {
|
|
args[i - 1] = arguments[i];
|
|
}
|
|
|
|
listeners.fn.apply(listeners.context, args);
|
|
} else {
|
|
var length = listeners.length
|
|
, j;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true);
|
|
|
|
switch (len) {
|
|
case 1: listeners[i].fn.call(listeners[i].context); break;
|
|
case 2: listeners[i].fn.call(listeners[i].context, a1); break;
|
|
case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break;
|
|
case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break;
|
|
default:
|
|
if (!args) for (j = 1, args = new Array(len -1); j < len; j++) {
|
|
args[j - 1] = arguments[j];
|
|
}
|
|
|
|
listeners[i].fn.apply(listeners[i].context, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Add a listener for a given event.
|
|
*
|
|
* @param {(String|Symbol)} event The event name.
|
|
* @param {Function} fn The listener function.
|
|
* @param {*} [context=this] The context to invoke the listener with.
|
|
* @returns {EventEmitter} `this`.
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.on = function on(event, fn, context) {
|
|
return addListener(this, event, fn, context, false);
|
|
};
|
|
|
|
/**
|
|
* Add a one-time listener for a given event.
|
|
*
|
|
* @param {(String|Symbol)} event The event name.
|
|
* @param {Function} fn The listener function.
|
|
* @param {*} [context=this] The context to invoke the listener with.
|
|
* @returns {EventEmitter} `this`.
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.once = function once(event, fn, context) {
|
|
return addListener(this, event, fn, context, true);
|
|
};
|
|
|
|
/**
|
|
* Remove the listeners of a given event.
|
|
*
|
|
* @param {(String|Symbol)} event The event name.
|
|
* @param {Function} fn Only remove the listeners that match this function.
|
|
* @param {*} context Only remove the listeners that have this context.
|
|
* @param {Boolean} once Only remove one-time listeners.
|
|
* @returns {EventEmitter} `this`.
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) {
|
|
var evt = prefix ? prefix + event : event;
|
|
|
|
if (!this._events[evt]) return this;
|
|
if (!fn) {
|
|
clearEvent(this, evt);
|
|
return this;
|
|
}
|
|
|
|
var listeners = this._events[evt];
|
|
|
|
if (listeners.fn) {
|
|
if (
|
|
listeners.fn === fn &&
|
|
(!once || listeners.once) &&
|
|
(!context || listeners.context === context)
|
|
) {
|
|
clearEvent(this, evt);
|
|
}
|
|
} else {
|
|
for (var i = 0, events = [], length = listeners.length; i < length; i++) {
|
|
if (
|
|
listeners[i].fn !== fn ||
|
|
(once && !listeners[i].once) ||
|
|
(context && listeners[i].context !== context)
|
|
) {
|
|
events.push(listeners[i]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reset the array, or remove it completely if we have no more listeners.
|
|
//
|
|
if (events.length) this._events[evt] = events.length === 1 ? events[0] : events;
|
|
else clearEvent(this, evt);
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
/**
|
|
* Remove all listeners, or those of the specified event.
|
|
*
|
|
* @param {(String|Symbol)} [event] The event name.
|
|
* @returns {EventEmitter} `this`.
|
|
* @public
|
|
*/
|
|
EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
|
|
var evt;
|
|
|
|
if (event) {
|
|
evt = prefix ? prefix + event : event;
|
|
if (this._events[evt]) clearEvent(this, evt);
|
|
} else {
|
|
this._events = new Events();
|
|
this._eventsCount = 0;
|
|
}
|
|
|
|
return this;
|
|
};
|
|
|
|
//
|
|
// Alias methods names because people roll like that.
|
|
//
|
|
EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
|
|
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
|
|
|
|
//
|
|
// Expose the prefix.
|
|
//
|
|
EventEmitter.prefixed = prefix;
|
|
|
|
//
|
|
// Allow `EventEmitter` to be imported as module namespace.
|
|
//
|
|
EventEmitter.EventEmitter = EventEmitter;
|
|
|
|
//
|
|
// Expose the module.
|
|
//
|
|
{
|
|
module.exports = EventEmitter;
|
|
}
|
|
} (eventemitter3));
|
|
return eventemitter3.exports;
|
|
}
|
|
|
|
var eventemitter3Exports = requireEventemitter3();
|
|
const EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
|
|
|
|
var ButtplugLogLevel = /* @__PURE__ */ ((ButtplugLogLevel2) => {
|
|
ButtplugLogLevel2[ButtplugLogLevel2["Off"] = 0] = "Off";
|
|
ButtplugLogLevel2[ButtplugLogLevel2["Error"] = 1] = "Error";
|
|
ButtplugLogLevel2[ButtplugLogLevel2["Warn"] = 2] = "Warn";
|
|
ButtplugLogLevel2[ButtplugLogLevel2["Info"] = 3] = "Info";
|
|
ButtplugLogLevel2[ButtplugLogLevel2["Debug"] = 4] = "Debug";
|
|
ButtplugLogLevel2[ButtplugLogLevel2["Trace"] = 5] = "Trace";
|
|
return ButtplugLogLevel2;
|
|
})(ButtplugLogLevel || {});
|
|
class LogMessage {
|
|
/** Timestamp for the log message */
|
|
timestamp;
|
|
/** Log Message */
|
|
logMessage;
|
|
/** Log Level */
|
|
logLevel;
|
|
/**
|
|
* @param logMessage Log message.
|
|
* @param logLevel: Log severity level.
|
|
*/
|
|
constructor(logMessage, logLevel) {
|
|
const a = /* @__PURE__ */ new Date();
|
|
const hour = a.getHours();
|
|
const min = a.getMinutes();
|
|
const sec = a.getSeconds();
|
|
this.timestamp = `${hour}:${min}:${sec}`;
|
|
this.logMessage = logMessage;
|
|
this.logLevel = logLevel;
|
|
}
|
|
/**
|
|
* Returns the log message.
|
|
*/
|
|
get Message() {
|
|
return this.logMessage;
|
|
}
|
|
/**
|
|
* Returns the log message level.
|
|
*/
|
|
get LogLevel() {
|
|
return this.logLevel;
|
|
}
|
|
/**
|
|
* Returns the log message timestamp.
|
|
*/
|
|
get Timestamp() {
|
|
return this.timestamp;
|
|
}
|
|
/**
|
|
* Returns a formatted string with timestamp, level, and message.
|
|
*/
|
|
get FormattedMessage() {
|
|
return `${ButtplugLogLevel[this.logLevel]} : ${this.timestamp} : ${this.logMessage}`;
|
|
}
|
|
}
|
|
class ButtplugLogger extends EventEmitter {
|
|
/** Singleton instance for the logger */
|
|
static sLogger = void 0;
|
|
/** Sets maximum log level to log to console */
|
|
maximumConsoleLogLevel = 0 /* Off */;
|
|
/** Sets maximum log level for all log messages */
|
|
maximumEventLogLevel = 0 /* Off */;
|
|
/**
|
|
* Returns the stored static instance of the logger, creating one if it
|
|
* doesn't currently exist.
|
|
*/
|
|
static get Logger() {
|
|
if (ButtplugLogger.sLogger === void 0) {
|
|
ButtplugLogger.sLogger = new ButtplugLogger();
|
|
}
|
|
return this.sLogger;
|
|
}
|
|
/**
|
|
* Constructor. Can only be called internally since we regulate ButtplugLogger
|
|
* ownership.
|
|
*/
|
|
constructor() {
|
|
super();
|
|
}
|
|
/**
|
|
* Set the maximum log level to output to console.
|
|
*/
|
|
get MaximumConsoleLogLevel() {
|
|
return this.maximumConsoleLogLevel;
|
|
}
|
|
/**
|
|
* Get the maximum log level to output to console.
|
|
*/
|
|
set MaximumConsoleLogLevel(buttplugLogLevel) {
|
|
this.maximumConsoleLogLevel = buttplugLogLevel;
|
|
}
|
|
/**
|
|
* Set the global maximum log level
|
|
*/
|
|
get MaximumEventLogLevel() {
|
|
return this.maximumEventLogLevel;
|
|
}
|
|
/**
|
|
* Get the global maximum log level
|
|
*/
|
|
set MaximumEventLogLevel(logLevel) {
|
|
this.maximumEventLogLevel = logLevel;
|
|
}
|
|
/**
|
|
* Log new message at Error level.
|
|
*/
|
|
Error(msg) {
|
|
this.AddLogMessage(msg, 1 /* Error */);
|
|
}
|
|
/**
|
|
* Log new message at Warn level.
|
|
*/
|
|
Warn(msg) {
|
|
this.AddLogMessage(msg, 2 /* Warn */);
|
|
}
|
|
/**
|
|
* Log new message at Info level.
|
|
*/
|
|
Info(msg) {
|
|
this.AddLogMessage(msg, 3 /* Info */);
|
|
}
|
|
/**
|
|
* Log new message at Debug level.
|
|
*/
|
|
Debug(msg) {
|
|
this.AddLogMessage(msg, 4 /* Debug */);
|
|
}
|
|
/**
|
|
* Log new message at Trace level.
|
|
*/
|
|
Trace(msg) {
|
|
this.AddLogMessage(msg, 5 /* Trace */);
|
|
}
|
|
/**
|
|
* Checks to see if message should be logged, and if so, adds message to the
|
|
* log buffer. May also print message and emit event.
|
|
*/
|
|
AddLogMessage(msg, level) {
|
|
if (level > this.maximumEventLogLevel && level > this.maximumConsoleLogLevel) {
|
|
return;
|
|
}
|
|
const logMsg = new LogMessage(msg, level);
|
|
if (level <= this.maximumConsoleLogLevel) {
|
|
console.log(logMsg.FormattedMessage);
|
|
}
|
|
if (level <= this.maximumEventLogLevel) {
|
|
this.emit("log", logMsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
class ButtplugError extends Error {
|
|
get ErrorClass() {
|
|
return this.errorClass;
|
|
}
|
|
get InnerError() {
|
|
return this.innerError;
|
|
}
|
|
get Id() {
|
|
return this.messageId;
|
|
}
|
|
get ErrorMessage() {
|
|
return {
|
|
Error: {
|
|
Id: this.Id,
|
|
ErrorCode: this.ErrorClass,
|
|
ErrorMessage: this.message
|
|
}
|
|
};
|
|
}
|
|
static LogAndError(constructor, logger, message, id = SYSTEM_MESSAGE_ID) {
|
|
logger.Error(message);
|
|
return new constructor(message, id);
|
|
}
|
|
static FromError(error) {
|
|
switch (error.ErrorCode) {
|
|
case ErrorClass.ERROR_DEVICE:
|
|
return new ButtplugDeviceError(error.ErrorMessage, error.Id);
|
|
case ErrorClass.ERROR_INIT:
|
|
return new ButtplugInitError(error.ErrorMessage, error.Id);
|
|
case ErrorClass.ERROR_UNKNOWN:
|
|
return new ButtplugUnknownError(error.ErrorMessage, error.Id);
|
|
case ErrorClass.ERROR_PING:
|
|
return new ButtplugPingError(error.ErrorMessage, error.Id);
|
|
case ErrorClass.ERROR_MSG:
|
|
return new ButtplugMessageError(error.ErrorMessage, error.Id);
|
|
default:
|
|
throw new Error(`Message type ${error.ErrorCode} not handled`);
|
|
}
|
|
}
|
|
errorClass = ErrorClass.ERROR_UNKNOWN;
|
|
innerError;
|
|
messageId;
|
|
constructor(message, errorClass, id = SYSTEM_MESSAGE_ID, inner) {
|
|
super(message);
|
|
this.errorClass = errorClass;
|
|
this.innerError = inner;
|
|
this.messageId = id;
|
|
}
|
|
}
|
|
class ButtplugInitError extends ButtplugError {
|
|
constructor(message, id = SYSTEM_MESSAGE_ID) {
|
|
super(message, ErrorClass.ERROR_INIT, id);
|
|
}
|
|
}
|
|
class ButtplugDeviceError extends ButtplugError {
|
|
constructor(message, id = SYSTEM_MESSAGE_ID) {
|
|
super(message, ErrorClass.ERROR_DEVICE, id);
|
|
}
|
|
}
|
|
class ButtplugMessageError extends ButtplugError {
|
|
constructor(message, id = SYSTEM_MESSAGE_ID) {
|
|
super(message, ErrorClass.ERROR_MSG, id);
|
|
}
|
|
}
|
|
class ButtplugPingError extends ButtplugError {
|
|
constructor(message, id = SYSTEM_MESSAGE_ID) {
|
|
super(message, ErrorClass.ERROR_PING, id);
|
|
}
|
|
}
|
|
class ButtplugUnknownError extends ButtplugError {
|
|
constructor(message, id = SYSTEM_MESSAGE_ID) {
|
|
super(message, ErrorClass.ERROR_UNKNOWN, id);
|
|
}
|
|
}
|
|
|
|
const SYSTEM_MESSAGE_ID = 0;
|
|
const DEFAULT_MESSAGE_ID = 1;
|
|
const MAX_ID = 4294967295;
|
|
const MESSAGE_SPEC_VERSION_MAJOR = 4;
|
|
const MESSAGE_SPEC_VERSION_MINOR = 0;
|
|
function msgId(msg) {
|
|
for (const [_, entry] of Object.entries(msg)) {
|
|
if (entry != void 0) {
|
|
return entry.Id;
|
|
}
|
|
}
|
|
throw new ButtplugMessageError(`Message ${msg} does not have an ID.`);
|
|
}
|
|
function setMsgId(msg, id) {
|
|
for (const [_, entry] of Object.entries(msg)) {
|
|
if (entry != void 0) {
|
|
entry.Id = id;
|
|
return;
|
|
}
|
|
}
|
|
throw new ButtplugMessageError(`Message ${msg} does not have an ID.`);
|
|
}
|
|
var ErrorClass = /* @__PURE__ */ ((ErrorClass2) => {
|
|
ErrorClass2[ErrorClass2["ERROR_UNKNOWN"] = 0] = "ERROR_UNKNOWN";
|
|
ErrorClass2[ErrorClass2["ERROR_INIT"] = 1] = "ERROR_INIT";
|
|
ErrorClass2[ErrorClass2["ERROR_PING"] = 2] = "ERROR_PING";
|
|
ErrorClass2[ErrorClass2["ERROR_MSG"] = 3] = "ERROR_MSG";
|
|
ErrorClass2[ErrorClass2["ERROR_DEVICE"] = 4] = "ERROR_DEVICE";
|
|
return ErrorClass2;
|
|
})(ErrorClass || {});
|
|
var OutputType = /* @__PURE__ */ ((OutputType2) => {
|
|
OutputType2["Unknown"] = "Unknown";
|
|
OutputType2["Vibrate"] = "Vibrate";
|
|
OutputType2["Rotate"] = "Rotate";
|
|
OutputType2["Oscillate"] = "Oscillate";
|
|
OutputType2["Constrict"] = "Constrict";
|
|
OutputType2["Inflate"] = "Inflate";
|
|
OutputType2["Position"] = "Position";
|
|
OutputType2["HwPositionWithDuration"] = "HwPositionWithDuration";
|
|
OutputType2["Temperature"] = "Temperature";
|
|
OutputType2["Spray"] = "Spray";
|
|
OutputType2["Led"] = "Led";
|
|
return OutputType2;
|
|
})(OutputType || {});
|
|
var InputType = /* @__PURE__ */ ((InputType2) => {
|
|
InputType2["Unknown"] = "Unknown";
|
|
InputType2["Battery"] = "Battery";
|
|
InputType2["RSSI"] = "RSSI";
|
|
InputType2["Button"] = "Button";
|
|
InputType2["Pressure"] = "Pressure";
|
|
return InputType2;
|
|
})(InputType || {});
|
|
var InputCommandType = /* @__PURE__ */ ((InputCommandType2) => {
|
|
InputCommandType2["Read"] = "Read";
|
|
InputCommandType2["Subscribe"] = "Subscribe";
|
|
InputCommandType2["Unsubscribe"] = "Unsubscribe";
|
|
return InputCommandType2;
|
|
})(InputCommandType || {});
|
|
|
|
class ButtplugClientDeviceFeature {
|
|
constructor(_deviceIndex, _deviceName, _feature, _sendClosure) {
|
|
this._deviceIndex = _deviceIndex;
|
|
this._deviceName = _deviceName;
|
|
this._feature = _feature;
|
|
this._sendClosure = _sendClosure;
|
|
}
|
|
send = async (msg) => {
|
|
return await this._sendClosure(msg);
|
|
};
|
|
sendMsgExpectOk = async (msg) => {
|
|
const response = await this.send(msg);
|
|
if (response.Ok !== void 0) {
|
|
return;
|
|
} else if (response.Error !== void 0) {
|
|
throw ButtplugError.FromError(response);
|
|
} else {
|
|
throw new ButtplugMessageError("Expected Ok or Error, and didn't get either!");
|
|
}
|
|
};
|
|
isOutputValid(type) {
|
|
if (this._feature.Output !== void 0 && !Object.prototype.hasOwnProperty.call(this._feature.Output, type)) {
|
|
throw new ButtplugDeviceError(
|
|
`Feature index ${this._feature.FeatureIndex} does not support type ${type} for device ${this._deviceName}`
|
|
);
|
|
}
|
|
}
|
|
isInputValid(type) {
|
|
if (this._feature.Input !== void 0 && !Object.prototype.hasOwnProperty.call(this._feature.Input, type)) {
|
|
throw new ButtplugDeviceError(
|
|
`Feature index ${this._feature.FeatureIndex} does not support type ${type} for device ${this._deviceName}`
|
|
);
|
|
}
|
|
}
|
|
async sendOutputCmd(command) {
|
|
this.isOutputValid(command.outputType);
|
|
if (command.value === void 0) {
|
|
throw new ButtplugDeviceError(`${command.outputType} requires value defined`);
|
|
}
|
|
const type = command.outputType;
|
|
let duration = void 0;
|
|
if (type == OutputType.HwPositionWithDuration) {
|
|
if (command.duration === void 0) {
|
|
throw new ButtplugDeviceError("PositionWithDuration requires duration defined");
|
|
}
|
|
duration = command.duration;
|
|
}
|
|
let value;
|
|
const p = command.value;
|
|
if (p.percent === void 0) {
|
|
value = command.value.steps;
|
|
} else {
|
|
value = Math.ceil(this._feature.Output[type].Value[1] * p.percent);
|
|
}
|
|
const newCommand = { Value: value, Duration: duration };
|
|
const outCommand = {};
|
|
outCommand[type.toString()] = newCommand;
|
|
const cmd = {
|
|
OutputCmd: {
|
|
Id: 1,
|
|
DeviceIndex: this._deviceIndex,
|
|
FeatureIndex: this._feature.FeatureIndex,
|
|
Command: outCommand
|
|
}
|
|
};
|
|
await this.sendMsgExpectOk(cmd);
|
|
}
|
|
get featureDescriptor() {
|
|
return this._feature.FeatureDescription;
|
|
}
|
|
get featureIndex() {
|
|
return this._feature.FeatureIndex;
|
|
}
|
|
get outputTypes() {
|
|
if (this._feature.Output === void 0) return [];
|
|
return Object.keys(this._feature.Output);
|
|
}
|
|
get inputTypes() {
|
|
if (this._feature.Input === void 0) return [];
|
|
return Object.keys(this._feature.Input);
|
|
}
|
|
outputMaxValue(type) {
|
|
if (this._feature.Output === void 0 || this._feature.Output[type] === void 0) {
|
|
return 0;
|
|
}
|
|
const val = this._feature.Output[type].Value;
|
|
if (Array.isArray(val)) {
|
|
return val[val.length - 1];
|
|
}
|
|
return val;
|
|
}
|
|
hasOutput(type) {
|
|
if (this._feature.Output !== void 0) {
|
|
return Object.prototype.hasOwnProperty.call(this._feature.Output, type.toString());
|
|
}
|
|
return false;
|
|
}
|
|
hasInput(type) {
|
|
if (this._feature.Input !== void 0) {
|
|
return Object.prototype.hasOwnProperty.call(this._feature.Input, type.toString());
|
|
}
|
|
return false;
|
|
}
|
|
async runOutput(cmd) {
|
|
if (this._feature.Output !== void 0 && Object.prototype.hasOwnProperty.call(this._feature.Output, cmd.outputType.toString())) {
|
|
return this.sendOutputCmd(cmd);
|
|
}
|
|
throw new ButtplugDeviceError(`Output type ${cmd.outputType} not supported by feature.`);
|
|
}
|
|
async runInput(inputType, inputCommand) {
|
|
this.isInputValid(inputType);
|
|
const inputAttributes = this._feature.Input[inputType];
|
|
console.log(this._feature.Input);
|
|
if (inputCommand === InputCommandType.Unsubscribe && !inputAttributes.Command.includes(InputCommandType.Subscribe) && !inputAttributes.Command.includes(inputCommand)) {
|
|
throw new ButtplugDeviceError(`${inputType} does not support command ${inputCommand}`);
|
|
}
|
|
const cmd = {
|
|
InputCmd: {
|
|
Id: 1,
|
|
DeviceIndex: this._deviceIndex,
|
|
FeatureIndex: this._feature.FeatureIndex,
|
|
Type: inputType,
|
|
Command: inputCommand
|
|
}
|
|
};
|
|
if (inputCommand == InputCommandType.Read) {
|
|
const response = await this.send(cmd);
|
|
if (response.InputReading !== void 0) {
|
|
return response.InputReading;
|
|
} else if (response.Error !== void 0) {
|
|
throw ButtplugError.FromError(response);
|
|
} else {
|
|
throw new ButtplugMessageError("Expected InputReading or Error, and didn't get either!");
|
|
}
|
|
} else {
|
|
console.log(`Sending subscribe message: ${JSON.stringify(cmd)}`);
|
|
await this.sendMsgExpectOk(cmd);
|
|
console.log("Got back ok?");
|
|
}
|
|
}
|
|
}
|
|
|
|
class ButtplugClientDevice extends EventEmitter {
|
|
/**
|
|
* @param _index Index of the device, as created by the device manager.
|
|
* @param _name Name of the device.
|
|
* @param allowedMsgs Buttplug messages the device can receive.
|
|
*/
|
|
constructor(_deviceInfo, _sendClosure) {
|
|
super();
|
|
this._deviceInfo = _deviceInfo;
|
|
this._sendClosure = _sendClosure;
|
|
this._features = new Map(
|
|
Object.entries(_deviceInfo.DeviceFeatures).map(([index, v]) => [
|
|
parseInt(index),
|
|
new ButtplugClientDeviceFeature(
|
|
_deviceInfo.DeviceIndex,
|
|
_deviceInfo.DeviceName,
|
|
v,
|
|
_sendClosure
|
|
)
|
|
])
|
|
);
|
|
}
|
|
_features;
|
|
/**
|
|
* Return the name of the device.
|
|
*/
|
|
get name() {
|
|
return this._deviceInfo.DeviceName;
|
|
}
|
|
/**
|
|
* Return the user set name of the device.
|
|
*/
|
|
get displayName() {
|
|
return this._deviceInfo.DeviceDisplayName;
|
|
}
|
|
/**
|
|
* Return the index of the device.
|
|
*/
|
|
get index() {
|
|
return this._deviceInfo.DeviceIndex;
|
|
}
|
|
/**
|
|
* Return the index of the device.
|
|
*/
|
|
get messageTimingGap() {
|
|
return this._deviceInfo.DeviceMessageTimingGap;
|
|
}
|
|
get features() {
|
|
return this._features;
|
|
}
|
|
static fromMsg(msg, sendClosure) {
|
|
return new ButtplugClientDevice(msg, sendClosure);
|
|
}
|
|
async send(msg) {
|
|
return await this._sendClosure(msg);
|
|
}
|
|
sendMsgExpectOk = async (msg) => {
|
|
const response = await this.send(msg);
|
|
if (response.Ok !== void 0) {
|
|
return;
|
|
} else if (response.Error !== void 0) {
|
|
throw ButtplugError.FromError(response);
|
|
} else ;
|
|
};
|
|
isOutputValid(featureIndex, type) {
|
|
if (!Object.prototype.hasOwnProperty.call(
|
|
this._deviceInfo.DeviceFeatures,
|
|
featureIndex.toString()
|
|
)) {
|
|
throw new ButtplugDeviceError(
|
|
`Feature index ${featureIndex} does not exist for device ${this.name}`
|
|
);
|
|
}
|
|
if (this._deviceInfo.DeviceFeatures[featureIndex.toString()].Outputs !== void 0 && !Object.prototype.hasOwnProperty.call(
|
|
this._deviceInfo.DeviceFeatures[featureIndex.toString()].Outputs,
|
|
type
|
|
)) {
|
|
throw new ButtplugDeviceError(
|
|
`Feature index ${featureIndex} does not support type ${type} for device ${this.name}`
|
|
);
|
|
}
|
|
}
|
|
hasOutput(type) {
|
|
return this._features.values().filter((f) => f.hasOutput(type)).toArray().length > 0;
|
|
}
|
|
hasInput(type) {
|
|
return this._features.values().filter((f) => f.hasInput(type)).toArray().length > 0;
|
|
}
|
|
async runOutput(cmd) {
|
|
const p = [];
|
|
for (const f of this._features.values()) {
|
|
if (f.hasOutput(cmd.outputType)) {
|
|
p.push(f.runOutput(cmd));
|
|
}
|
|
}
|
|
if (p.length == 0) {
|
|
return Promise.reject(`No features with output type ${cmd.outputType}`);
|
|
}
|
|
await Promise.all(p);
|
|
}
|
|
async stop() {
|
|
await this.sendMsgExpectOk({
|
|
StopCmd: {
|
|
Id: 1,
|
|
DeviceIndex: this.index,
|
|
FeatureIndex: void 0,
|
|
Inputs: true,
|
|
Outputs: true
|
|
}
|
|
});
|
|
}
|
|
async battery() {
|
|
for (const f of this._features.values()) {
|
|
if (f.hasInput(InputType.Battery)) {
|
|
const response = await f.runInput(
|
|
InputType.Battery,
|
|
InputCommandType.Read
|
|
);
|
|
if (response === void 0) {
|
|
throw new ButtplugMessageError("Got incorrect message back.");
|
|
}
|
|
if (response.Reading[InputType.Battery] === void 0) {
|
|
throw new ButtplugMessageError("Got reading with no Battery info.");
|
|
}
|
|
return response.Reading[InputType.Battery].Value;
|
|
}
|
|
}
|
|
throw new ButtplugDeviceError(`No battery present on this device.`);
|
|
}
|
|
emitDisconnected() {
|
|
this.emit("deviceremoved");
|
|
}
|
|
}
|
|
|
|
class ButtplugMessageSorter {
|
|
constructor(_useCounter) {
|
|
this._useCounter = _useCounter;
|
|
}
|
|
_counter = 1;
|
|
_waitingMsgs = /* @__PURE__ */ new Map();
|
|
// One of the places we should actually return a promise, as we need to store
|
|
// them while waiting for them to return across the line.
|
|
// tslint:disable:promise-function-async
|
|
PrepareOutgoingMessage(msg) {
|
|
if (this._useCounter) {
|
|
setMsgId(msg, this._counter);
|
|
this._counter += 1;
|
|
}
|
|
let res;
|
|
let rej;
|
|
const msgPromise = new Promise((resolve, reject) => {
|
|
res = resolve;
|
|
rej = reject;
|
|
});
|
|
this._waitingMsgs.set(msgId(msg), [res, rej]);
|
|
return msgPromise;
|
|
}
|
|
ParseIncomingMessages(msgs) {
|
|
const noMatch = [];
|
|
for (const x of msgs) {
|
|
const id = msgId(x);
|
|
if (id !== SYSTEM_MESSAGE_ID && this._waitingMsgs.has(id)) {
|
|
const [res, rej] = this._waitingMsgs.get(id);
|
|
this._waitingMsgs.delete(id);
|
|
if (x.Error !== void 0) {
|
|
rej(ButtplugError.FromError(x.Error));
|
|
continue;
|
|
}
|
|
res(x);
|
|
continue;
|
|
} else {
|
|
noMatch.push(x);
|
|
}
|
|
}
|
|
return noMatch;
|
|
}
|
|
}
|
|
|
|
class ButtplugClientConnectorException extends ButtplugError {
|
|
constructor(message) {
|
|
super(message, ErrorClass.ERROR_UNKNOWN);
|
|
}
|
|
}
|
|
|
|
class ButtplugClient extends EventEmitter {
|
|
_pingTimer = null;
|
|
_connector = null;
|
|
_devices = /* @__PURE__ */ new Map();
|
|
_clientName;
|
|
_logger = ButtplugLogger.Logger;
|
|
_isScanning = false;
|
|
_sorter = new ButtplugMessageSorter(true);
|
|
constructor(clientName = "Generic Buttplug Client") {
|
|
super();
|
|
this._clientName = clientName;
|
|
this._logger.Debug(`ButtplugClient: Client ${clientName} created.`);
|
|
}
|
|
get connected() {
|
|
return this._connector !== null && this._connector.Connected;
|
|
}
|
|
get devices() {
|
|
this.checkConnector();
|
|
return this._devices;
|
|
}
|
|
get isScanning() {
|
|
return this._isScanning;
|
|
}
|
|
connect = async (connector) => {
|
|
this._logger.Info(`ButtplugClient: Connecting using ${connector.constructor.name}`);
|
|
await connector.connect();
|
|
this._connector = connector;
|
|
this._connector.addListener("message", this.parseMessages);
|
|
this._connector.addListener("disconnect", this.disconnectHandler);
|
|
await this.initializeConnection();
|
|
};
|
|
disconnect = async () => {
|
|
this._logger.Debug("ButtplugClient: Disconnect called");
|
|
this._devices.clear();
|
|
this.checkConnector();
|
|
await this.shutdownConnection();
|
|
await this._connector.disconnect();
|
|
};
|
|
startScanning = async () => {
|
|
this._logger.Debug("ButtplugClient: StartScanning called");
|
|
this._isScanning = true;
|
|
await this.sendMsgExpectOk({ StartScanning: { Id: 1 } });
|
|
};
|
|
stopScanning = async () => {
|
|
this._logger.Debug("ButtplugClient: StopScanning called");
|
|
this._isScanning = false;
|
|
await this.sendMsgExpectOk({ StopScanning: { Id: 1 } });
|
|
};
|
|
stopAllDevices = async () => {
|
|
this._logger.Debug("ButtplugClient: StopAllDevices");
|
|
await this.sendMsgExpectOk({
|
|
StopCmd: {
|
|
Id: 1,
|
|
DeviceIndex: void 0,
|
|
FeatureIndex: void 0,
|
|
Inputs: true,
|
|
Outputs: true
|
|
}
|
|
});
|
|
};
|
|
disconnectHandler = () => {
|
|
this._logger.Info("ButtplugClient: Disconnect event receieved.");
|
|
this.emit("disconnect");
|
|
};
|
|
parseMessages = (msgs) => {
|
|
const leftoverMsgs = this._sorter.ParseIncomingMessages(msgs);
|
|
for (const x of leftoverMsgs) {
|
|
if (x.DeviceList !== void 0) {
|
|
this.parseDeviceList(x.DeviceList);
|
|
break;
|
|
} else if (x.ScanningFinished !== void 0) {
|
|
this._isScanning = false;
|
|
this.emit("scanningfinished", x);
|
|
} else if (x.InputReading !== void 0) {
|
|
this.emit("inputreading", x);
|
|
} else {
|
|
console.log(`Unhandled message: ${x}`);
|
|
}
|
|
}
|
|
};
|
|
initializeConnection = async () => {
|
|
this.checkConnector();
|
|
const msg = await this.sendMessage({
|
|
RequestServerInfo: {
|
|
ClientName: this._clientName,
|
|
Id: 1,
|
|
ProtocolVersionMajor: MESSAGE_SPEC_VERSION_MAJOR,
|
|
ProtocolVersionMinor: MESSAGE_SPEC_VERSION_MINOR
|
|
}
|
|
});
|
|
if (msg.ServerInfo !== void 0) {
|
|
const serverinfo = msg;
|
|
this._logger.Info(`ButtplugClient: Connected to Server ${serverinfo.ServerName}`);
|
|
serverinfo.MaxPingTime;
|
|
await this.requestDeviceList();
|
|
return true;
|
|
} else if (msg.Error !== void 0) {
|
|
await this._connector.disconnect();
|
|
const err = msg.Error;
|
|
throw ButtplugError.LogAndError(
|
|
ButtplugInitError,
|
|
this._logger,
|
|
`Cannot connect to server. ${err.ErrorMessage}`
|
|
);
|
|
}
|
|
return false;
|
|
};
|
|
parseDeviceList = (list) => {
|
|
for (const [_, d] of Object.entries(list.Devices)) {
|
|
if (!this._devices.has(d.DeviceIndex)) {
|
|
const device = ButtplugClientDevice.fromMsg(d, this.sendMessageClosure);
|
|
this._logger.Debug(`ButtplugClient: Adding Device: ${device}`);
|
|
this._devices.set(d.DeviceIndex, device);
|
|
this.emit("deviceadded", device);
|
|
} else {
|
|
this._logger.Debug(`ButtplugClient: Device already added: ${d}`);
|
|
}
|
|
}
|
|
for (const [index, device] of this._devices.entries()) {
|
|
if (!Object.prototype.hasOwnProperty.call(list.Devices, index.toString())) {
|
|
this._devices.delete(index);
|
|
this.emit("deviceremoved", device);
|
|
}
|
|
}
|
|
};
|
|
requestDeviceList = async () => {
|
|
this.checkConnector();
|
|
this._logger.Debug("ButtplugClient: ReceiveDeviceList called");
|
|
const response = await this.sendMessage({
|
|
RequestDeviceList: { Id: 1 }
|
|
});
|
|
this.parseDeviceList(response.DeviceList);
|
|
};
|
|
shutdownConnection = async () => {
|
|
await this.stopAllDevices();
|
|
if (this._pingTimer !== null) {
|
|
clearInterval(this._pingTimer);
|
|
this._pingTimer = null;
|
|
}
|
|
};
|
|
async sendMessage(msg) {
|
|
this.checkConnector();
|
|
const p = this._sorter.PrepareOutgoingMessage(msg);
|
|
await this._connector.send(msg);
|
|
return await p;
|
|
}
|
|
checkConnector() {
|
|
if (!this.connected) {
|
|
throw new ButtplugClientConnectorException("ButtplugClient not connected");
|
|
}
|
|
}
|
|
sendMsgExpectOk = async (msg) => {
|
|
const response = await this.sendMessage(msg);
|
|
if (response.Ok !== void 0) {
|
|
return;
|
|
} else if (response.Error !== void 0) {
|
|
throw ButtplugError.FromError(response);
|
|
} else {
|
|
throw ButtplugError.LogAndError(
|
|
ButtplugMessageError,
|
|
this._logger,
|
|
`Message ${response} not handled by SendMsgExpectOk`
|
|
);
|
|
}
|
|
};
|
|
sendMessageClosure = async (msg) => {
|
|
return await this.sendMessage(msg);
|
|
};
|
|
}
|
|
|
|
class ButtplugBrowserWebsocketConnector extends EventEmitter {
|
|
constructor(_url) {
|
|
super();
|
|
this._url = _url;
|
|
}
|
|
_ws;
|
|
_websocketConstructor = null;
|
|
get Connected() {
|
|
return this._ws !== void 0;
|
|
}
|
|
connect = async () => {
|
|
return new Promise((resolve, reject) => {
|
|
const ws = new (this._websocketConstructor ?? WebSocket)(this._url);
|
|
const onErrorCallback = (event) => {
|
|
reject(event);
|
|
};
|
|
const onCloseCallback = (event) => reject(event.reason);
|
|
ws.addEventListener("open", async () => {
|
|
this._ws = ws;
|
|
try {
|
|
await this.initialize();
|
|
this._ws.addEventListener("message", (msg) => {
|
|
this.parseIncomingMessage(msg);
|
|
});
|
|
this._ws.removeEventListener("close", onCloseCallback);
|
|
this._ws.removeEventListener("error", onErrorCallback);
|
|
this._ws.addEventListener("close", this.disconnect);
|
|
resolve();
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
});
|
|
ws.addEventListener("error", onErrorCallback);
|
|
ws.addEventListener("close", onCloseCallback);
|
|
});
|
|
};
|
|
disconnect = async () => {
|
|
if (!this.Connected) {
|
|
return;
|
|
}
|
|
this._ws.close();
|
|
this._ws = void 0;
|
|
this.emit("disconnect");
|
|
};
|
|
sendMessage(msg) {
|
|
if (!this.Connected) {
|
|
throw new Error("ButtplugBrowserWebsocketConnector not connected");
|
|
}
|
|
this._ws.send("[" + JSON.stringify(msg) + "]");
|
|
}
|
|
initialize = async () => {
|
|
return Promise.resolve();
|
|
};
|
|
parseIncomingMessage(event) {
|
|
if (typeof event.data === "string") {
|
|
const msgs = JSON.parse(event.data);
|
|
this.emit("message", msgs);
|
|
} else if (event.data instanceof Blob) ;
|
|
}
|
|
onReaderLoad(event) {
|
|
const msgs = JSON.parse(event.target.result);
|
|
this.emit("message", msgs);
|
|
}
|
|
}
|
|
|
|
class ButtplugBrowserWebsocketClientConnector extends ButtplugBrowserWebsocketConnector {
|
|
send = (msg) => {
|
|
if (!this.Connected) {
|
|
throw new Error("ButtplugClient not connected");
|
|
}
|
|
this.sendMessage(msg);
|
|
};
|
|
}
|
|
|
|
var browser;
|
|
var hasRequiredBrowser;
|
|
|
|
function requireBrowser () {
|
|
if (hasRequiredBrowser) return browser;
|
|
hasRequiredBrowser = 1;
|
|
|
|
browser = function () {
|
|
throw new Error(
|
|
'ws does not work in the browser. Browser clients must use the native ' +
|
|
'WebSocket object'
|
|
);
|
|
};
|
|
return browser;
|
|
}
|
|
|
|
var browserExports = requireBrowser();
|
|
|
|
class ButtplugNodeWebsocketClientConnector extends ButtplugBrowserWebsocketClientConnector {
|
|
_websocketConstructor = browserExports.WebSocket;
|
|
}
|
|
|
|
class PercentOrSteps {
|
|
_percent;
|
|
_steps;
|
|
get percent() {
|
|
return this._percent;
|
|
}
|
|
get steps() {
|
|
return this._steps;
|
|
}
|
|
static createSteps(s) {
|
|
const v = new PercentOrSteps();
|
|
v._steps = s;
|
|
return v;
|
|
}
|
|
static createPercent(p) {
|
|
if (p < 0 || p > 1) {
|
|
throw new ButtplugDeviceError(`Percent value ${p} is not in the range 0.0 <= x <= 1.0`);
|
|
}
|
|
const v = new PercentOrSteps();
|
|
v._percent = p;
|
|
return v;
|
|
}
|
|
}
|
|
class DeviceOutputCommand {
|
|
constructor(_outputType, _value, _duration) {
|
|
this._outputType = _outputType;
|
|
this._value = _value;
|
|
this._duration = _duration;
|
|
}
|
|
get outputType() {
|
|
return this._outputType;
|
|
}
|
|
get value() {
|
|
return this._value;
|
|
}
|
|
get duration() {
|
|
return this._duration;
|
|
}
|
|
}
|
|
class DeviceOutputValueConstructor {
|
|
constructor(_outputType) {
|
|
this._outputType = _outputType;
|
|
}
|
|
steps(steps) {
|
|
return new DeviceOutputCommand(this._outputType, PercentOrSteps.createSteps(steps), void 0);
|
|
}
|
|
percent(percent) {
|
|
return new DeviceOutputCommand(
|
|
this._outputType,
|
|
PercentOrSteps.createPercent(percent),
|
|
void 0
|
|
);
|
|
}
|
|
}
|
|
class DeviceOutputPositionWithDurationConstructor {
|
|
steps(steps, duration) {
|
|
return new DeviceOutputCommand(
|
|
OutputType.Position,
|
|
PercentOrSteps.createSteps(steps),
|
|
duration
|
|
);
|
|
}
|
|
percent(percent, duration) {
|
|
return new DeviceOutputCommand(
|
|
OutputType.HwPositionWithDuration,
|
|
PercentOrSteps.createPercent(percent),
|
|
duration
|
|
);
|
|
}
|
|
}
|
|
class DeviceOutput {
|
|
constructor() {
|
|
}
|
|
static get Vibrate() {
|
|
return new DeviceOutputValueConstructor(OutputType.Vibrate);
|
|
}
|
|
static get Rotate() {
|
|
return new DeviceOutputValueConstructor(OutputType.Rotate);
|
|
}
|
|
static get Oscillate() {
|
|
return new DeviceOutputValueConstructor(OutputType.Oscillate);
|
|
}
|
|
static get Constrict() {
|
|
return new DeviceOutputValueConstructor(OutputType.Constrict);
|
|
}
|
|
static get Inflate() {
|
|
return new DeviceOutputValueConstructor(OutputType.Inflate);
|
|
}
|
|
static get Temperature() {
|
|
return new DeviceOutputValueConstructor(OutputType.Temperature);
|
|
}
|
|
static get Led() {
|
|
return new DeviceOutputValueConstructor(OutputType.Led);
|
|
}
|
|
static get Spray() {
|
|
return new DeviceOutputValueConstructor(OutputType.Spray);
|
|
}
|
|
static get Position() {
|
|
return new DeviceOutputValueConstructor(OutputType.Position);
|
|
}
|
|
static get PositionWithDuration() {
|
|
return new DeviceOutputPositionWithDurationConstructor();
|
|
}
|
|
}
|
|
|
|
class ButtplugWasmClientConnector extends EventEmitter {
|
|
static _loggingActivated = false;
|
|
static wasmInstance;
|
|
_connected = false;
|
|
client;
|
|
serverPtr;
|
|
constructor() {
|
|
super();
|
|
}
|
|
get Connected() {
|
|
return this._connected;
|
|
}
|
|
static maybeLoadWasm = async () => {
|
|
if (ButtplugWasmClientConnector.wasmInstance == void 0) {
|
|
const wasmModule = await import('../wasm/index.js');
|
|
await wasmModule.default();
|
|
ButtplugWasmClientConnector.wasmInstance = wasmModule;
|
|
}
|
|
};
|
|
static activateLogging = async (logLevel = "debug") => {
|
|
await ButtplugWasmClientConnector.maybeLoadWasm();
|
|
if (this._loggingActivated) {
|
|
console.log("Logging already activated, ignoring.");
|
|
return;
|
|
}
|
|
console.log("Turning on logging.");
|
|
ButtplugWasmClientConnector.wasmInstance.buttplug_activate_env_logger(logLevel);
|
|
};
|
|
initialize = async () => {
|
|
};
|
|
connect = async () => {
|
|
await ButtplugWasmClientConnector.maybeLoadWasm();
|
|
this.client = ButtplugWasmClientConnector.wasmInstance.buttplug_create_embedded_wasm_server(
|
|
(msgs) => {
|
|
this.emitMessage(msgs);
|
|
},
|
|
this.serverPtr
|
|
);
|
|
this._connected = true;
|
|
};
|
|
disconnect = async () => {
|
|
};
|
|
send = (msg) => {
|
|
ButtplugWasmClientConnector.wasmInstance.buttplug_client_send_json_message(
|
|
this.client,
|
|
new TextEncoder().encode("[" + JSON.stringify(msg) + "]"),
|
|
(output) => {
|
|
this.emitMessage(output);
|
|
}
|
|
);
|
|
};
|
|
emitMessage = (msg) => {
|
|
const str = new TextDecoder().decode(msg);
|
|
const msgs = JSON.parse(str);
|
|
this.emit("message", msgs);
|
|
};
|
|
}
|
|
|
|
export { ButtplugBrowserWebsocketClientConnector, ButtplugClient, ButtplugClientConnectorException, ButtplugClientDevice, ButtplugClientDeviceFeature, ButtplugDeviceError, ButtplugError, ButtplugInitError, ButtplugLogLevel, ButtplugLogger, ButtplugMessageError, ButtplugMessageSorter, ButtplugNodeWebsocketClientConnector, ButtplugPingError, ButtplugUnknownError, ButtplugWasmClientConnector, DEFAULT_MESSAGE_ID, DeviceOutput, DeviceOutputCommand, DeviceOutputPositionWithDurationConstructor, DeviceOutputValueConstructor, ErrorClass, InputCommandType, InputType, LogMessage, MAX_ID, MESSAGE_SPEC_VERSION_MAJOR, MESSAGE_SPEC_VERSION_MINOR, OutputType, SYSTEM_MESSAGE_ID, msgId, setMsgId };
|