@eventmsg/core
Version:
EventMsgV3 TypeScript library - Core protocol implementation with transport abstraction
1,601 lines (1,587 loc) • 73.9 kB
JavaScript
//#region rolldown:runtime
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
key = keys[i];
if (!__hasOwnProp.call(to, key) && key !== except) {
__defProp(to, key, {
get: ((k) => from[k]).bind(null, key),
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
});
}
}
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
value: mod,
enumerable: true
}) : target, mod));
//#endregion
//#region src/errors/event-msg-error.ts
/**
* Base error class for all EventMsg-related errors.
* Provides consistent error handling with context and solutions.
*/
var EventMsgError = class extends Error {
/** Error code for programmatic handling */
code;
/** Additional context about the error */
context;
/** Suggested solutions for the error */
solutions;
/** Original error that caused this error */
cause;
constructor(message, code, options = {}) {
super(message);
this.name = this.constructor.name;
this.code = code;
this.context = options.context;
this.solutions = options.solutions || [];
this.cause = options.cause;
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
}
/**
* Get a detailed error message including context and solutions
*/
getDetailedMessage() {
let details = `${this.name}: ${this.message}`;
if (this.code) details += ` (${this.code})`;
if (this.context && Object.keys(this.context).length > 0) {
details += "\nContext:";
for (const [key, value] of Object.entries(this.context)) details += `\n ${key}: ${JSON.stringify(value)}`;
}
if (this.solutions.length > 0) {
details += "\nSuggested solutions:";
for (const solution of this.solutions) details += `\n • ${solution}`;
}
if (this.cause) details += `\nCaused by: ${this.cause.message}`;
return details;
}
/**
* Convert error to JSON for logging/serialization
*/
toJSON() {
return {
name: this.name,
message: this.message,
code: this.code,
context: this.context,
solutions: this.solutions,
stack: this.stack,
cause: this.cause ? {
name: this.cause.name,
message: this.cause.message
} : void 0
};
}
};
//#endregion
//#region src/errors/protocol-error.ts
/**
* Error related to EventMsgV3 protocol operations (encoding, decoding, validation)
*/
var ProtocolError = class extends EventMsgError {
constructor(message, options = {}) {
super(message, "PROTOCOL_ERROR", {
...options,
solutions: options.solutions || [
"Check message format",
"Verify protocol specification compliance",
"Review message content"
]
});
}
};
/**
* Message encoding failed
*/
var EncodingError = class extends ProtocolError {
constructor(message = "Failed to encode message", options = {}) {
super(message, {
...options,
solutions: [
"Check event name length (≤64 bytes)",
"Check event data length (≤3048 bytes)",
"Verify data is JSON serializable",
"Check for invalid characters"
]
});
this.code = "ENCODING_ERROR";
}
};
/**
* Message decoding failed
*/
var DecodingError = class extends ProtocolError {
constructor(message = "Failed to decode message", options = {}) {
super(message, {
...options,
solutions: [
"Check message framing (SOH, STX, US, EOT)",
"Verify byte stuffing is correct",
"Check for data corruption",
"Ensure sender uses same protocol version"
]
});
this.code = "DECODING_ERROR";
}
};
/**
* Message validation failed
*/
var ValidationError = class extends ProtocolError {
constructor(message = "Message validation failed", options = {}) {
super(message, {
...options,
solutions: [
"Check address ranges (0-255)",
"Verify message size limits",
"Check required fields are present",
"Validate data types"
]
});
this.code = "VALIDATION_ERROR";
}
};
/**
* Invalid message format
*/
var InvalidMessageError = class extends ProtocolError {
constructor(message = "Invalid message format", options = {}) {
super(message, {
...options,
solutions: [
"Check message starts with SOH (0x01)",
"Check message ends with EOT (0x04)",
"Verify minimum message length (10 bytes)",
"Check header is exactly 7 bytes after unstuffing"
]
});
this.code = "INVALID_MESSAGE_ERROR";
}
};
//#endregion
//#region src/errors/timeout-error.ts
/**
* Error for operations that exceed their timeout duration
*/
var TimeoutError = class extends EventMsgError {
/** Timeout duration in milliseconds */
timeoutMs;
/** Operation that timed out */
operation;
constructor(message, timeoutMs, operation = "operation", options = {}) {
super(message, "TIMEOUT_ERROR", {
...options,
context: {
...options.context,
timeoutMs,
operation
},
solutions: [
"Increase timeout duration",
"Check network connectivity",
"Verify target device is responding",
"Reduce message frequency"
]
});
this.timeoutMs = timeoutMs;
this.operation = operation;
}
};
/**
* Send operation timed out
*/
var SendTimeoutError = class extends TimeoutError {
constructor(timeoutMs, options = {}) {
super(`Send operation timed out after ${timeoutMs}ms`, timeoutMs, "send", {
...options,
solutions: [
"Increase send timeout",
"Check transport connection",
"Verify target device is reachable",
"Check for network congestion"
]
});
this.code = "SEND_TIMEOUT_ERROR";
}
};
/**
* WaitFor operation timed out
*/
var WaitForTimeoutError = class extends TimeoutError {
/** Event name that was being waited for */
eventName;
constructor(eventName, timeoutMs, options = {}) {
super(`waitFor('${eventName}') timed out after ${timeoutMs}ms`, timeoutMs, "waitFor", {
...options,
context: {
...options.context,
eventName
},
solutions: [
"Increase waitFor timeout",
"Check if event name is correct",
"Verify sender is responding",
"Check event filter conditions"
]
});
this.eventName = eventName;
this.code = "WAIT_FOR_TIMEOUT_ERROR";
}
};
/**
* Connection timeout error
*/
var ConnectionTimeoutError = class extends TimeoutError {
constructor(timeoutMs, options = {}) {
super(`Connection attempt timed out after ${timeoutMs}ms`, timeoutMs, "connect", {
...options,
solutions: [
"Increase connection timeout",
"Check device availability",
"Verify connection parameters",
"Check for interference"
]
});
this.code = "CONNECTION_TIMEOUT_ERROR";
}
};
//#endregion
//#region src/errors/transport-error.ts
/**
* Error related to transport layer operations (connection, sending, receiving)
*/
var TransportError = class extends EventMsgError {
constructor(message, options = {}) {
super(message, "TRANSPORT_ERROR", {
...options,
solutions: options.solutions || [
"Check transport connection",
"Verify transport configuration",
"Retry the operation"
]
});
}
};
/**
* Transport connection failed
*/
var ConnectionError = class extends TransportError {
constructor(message = "Failed to connect to transport", options = {}) {
super(message, {
...options,
solutions: [
"Check if the device is available",
"Verify connection parameters",
"Ensure no other process is using the transport",
"Check device permissions"
]
});
this.code = "CONNECTION_ERROR";
}
};
/**
* Transport send operation failed
*/
var SendError = class extends TransportError {
constructor(message = "Failed to send data through transport", options = {}) {
super(message, {
...options,
solutions: [
"Check transport connection status",
"Verify message size is within limits",
"Retry sending the message",
"Check for transport buffer overflow"
]
});
this.code = "SEND_ERROR";
}
};
/**
* Transport disconnection error
*/
var DisconnectionError = class extends TransportError {
constructor(message = "Transport disconnected unexpectedly", options = {}) {
super(message, {
...options,
solutions: [
"Check physical connection",
"Implement reconnection logic",
"Monitor connection status",
"Handle graceful disconnection"
]
});
this.code = "DISCONNECTION_ERROR";
}
};
//#endregion
//#region src/errors/validation-error.ts
/**
* Error for input validation failures
*/
var ValidationError$1 = class extends EventMsgError {
/** Field that failed validation */
field;
/** Value that was invalid */
value;
constructor(message, options = {}) {
super(message, "VALIDATION_ERROR", {
...options,
context: {
...options.context,
field: options.field,
value: options.value
},
solutions: [
"Check input parameters",
"Verify value ranges and types",
"Review API documentation"
]
});
this.field = options.field;
this.value = options.value;
}
};
/**
* Address validation error (0-255 range)
*/
var AddressValidationError = class extends ValidationError$1 {
constructor(address, field = "address", options = {}) {
super(`Invalid ${field}: ${address}. Must be 0-255`, {
...options,
field,
value: address,
solutions: [
"Use address in range 0-255",
"Check for negative values",
"Verify address is an integer"
]
});
this.code = "ADDRESS_VALIDATION_ERROR";
}
};
/**
* Message size validation error
*/
var MessageSizeError = class extends ValidationError$1 {
constructor(size, maxSize, field = "message", options = {}) {
super(`${field} size ${size} bytes exceeds maximum ${maxSize} bytes`, {
...options,
field,
value: size,
context: {
...options.context,
maxSize
},
solutions: [
`Reduce ${field} size to ${maxSize} bytes or less`,
"Consider splitting large messages",
"Use more efficient encoding"
]
});
this.code = "MESSAGE_SIZE_ERROR";
}
};
//#endregion
//#region ../../node_modules/.bun/eventemitter3@5.0.1/node_modules/eventemitter3/index.js
var require_eventemitter3 = /* @__PURE__ */ __commonJS({ "../../node_modules/.bun/eventemitter3@5.0.1/node_modules/eventemitter3/index.js": ((exports, 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() {}
if (Object.create) {
Events.prototype = Object.create(null);
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$3() {
this._events = new Events();
this._eventsCount = 0;
}
/**
* Return an array listing the events for which the emitter has registered
* listeners.
*
* @returns {Array}
* @public
*/
EventEmitter$3.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$3.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$3.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$3.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, void 0, 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, void 0, 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$3.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$3.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$3.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]);
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$3.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;
};
EventEmitter$3.prototype.off = EventEmitter$3.prototype.removeListener;
EventEmitter$3.prototype.addListener = EventEmitter$3.prototype.on;
EventEmitter$3.prefixed = prefix;
EventEmitter$3.EventEmitter = EventEmitter$3;
if ("undefined" !== typeof module) module.exports = EventEmitter$3;
}) });
//#endregion
//#region ../../node_modules/.bun/eventemitter3@5.0.1/node_modules/eventemitter3/index.mjs
var import_eventemitter3 = /* @__PURE__ */ __toESM(require_eventemitter3(), 1);
//#endregion
//#region ../../node_modules/.bun/consola@3.4.2/node_modules/consola/dist/core.mjs
const LogLevels = {
silent: Number.NEGATIVE_INFINITY,
fatal: 0,
error: 0,
warn: 1,
log: 2,
info: 3,
success: 3,
fail: 3,
ready: 3,
start: 3,
box: 3,
debug: 4,
trace: 5,
verbose: Number.POSITIVE_INFINITY
};
const LogTypes = {
silent: { level: -1 },
fatal: { level: LogLevels.fatal },
error: { level: LogLevels.error },
warn: { level: LogLevels.warn },
log: { level: LogLevels.log },
info: { level: LogLevels.info },
success: { level: LogLevels.success },
fail: { level: LogLevels.fail },
ready: { level: LogLevels.info },
start: { level: LogLevels.info },
box: { level: LogLevels.info },
debug: { level: LogLevels.debug },
trace: { level: LogLevels.trace },
verbose: { level: LogLevels.verbose }
};
function isPlainObject$1(value) {
if (value === null || typeof value !== "object") return false;
const prototype = Object.getPrototypeOf(value);
if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) return false;
if (Symbol.iterator in value) return false;
if (Symbol.toStringTag in value) return Object.prototype.toString.call(value) === "[object Module]";
return true;
}
function _defu(baseObject, defaults, namespace = ".", merger) {
if (!isPlainObject$1(defaults)) return _defu(baseObject, {}, namespace, merger);
const object = Object.assign({}, defaults);
for (const key in baseObject) {
if (key === "__proto__" || key === "constructor") continue;
const value = baseObject[key];
if (value === null || value === void 0) continue;
if (merger && merger(object, key, value, namespace)) continue;
if (Array.isArray(value) && Array.isArray(object[key])) object[key] = [...value, ...object[key]];
else if (isPlainObject$1(value) && isPlainObject$1(object[key])) object[key] = _defu(value, object[key], (namespace ? `${namespace}.` : "") + key.toString(), merger);
else object[key] = value;
}
return object;
}
function createDefu(merger) {
return (...arguments_) => arguments_.reduce((p, c) => _defu(p, c, "", merger), {});
}
const defu = createDefu();
function isPlainObject(obj) {
return Object.prototype.toString.call(obj) === "[object Object]";
}
function isLogObj(arg) {
if (!isPlainObject(arg)) return false;
if (!arg.message && !arg.args) return false;
if (arg.stack) return false;
return true;
}
let paused = false;
const queue = [];
var Consola = class Consola {
options;
_lastLog;
_mockFn;
/**
* Creates an instance of Consola with specified options or defaults.
*
* @param {Partial<ConsolaOptions>} [options={}] - Configuration options for the Consola instance.
*/
constructor(options = {}) {
const types = options.types || LogTypes;
this.options = defu({
...options,
defaults: { ...options.defaults },
level: _normalizeLogLevel(options.level, types),
reporters: [...options.reporters || []]
}, {
types: LogTypes,
throttle: 1e3,
throttleMin: 5,
formatOptions: {
date: true,
colors: false,
compact: true
}
});
for (const type in types) {
const defaults = {
type,
...this.options.defaults,
...types[type]
};
this[type] = this._wrapLogFn(defaults);
this[type].raw = this._wrapLogFn(defaults, true);
}
if (this.options.mockFn) this.mockTypes();
this._lastLog = {};
}
/**
* Gets the current log level of the Consola instance.
*
* @returns {number} The current log level.
*/
get level() {
return this.options.level;
}
/**
* Sets the minimum log level that will be output by the instance.
*
* @param {number} level - The new log level to set.
*/
set level(level) {
this.options.level = _normalizeLogLevel(level, this.options.types, this.options.level);
}
/**
* Displays a prompt to the user and returns the response.
* Throw an error if `prompt` is not supported by the current configuration.
*
* @template T
* @param {string} message - The message to display in the prompt.
* @param {T} [opts] - Optional options for the prompt. See {@link PromptOptions}.
* @returns {promise<T>} A promise that infer with the prompt options. See {@link PromptOptions}.
*/
prompt(message, opts) {
if (!this.options.prompt) throw new Error("prompt is not supported!");
return this.options.prompt(message, opts);
}
/**
* Creates a new instance of Consola, inheriting options from the current instance, with possible overrides.
*
* @param {Partial<ConsolaOptions>} options - Optional overrides for the new instance. See {@link ConsolaOptions}.
* @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
*/
create(options) {
const instance = new Consola({
...this.options,
...options
});
if (this._mockFn) instance.mockTypes(this._mockFn);
return instance;
}
/**
* Creates a new Consola instance with the specified default log object properties.
*
* @param {InputLogObject} defaults - Default properties to include in any log from the new instance. See {@link InputLogObject}.
* @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
*/
withDefaults(defaults) {
return this.create({
...this.options,
defaults: {
...this.options.defaults,
...defaults
}
});
}
/**
* Creates a new Consola instance with a specified tag, which will be included in every log.
*
* @param {string} tag - The tag to include in each log of the new instance.
* @returns {ConsolaInstance} A new Consola instance. See {@link ConsolaInstance}.
*/
withTag(tag) {
return this.withDefaults({ tag: this.options.defaults.tag ? this.options.defaults.tag + ":" + tag : tag });
}
/**
* Adds a custom reporter to the Consola instance.
* Reporters will be called for each log message, depending on their implementation and log level.
*
* @param {ConsolaReporter} reporter - The reporter to add. See {@link ConsolaReporter}.
* @returns {Consola} The current Consola instance.
*/
addReporter(reporter) {
this.options.reporters.push(reporter);
return this;
}
/**
* Removes a custom reporter from the Consola instance.
* If no reporter is specified, all reporters will be removed.
*
* @param {ConsolaReporter} reporter - The reporter to remove. See {@link ConsolaReporter}.
* @returns {Consola} The current Consola instance.
*/
removeReporter(reporter) {
if (reporter) {
const i = this.options.reporters.indexOf(reporter);
if (i !== -1) return this.options.reporters.splice(i, 1);
} else this.options.reporters.splice(0);
return this;
}
/**
* Replaces all reporters of the Consola instance with the specified array of reporters.
*
* @param {ConsolaReporter[]} reporters - The new reporters to set. See {@link ConsolaReporter}.
* @returns {Consola} The current Consola instance.
*/
setReporters(reporters) {
this.options.reporters = Array.isArray(reporters) ? reporters : [reporters];
return this;
}
wrapAll() {
this.wrapConsole();
this.wrapStd();
}
restoreAll() {
this.restoreConsole();
this.restoreStd();
}
/**
* Overrides console methods with Consola logging methods for consistent logging.
*/
wrapConsole() {
for (const type in this.options.types) {
if (!console["__" + type]) console["__" + type] = console[type];
console[type] = this[type].raw;
}
}
/**
* Restores the original console methods, removing Consola overrides.
*/
restoreConsole() {
for (const type in this.options.types) if (console["__" + type]) {
console[type] = console["__" + type];
delete console["__" + type];
}
}
/**
* Overrides standard output and error streams to redirect them through Consola.
*/
wrapStd() {
this._wrapStream(this.options.stdout, "log");
this._wrapStream(this.options.stderr, "log");
}
_wrapStream(stream, type) {
if (!stream) return;
if (!stream.__write) stream.__write = stream.write;
stream.write = (data) => {
this[type].raw(String(data).trim());
};
}
/**
* Restores the original standard output and error streams, removing the Consola redirection.
*/
restoreStd() {
this._restoreStream(this.options.stdout);
this._restoreStream(this.options.stderr);
}
_restoreStream(stream) {
if (!stream) return;
if (stream.__write) {
stream.write = stream.__write;
delete stream.__write;
}
}
/**
* Pauses logging, queues incoming logs until resumed.
*/
pauseLogs() {
paused = true;
}
/**
* Resumes logging, processing any queued logs.
*/
resumeLogs() {
paused = false;
const _queue = queue.splice(0);
for (const item of _queue) item[0]._logFn(item[1], item[2]);
}
/**
* Replaces logging methods with mocks if a mock function is provided.
*
* @param {ConsolaOptions["mockFn"]} mockFn - The function to use for mocking logging methods. See {@link ConsolaOptions["mockFn"]}.
*/
mockTypes(mockFn) {
const _mockFn = mockFn || this.options.mockFn;
this._mockFn = _mockFn;
if (typeof _mockFn !== "function") return;
for (const type in this.options.types) {
this[type] = _mockFn(type, this.options.types[type]) || this[type];
this[type].raw = this[type];
}
}
_wrapLogFn(defaults, isRaw) {
return (...args) => {
if (paused) {
queue.push([
this,
defaults,
args,
isRaw
]);
return;
}
return this._logFn(defaults, args, isRaw);
};
}
_logFn(defaults, args, isRaw) {
if ((defaults.level || 0) > this.level) return false;
const logObj = {
date: /* @__PURE__ */ new Date(),
args: [],
...defaults,
level: _normalizeLogLevel(defaults.level, this.options.types)
};
if (!isRaw && args.length === 1 && isLogObj(args[0])) Object.assign(logObj, args[0]);
else logObj.args = [...args];
if (logObj.message) {
logObj.args.unshift(logObj.message);
delete logObj.message;
}
if (logObj.additional) {
if (!Array.isArray(logObj.additional)) logObj.additional = logObj.additional.split("\n");
logObj.args.push("\n" + logObj.additional.join("\n"));
delete logObj.additional;
}
logObj.type = typeof logObj.type === "string" ? logObj.type.toLowerCase() : "log";
logObj.tag = typeof logObj.tag === "string" ? logObj.tag : "";
const resolveLog = (newLog = false) => {
const repeated = (this._lastLog.count || 0) - this.options.throttleMin;
if (this._lastLog.object && repeated > 0) {
const args2 = [...this._lastLog.object.args];
if (repeated > 1) args2.push(`(repeated ${repeated} times)`);
this._log({
...this._lastLog.object,
args: args2
});
this._lastLog.count = 1;
}
if (newLog) {
this._lastLog.object = logObj;
this._log(logObj);
}
};
clearTimeout(this._lastLog.timeout);
const diffTime = this._lastLog.time && logObj.date ? logObj.date.getTime() - this._lastLog.time.getTime() : 0;
this._lastLog.time = logObj.date;
if (diffTime < this.options.throttle) try {
const serializedLog = JSON.stringify([
logObj.type,
logObj.tag,
logObj.args
]);
const isSameLog = this._lastLog.serialized === serializedLog;
this._lastLog.serialized = serializedLog;
if (isSameLog) {
this._lastLog.count = (this._lastLog.count || 0) + 1;
if (this._lastLog.count > this.options.throttleMin) {
this._lastLog.timeout = setTimeout(resolveLog, this.options.throttle);
return;
}
}
} catch {}
resolveLog(true);
}
_log(logObj) {
for (const reporter of this.options.reporters) reporter.log(logObj, { options: this.options });
}
};
function _normalizeLogLevel(input, types = {}, defaultLevel = 3) {
if (input === void 0) return defaultLevel;
if (typeof input === "number") return input;
if (types[input] && types[input].level !== void 0) return types[input].level;
return defaultLevel;
}
Consola.prototype.add = Consola.prototype.addReporter;
Consola.prototype.remove = Consola.prototype.removeReporter;
Consola.prototype.clear = Consola.prototype.removeReporter;
Consola.prototype.withScope = Consola.prototype.withTag;
Consola.prototype.mock = Consola.prototype.mockTypes;
Consola.prototype.pause = Consola.prototype.pauseLogs;
Consola.prototype.resume = Consola.prototype.resumeLogs;
function createConsola$1(options = {}) {
return new Consola(options);
}
//#endregion
//#region ../../node_modules/.bun/consola@3.4.2/node_modules/consola/dist/browser.mjs
var BrowserReporter = class {
options;
defaultColor;
levelColorMap;
typeColorMap;
constructor(options) {
this.options = { ...options };
this.defaultColor = "#7f8c8d";
this.levelColorMap = {
0: "#c0392b",
1: "#f39c12",
3: "#00BCD4"
};
this.typeColorMap = { success: "#2ecc71" };
}
_getLogFn(level) {
if (level < 1) return console.__error || console.error;
if (level === 1) return console.__warn || console.warn;
return console.__log || console.log;
}
log(logObj) {
const consoleLogFn = this._getLogFn(logObj.level);
const type = logObj.type === "log" ? "" : logObj.type;
const tag = logObj.tag || "";
const style = `
background: ${this.typeColorMap[logObj.type] || this.levelColorMap[logObj.level] || this.defaultColor};
border-radius: 0.5em;
color: white;
font-weight: bold;
padding: 2px 0.5em;
`;
const badge = `%c${[tag, type].filter(Boolean).join(":")}`;
if (typeof logObj.args[0] === "string") consoleLogFn(`${badge}%c ${logObj.args[0]}`, style, "", ...logObj.args.slice(1));
else consoleLogFn(badge, style, ...logObj.args);
}
};
function createConsola(options = {}) {
return createConsola$1({
reporters: options.reporters || [new BrowserReporter({})],
prompt(message, options2 = {}) {
if (options2.type === "confirm") return Promise.resolve(confirm(message));
return Promise.resolve(prompt(message));
},
...options
});
}
const consola = createConsola();
//#endregion
//#region src/types/logger.ts
/**
* Default logging configuration
*/
const DEFAULT_LOGGING_CONFIG = {
enabled: true,
level: 3
};
/**
* Log namespaces for tagging
*/
const LOG_NAMESPACES = {
CORE: "eventmsg:core",
PROTOCOL: "eventmsg:protocol",
TRANSPORT: "eventmsg:transport",
TRANSPORT_WEBBLE: "eventmsg:transport:webble"
};
//#endregion
//#region src/internal/logger.ts
let globalConsola = createConsola({ level: DEFAULT_LOGGING_CONFIG.level ?? 3 });
/**
* Configure global logging
*/
function configureLogging(config) {
const finalConfig = {
...DEFAULT_LOGGING_CONFIG,
...config
};
if (finalConfig.enabled === false) globalConsola = createConsola({
level: -999,
reporters: []
});
else if (finalConfig.enabled && finalConfig.level !== -999) globalConsola = createConsola({ level: finalConfig.level ?? 3 });
else globalConsola = createConsola({
level: -999,
reporters: []
});
}
/**
* Get logger for a component
*/
function getLogger(namespace) {
return globalConsola.withTag(LOG_NAMESPACES[namespace]);
}
/**
* Simple hex dump for debugging
*/
function hexDump(data, maxBytes = 64) {
const limited = data.length > maxBytes ? data.slice(0, maxBytes) : data;
const hex = Array.from(limited).map((b) => b.toString(16).padStart(2, "0")).join(" ");
if (data.length > maxBytes) return `${hex} ... (${data.length - maxBytes} more bytes)`;
return hex;
}
//#endregion
//#region src/internal/byte-stuffing.ts
/**
* EventMsgV3 Byte Stuffing Implementation
*
* Implements the exact byte stuffing algorithm from the C implementation
* to ensure protocol compatibility.
*/
/**
* Control characters used in EventMsgV3 protocol
*/
const CONTROL_CHARS = {
SOH: 1,
STX: 2,
EOT: 4,
US: 31,
ESC: 27
};
/**
* XOR mask for byte stuffing
*/
const STUFF_MASK = 32;
/**
* Check if a byte is a control character that needs stuffing
*/
function isControlChar(byte) {
return byte === CONTROL_CHARS.SOH || byte === CONTROL_CHARS.STX || byte === CONTROL_CHARS.EOT || byte === CONTROL_CHARS.US || byte === CONTROL_CHARS.ESC;
}
/**
* Stuff (encode) bytes by escaping control characters
*
* Algorithm:
* - If byte is a control character: Insert ESC, then XOR byte with 0x20
* - Otherwise: Insert byte as-is
*
* @param data Input data to stuff
* @returns Stuffed data
*/
function stuff(data) {
const result = [];
for (let i = 0; i < data.length; i++) {
const byte = data[i];
if (byte === void 0) continue;
if (isControlChar(byte)) {
result.push(CONTROL_CHARS.ESC);
result.push(byte ^ STUFF_MASK);
} else result.push(byte);
}
return new Uint8Array(result);
}
/**
* Unstuff (decode) bytes by handling escaped characters
*
* Algorithm:
* - If byte is ESC: Mark next byte as escaped, continue
* - If previous byte was ESC: XOR current byte with 0x20, add to output
* - Otherwise: Add byte as-is to output
*
* @param data Stuffed data to unstuff
* @returns Unstuffed data
* @throws {Error} If data contains invalid escape sequences
*/
function unstuff(data) {
const result = [];
let escaped = false;
for (let i = 0; i < data.length; i++) {
const byte = data[i];
if (byte === void 0) continue;
if (escaped) {
result.push(byte ^ STUFF_MASK);
escaped = false;
} else if (byte === CONTROL_CHARS.ESC) escaped = true;
else result.push(byte);
}
if (escaped) throw new Error("Invalid byte stuffing: data ends with incomplete escape sequence");
return new Uint8Array(result);
}
/**
* Calculate the maximum possible size after stuffing
* In worst case, every byte could be a control character, doubling the size
*
* @param originalSize Original data size
* @returns Maximum size after stuffing
*/
function getMaxStuffedSize(originalSize) {
return originalSize * 2;
}
/**
* Calculate the minimum possible size after unstuffing
* In best case, no bytes are stuffed
*
* @param stuffedSize Stuffed data size
* @returns Minimum size after unstuffing
*/
function getMinUnstuffedSize(stuffedSize) {
return Math.floor(stuffedSize / 2);
}
/**
* Test if data contains any control characters that would need stuffing
*
* @param data Data to test
* @returns True if data contains control characters
*/
function needsStuffing(data) {
for (let i = 0; i < data.length; i++) {
const byte = data[i];
if (byte !== void 0 && isControlChar(byte)) return true;
}
return false;
}
/**
* Validate that stuffed data has proper escape sequences
*
* @param data Stuffed data to validate
* @returns True if data has valid stuffing
*/
function isValidStuffing(data) {
let escaped = false;
for (let i = 0; i < data.length; i++) {
const byte = data[i];
if (byte === void 0) continue;
if (escaped) {
if (!isControlChar(byte ^ STUFF_MASK)) return false;
escaped = false;
} else if (byte === CONTROL_CHARS.ESC) escaped = true;
else if (isControlChar(byte)) return false;
}
return !escaped;
}
//#endregion
//#region src/types/config.ts
/**
* Default protocol options
*/
const DEFAULT_PROTOCOL_OPTIONS = {
strictValidation: true,
maxEventNameLength: 64,
maxEventDataLength: 3048
};
/**
* Default configuration values
*/
const DEFAULT_CONFIG = {
maxMessageSize: 4096,
messageTimeout: 5e3,
encoding: "utf8",
logging: DEFAULT_LOGGING_CONFIG,
protocol: DEFAULT_PROTOCOL_OPTIONS
};
//#endregion
//#region src/protocol.ts
/**
* EventMsgV3 Protocol Implementation
*
* Handles encoding and decoding of EventMsgV3 binary protocol messages.
* Message format: [SOH][StuffedHeader][STX][StuffedEventName][US][StuffedEventData][EOT]
*/
var Protocol = class {
options;
encoding;
logger;
constructor(config) {
this.options = {
...DEFAULT_PROTOCOL_OPTIONS,
...config.protocol
};
this.encoding = config.encoding ?? "utf8";
this.logger = getLogger("PROTOCOL");
}
/**
* Encode a message into EventMsgV3 binary format
*
* @param header Message header (7 bytes)
* @param eventName Event name string
* @param eventData Event data string (JSON)
* @returns Encoded binary message
* @throws {EncodingError} If encoding fails
* @throws {MessageSizeError} If message exceeds size limits
*/
encode(header, eventName, eventData) {
this.logger.debug("Starting message encode", {
eventName,
eventDataLength: eventData.length,
header,
encoding: this.encoding
});
try {
this.validateHeader(header);
this.validateEventName(eventName);
this.validateEventData(eventData);
const eventNameBytes = this.stringToBytes(eventName);
const eventDataBytes = this.stringToBytes(eventData);
const headerBytes = this.serializeHeader(header);
const stuffedHeader = stuff(headerBytes);
const stuffedEventName = stuff(eventNameBytes);
const stuffedEventData = stuff(eventDataBytes);
this.logger.trace("Byte stuffing complete", {
headerSize: `${headerBytes.length} -> ${stuffedHeader.length}`,
nameSize: `${eventNameBytes.length} -> ${stuffedEventName.length}`,
dataSize: `${eventDataBytes.length} -> ${stuffedEventData.length}`
});
const totalSize = 1 + stuffedHeader.length + 1 + stuffedEventName.length + 1 + stuffedEventData.length + 1;
if (totalSize > this.options.maxEventDataLength + 1e3) throw new MessageSizeError(totalSize, this.options.maxEventDataLength + 1e3, "encoded message");
const message = new Uint8Array(totalSize);
let offset = 0;
message[offset++] = CONTROL_CHARS.SOH;
message.set(stuffedHeader, offset);
offset += stuffedHeader.length;
message[offset++] = CONTROL_CHARS.STX;
message.set(stuffedEventName, offset);
offset += stuffedEventName.length;
message[offset++] = CONTROL_CHARS.US;
message.set(stuffedEventData, offset);
offset += stuffedEventData.length;
message[offset++] = CONTROL_CHARS.EOT;
const finalMessage = message.subarray(0, offset);
this.logger.info("Message encoded successfully", {
eventName,
finalSize: finalMessage.length,
messageId: header.messageId
});
this.logger.trace("Encoded message hex dump", { hex: hexDump(finalMessage) });
return finalMessage;
} catch (error) {
this.logger.error("Message encoding failed", {
error: error instanceof Error ? error.message : String(error),
eventName,
eventDataLength: eventData.length,
cause: error instanceof Error ? error.name : "Unknown"
});
if (error instanceof MessageSizeError) throw error;
throw new EncodingError("Failed to encode EventMsgV3 message", {
context: {
eventName,
eventDataLength: eventData.length
},
cause: error instanceof Error ? error : new Error(String(error))
});
}
}
/**
* Encode a message with binary data into EventMsgV3 format
*
* @param header Message header (7 bytes)
* @param eventName Event name string
* @param eventData Binary event data
* @returns Encoded binary message
* @throws {EncodingError} If encoding fails
* @throws {MessageSizeError} If message exceeds size limits
*/
encodeBinary(header, eventName, eventData) {
this.logger.debug("Starting binary message encode", {
eventName,
eventDataLength: eventData.length,
header
});
try {
this.validateHeader(header);
this.validateEventName(eventName);
if (eventData.length > this.options.maxEventDataLength) throw new MessageSizeError(eventData.length, this.options.maxEventDataLength, "eventData");
const eventNameBytes = this.stringToBytes(eventName);
const headerBytes = this.serializeHeader(header);
const stuffedHeader = stuff(headerBytes);
const stuffedEventName = stuff(eventNameBytes);
const stuffedEventData = stuff(eventData);
this.logger.trace("Binary byte stuffing complete", {
headerSize: `${headerBytes.length} -> ${stuffedHeader.length}`,
nameSize: `${eventNameBytes.length} -> ${stuffedEventName.length}`,
dataSize: `${eventData.length} -> ${stuffedEventData.length}`
});
const totalSize = 1 + stuffedHeader.length + 1 + stuffedEventName.length + 1 + stuffedEventData.length + 1;
if (totalSize > this.options.maxEventDataLength + 1e3) throw new MessageSizeError(totalSize, this.options.maxEventDataLength + 1e3, "encoded message");
const message = new Uint8Array(totalSize);
let offset = 0;
message[offset++] = CONTROL_CHARS.SOH;
message.set(stuffedHeader, offset);
offset += stuffedHeader.length;
message[offset++] = CONTROL_CHARS.STX;
message.set(stuffedEventName, offset);
offset += stuffedEventName.length;
message[offset++] = CONTROL_CHARS.US;
message.set(stuffedEventData, offset);
offset += stuffedEventData.length;
message[offset++] = CONTROL_CHARS.EOT;
const finalMessage = message.subarray(0, offset);
this.logger.info("Binary message encoded successfully", {
eventName,
finalSize: finalMessage.length,
messageId: header.messageId
});
this.logger.trace("Encoded binary message hex dump", { hex: hexDump(finalMessage) });
return finalMessage;
} catch (error) {
this.logger.error("Binary message encoding failed", {
error: error instanceof Error ? error.message : String(error),
eventName,
eventDataLength: eventData.length,
cause: error instanceof Error ? error.name : "Unknown"
});
if (error instanceof MessageSizeError) throw error;
throw new EncodingError("Failed to encode EventMsgV3 binary message", {
context: {
eventName,
eventDataLength: eventData.length
},
cause: error instanceof Error ? error : new Error(String(error))
});
}
}
/**
* Decode a binary message into EventMsgV3 components
*
* @param data Binary message data
* @returns Decoded message components
* @throws {DecodingError} If decoding fails
* @throws {InvalidMessageError} If message format is invalid
*/
decode(data) {
this.logger.debug("Starting message decode", {
messageLength: data.length,
encoding: this.encoding
});
this.logger.trace("Incoming message hex dump", { hex: hexDump(data) });
try {
if (data.length < 10) throw new InvalidMessageError(`Message too short: ${data.length} bytes (minimum 10)`, { context: { messageLength: data.length } });
if (data[0] !== CONTROL_CHARS.SOH) throw new InvalidMessageError(`Message must start with SOH (0x01), got 0x${data[0]?.toString(16).padStart(2, "0")}`, { context: { firstByte: data[0] } });
if (data.at(-1) !== CONTROL_CHARS.EOT) throw new InvalidMessageError(`Message must end with EOT (0x04), got 0x${data.at(-1)?.toString(16).padStart(2, "0")}`, { context: { lastByte: data.at(-1) } });
const stxIndex = this.findControlChar(data, CONTROL_CHARS.STX, 1);
const usIndex = this.findControlChar(data, CONTROL_CHARS.US, stxIndex + 1);
if (stxIndex === -1) throw new InvalidMessageError("STX (0x02) not found in message");
if (usIndex === -1) throw new InvalidMessageError("US (0x1F) not found in message");
const stuffedHeader = data.subarray(1, stxIndex);
const stuffedEventName = data.subarray(stxIndex + 1, usIndex);
const stuffedEventData = data.subarray(usIndex + 1, data.length - 1);
const headerBytes = unstuff(stuffedHeader);
const eventNameBytes = unstuff(stuffedEventName);
const eventDataBytes = unstuff(stuffedEventData);
this.logger.trace("Byte unstuffing complete", {
headerSize: `${stuffedHeader.length} -> ${headerBytes.length}`,
nameSize: `${stuffedEventName.length} -> ${eventNameBytes.length}`,
dataSize: `${stuffedEventData.length} -> ${eventDataBytes.length}`
});
const header = this.deserializeHeader(headerBytes);
const eventName = this.bytesToString(eventNameBytes);
const eventData = this.bytesToString(eventDataBytes);
if (this.options.strictValidation) {
this.validateHeader(header);
this.validateEventName(eventName);
this.validateEventData(eventData);
}
this.logger.info("Message decoded successfully", {
eventName,
messageId: header.messageId,
senderId: header.senderId,
receiverId: header.receiverId,
eventDataLength: eventData.length
});
return {
header,
eventName,
eventData
};
} catch (error) {
this.logger.error("Message decoding failed", {
error: error instanceof Error ? error.message : String(error),
messageLength: data.length,
cause: error instanceof Error ? error.name : "Unknown",
hex: hexDump(data, 64)
});
if (error instanceof InvalidMessageError) throw error;
throw new DecodingError("Failed to decode EventMsgV3 message", {
context: { messageLength: data.length },
cause: error instanceof Error ? error : new Error(String(error))
});
}
}
/**
* Find a control character in data, skipping escaped instances
*/
findControlChar(data, target, startIndex) {
let escaped = false;
for (let i = startIndex; i < data.length; i++) {
const byte = data[i];
if (byte === void 0) continue;
if (escaped) escaped = false;
else if (byte === CONTROL_CHARS.ESC) escaped = true;
else if (byte === target) return i;
}
return -1;
}
/**
* Serialize message header to 7 bytes (big-endian messageId)
*/
serializeHeader(header) {
const bytes = new Uint8Array(7);
bytes[0] = header.senderId;
bytes[1] = header.receiverId;
bytes[2] = header.senderGroupId;
bytes[3] = header.receiverGroupId;
bytes[4] = header.flags;
bytes[5] = header.messageId >> 8 & 255;
bytes[6] = header.messageId & 255;
return bytes;
}
/**
* Deserialize 7 bytes to message header (big-endian messageId)
*/
deserializeHeader(bytes) {
if (bytes.length !== 7) throw new InvalidMessageError(`Header must be exactly 7 bytes, got ${bytes.length}`, { context: { headerLength: bytes.length } });
if (bytes[0] === void 0 || bytes[1] === void 0 || bytes[2] === void 0 || bytes[3] === void 0 || bytes[4] === void 0 || bytes[5] === void 0 || bytes[6] === void 0) throw new InvalidMessageError(`Header must be exactly 7 bytes, got ${bytes.length}`, { context: { headerLength: bytes.length } });
return {
senderId: bytes[0],
receiverId: bytes[1],
senderGroupId: bytes[2],
receiverGroupId: bytes[3],
flags: bytes[4],
messageId: bytes[5] << 8 | bytes[6]
};
}
/**
* Convert string to bytes using configured encoding
*/
stringToBytes(str) {
if (this.encoding === "utf8") return new TextEncoder().encode(str);
const bytes = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) bytes[i] = str.charCodeAt(i) & 255;
return bytes;
}
/**
* Convert bytes to string using configured encoding
*/
bytesToString(bytes) {
if (this.encoding === "utf8") return new TextDecoder("utf8").decode(bytes);
return String.fromCharCode(...bytes);
}
/**
* Validate message header fields
*/
validateHeader(header) {
const fields = [
{
name: "senderId",
value: header.senderId
},
{
name: "receiverId",
value: header.receiverId
},
{
name: "senderGroupId",
value: header.senderGroupId
},
{
name: "receiverGroupId",
value: header.receiverGroupId
},
{
name: "flags",
value: header.flags
}
];
for (const field of fields) if (field.value < 0 || field.value > 255 || !Number.isInteger(field.value)) throw new MessageSizeError(field.value, 255, field.name, { context: { validRange: "0-255" } });
if (header.messageId < 0 || header.messageId > 65535 || !Number.isInteger(header.messageId)) throw new MessageSizeError(header.messageId, 65535, "messageId", { context: { validRange: "0-65535" } });
}
/**
* Validate event name leng