diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f1e8296..8af3dd5a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: pnpm run eslint - name: Test - run: pnpm run test-with-coverage + run: pnpm run test:coverage - name: Log in to the Docker container registry if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push' @@ -188,4 +188,4 @@ jobs: run: pnpm run build - name: Test - run: pnpm run test-with-coverage + run: pnpm run test:coverage diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index ef75c7fe..00000000 --- a/babel.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - presets: [['@babel/preset-env', {targets: {node: 'current'}}], '@babel/preset-typescript'], - assumptions: {setPublicClassFields: true}, - plugins: [['@babel/plugin-proposal-decorators', {legacy: true}]], -}; diff --git a/lib/controller.ts b/lib/controller.ts index afeebe4f..5a3e813c 100644 --- a/lib/controller.ts +++ b/lib/controller.ts @@ -131,10 +131,11 @@ export class Controller { try { this.sdNotify = process.env.NOTIFY_SOCKET ? await import('sd-notify') : undefined; logger.debug('sd-notify loaded'); + /* v8 ignore start */ } catch { - // istanbul ignore next logger.debug('sd-notify is not installed'); } + /* v8 ignore stop */ // Start zigbee try { @@ -145,10 +146,13 @@ export class Controller { logger.error('Check https://www.zigbee2mqtt.io/guide/installation/20_zigbee2mqtt-fails-to-start.html for possible solutions'); logger.error('Exiting...'); logger.error((error as Error).stack!); - /* istanbul ignore if */ + + /* v8 ignore start */ if ((error as Error).message.includes('USB adapter discovery error (No valid USB adapter found)')) { logger.error('If this happens after updating to Zigbee2MQTT 2.0.0, see https://github.com/Koenkk/zigbee2mqtt/discussions/24364'); } + /* v8 ignore stop */ + return await this.exit(1); } @@ -292,6 +296,7 @@ export class Controller { softwareBuildID: entity.zh.softwareBuildID, // Manufacturer name can contain \u0000, remove this. // https://github.com/home-assistant/core/issues/85691 + /* v8 ignore next */ manufacturerName: entity.zh.manufacturerName?.split('\u0000')[0], }; } @@ -360,7 +365,6 @@ export class Controller { try { await extension[method]?.(); } catch (error) { - /* istanbul ignore next */ logger.error(`Failed to call '${extension.constructor.name}' '${method}' (${(error as Error).stack})`); } } diff --git a/lib/extension/availability.ts b/lib/extension/availability.ts index 82e33a99..8f656edc 100644 --- a/lib/extension/availability.ts +++ b/lib/extension/availability.ts @@ -42,7 +42,7 @@ export default class Availability extends Extension { private isAvailable(entity: Device | Group): boolean { if (entity.isDevice()) { - return Date.now() - (entity.zh.lastSeen ?? /* istanbul ignore next */ 0) < this.getTimeout(entity); + return Date.now() - (entity.zh.lastSeen ?? /* v8 ignore next */ 0) < this.getTimeout(entity); } else { const membersDevices = entity.membersDevices(); return membersDevices.length === 0 || membersDevices.some((d) => this.availabilityCache[d.ieeeAddr]); @@ -164,7 +164,7 @@ export default class Availability extends Extension { private async publishAvailability(entity: Device | Group, logLastSeen: boolean, forcePublish = false, skipGroups = false): Promise { if (logLastSeen && entity.isDevice()) { - const ago = Date.now() - (entity.zh.lastSeen ?? /* istanbul ignore next */ 0); + const ago = Date.now() - (entity.zh.lastSeen ?? /* v8 ignore next */ 0); if (this.isActiveDevice(entity)) { logger.debug(`Active device '${entity.name}' was last seen '${(ago / utils.minutes(1)).toFixed(2)}' minutes ago.`); diff --git a/lib/extension/bind.ts b/lib/extension/bind.ts index 8a080823..f2b024f1 100755 --- a/lib/extension/bind.ts +++ b/lib/extension/bind.ts @@ -170,12 +170,10 @@ const POLL_ON_MESSAGE: Readonly = [ const supportedAttrs = await getColorCapabilities(endpoint); const readAttrs: string[] = []; - /* istanbul ignore else */ if (supportedAttrs.colorXY) { readAttrs.push('currentX', 'currentY'); } - /* istanbul ignore else */ if (supportedAttrs.colorTemperature) { readAttrs.push('colorTemperature'); } @@ -390,7 +388,6 @@ export default class Bind extends Extension { failed: failedClusters, }; - /* istanbul ignore else */ if (successfulClusters.length !== 0) { if (type === 'bind') { await this.setupReporting( @@ -466,7 +463,6 @@ export default class Bind extends Extension { const coordinatorEndpoint = this.zigbee.firstCoordinatorEndpoint(); for (const bind of binds) { - /* istanbul ignore else */ if (bind.cluster.name in REPORT_CLUSTERS) { for (const endpoint of this.getSetupReportingEndpoints(bind, coordinatorEndpoint)) { const entity = `${this.zigbee.resolveEntity(endpoint.getDevice())!.name}/${endpoint.ID}`; @@ -477,7 +473,6 @@ export default class Bind extends Extension { const items = []; for (const c of REPORT_CLUSTERS[bind.cluster.name as ClusterName]!) { - /* istanbul ignore else */ if (!c.condition || (await c.condition(endpoint))) { const i = {...c}; delete i.condition; @@ -524,7 +519,6 @@ export default class Bind extends Extension { } for (const b of endpoint.binds) { - /* istanbul ignore else */ if (b.target === coordinator && !requiredClusters.includes(b.cluster.name) && b.cluster.name in REPORT_CLUSTERS) { boundClusters.push(b.cluster.name); } @@ -537,7 +531,6 @@ export default class Bind extends Extension { const items = []; for (const item of REPORT_CLUSTERS[cluster as ClusterName]!) { - /* istanbul ignore else */ if (!item.condition || (await item.condition(endpoint))) { const i = {...item}; delete i.condition; diff --git a/lib/extension/bridge.ts b/lib/extension/bridge.ts index 71fc6c9b..770d61b0 100644 --- a/lib/extension/bridge.ts +++ b/lib/extension/bridge.ts @@ -804,7 +804,7 @@ export default class Bridge extends Extension { let icon = device.options.icon ?? definitionIcon; if (icon) { - /* istanbul ignore next */ + /* v8 ignore next */ icon = icon.replace('${zigbeeModel}', utils.sanitizeImageParameter(device.zh.modelID ?? '')); icon = icon.replace('${model}', utils.sanitizeImageParameter(device.definition.model)); } diff --git a/lib/extension/extension.ts b/lib/extension/extension.ts index 3f09f3ed..53712987 100644 --- a/lib/extension/extension.ts +++ b/lib/extension/extension.ts @@ -43,7 +43,6 @@ abstract class Extension { /** * Is called once the extension has to start */ - /* istanbul ignore next */ async start(): Promise {} /** diff --git a/lib/extension/externalJS.ts b/lib/extension/externalJS.ts index 438659de..39342f89 100644 --- a/lib/extension/externalJS.ts +++ b/lib/extension/externalJS.ts @@ -64,7 +64,6 @@ export default abstract class ExternalJSExtension extends Extension { } for (const fileName of fs.readdirSync(this.basePath)) { - /* istanbul ignore else */ if (fileName.endsWith('.js')) { yield {name: fileName, code: this.getFileCode(fileName)}; } diff --git a/lib/extension/frontend.ts b/lib/extension/frontend.ts index ceb4d751..39a6f806 100644 --- a/lib/extension/frontend.ts +++ b/lib/extension/frontend.ts @@ -1,6 +1,8 @@ import type {IncomingMessage, Server, ServerResponse} from 'node:http'; import type {Socket} from 'node:net'; +import type {RequestHandler} from 'express-static-gzip'; + import assert from 'node:assert'; import {existsSync, readFileSync} from 'node:fs'; import {createServer} from 'node:http'; @@ -9,7 +11,7 @@ import {posix} from 'node:path'; import {parse} from 'node:url'; import bind from 'bind-decorator'; -import expressStaticGzip, {RequestHandler} from 'express-static-gzip'; +import expressStaticGzip from 'express-static-gzip'; import finalhandler from 'finalhandler'; import stringify from 'json-stable-stringify-without-jsonify'; import WebSocket from 'ws'; @@ -71,18 +73,19 @@ export default class Frontend extends Extension { } override async start(): Promise { - /* istanbul ignore next */ const options = { enableBrotli: true, // TODO: https://github.com/Koenkk/zigbee2mqtt/issues/24654 - enable compressed index serving when express-static-gzip is fixed. index: false, serveStatic: { index: 'index.html', + /* v8 ignore start */ setHeaders: (res: ServerResponse, path: string): void => { if (path.endsWith('index.html')) { res.setHeader('Cache-Control', 'no-store'); } }, + /* v8 ignore stop */ }, }; this.fileServer = expressStaticGzip(frontend.getPath(), options); @@ -172,7 +175,6 @@ export default class Frontend extends Extension { }); for (const [topic, payload] of Object.entries(this.mqtt.retainedMessages)) { - /* istanbul ignore else */ if (topic.startsWith(`${this.mqttBaseTopic}/`)) { ws.send( stringify({ @@ -188,9 +190,8 @@ export default class Frontend extends Extension { const payload = this.state.get(device); const lastSeen = settings.get().advanced.last_seen; - /* istanbul ignore if */ if (lastSeen !== 'disable') { - payload.last_seen = utils.formatDate(device.zh.lastSeen ?? 0, lastSeen); + payload.last_seen = utils.formatDate(device.zh.lastSeen ?? /* v8 ignore next */ 0, lastSeen); } if (device.zh.linkquality !== undefined) { @@ -202,14 +203,12 @@ export default class Frontend extends Extension { } @bind private onMQTTPublishMessage(data: eventdata.MQTTMessagePublished): void { - /* istanbul ignore else */ if (data.topic.startsWith(`${this.mqttBaseTopic}/`)) { // Send topic without base_topic const topic = data.topic.substring(this.mqttBaseTopic.length + 1); const payload = utils.parseJSON(data.payload, data.payload); for (const client of this.wss.clients) { - /* istanbul ignore else */ if (client.readyState === WebSocket.OPEN) { client.send(stringify({topic, payload})); } diff --git a/lib/extension/groups.ts b/lib/extension/groups.ts index 43446b29..a3b1457e 100644 --- a/lib/extension/groups.ts +++ b/lib/extension/groups.ts @@ -89,7 +89,6 @@ export default class Groups extends Extension { if (entity instanceof Device) { const endpoint = entity.endpoint(endpointName); - /* istanbul ignore else */ if (endpoint) { for (const group of groups) { if ( diff --git a/lib/extension/homeassistant.ts b/lib/extension/homeassistant.ts index 8e98e7d4..2d3c75dd 100644 --- a/lib/extension/homeassistant.ts +++ b/lib/extension/homeassistant.ts @@ -360,8 +360,7 @@ class Bridge { constructor(ieeeAdress: string, version: zh.CoordinatorVersion, discovery: DiscoveryEntry[]) { this.coordinatorIeeeAddress = ieeeAdress; this.coordinatorType = version.type; - /* istanbul ignore next */ - this.coordinatorFirmwareVersion = version.meta.revision ? `${version.meta.revision}` : ''; + this.coordinatorFirmwareVersion = version.meta.revision ? `${version.meta.revision}` : /* v8 ignore next */ ''; this.discoveryEntries = discovery; this.options = { @@ -718,11 +717,8 @@ export default class HomeAssistant extends Extension { }, }; - // istanbul ignore else if (tempCalibration.value_min != null) discoveryEntry.discovery_payload.min = tempCalibration.value_min; - // istanbul ignore else if (tempCalibration.value_max != null) discoveryEntry.discovery_payload.max = tempCalibration.value_max; - // istanbul ignore else if (tempCalibration.value_step != null) { discoveryEntry.discovery_payload.step = tempCalibration.value_step; } @@ -733,10 +729,10 @@ export default class HomeAssistant extends Extension { if (piHeatingDemand) { const discoveryEntry: DiscoveryEntry = { type: 'sensor', - object_id: endpoint ? /* istanbul ignore next */ `${piHeatingDemand.name}_${endpoint}` : `${piHeatingDemand.name}`, + object_id: endpoint ? /* v8 ignore next */ `${piHeatingDemand.name}_${endpoint}` : `${piHeatingDemand.name}`, mockProperties: [{property: piHeatingDemand.property, value: null}], discovery_payload: { - name: endpoint ? /* istanbul ignore next */ `${piHeatingDemand.label} ${endpoint}` : piHeatingDemand.label, + name: endpoint ? /* v8 ignore next */ `${piHeatingDemand.label} ${endpoint}` : piHeatingDemand.label, value_template: `{{ value_json.${piHeatingDemand.property} }}`, ...(piHeatingDemand.unit && {unit_of_measurement: piHeatingDemand.unit}), entity_category: 'diagnostic', @@ -816,7 +812,6 @@ export default class HomeAssistant extends Extension { const closingState = motorState.values.find((s) => COVER_CLOSING_LOOKUP.includes(s.toString().toLowerCase())); const stoppedState = motorState.values.find((s) => COVER_STOPPED_LOOKUP.includes(s.toString().toLowerCase())); - // istanbul ignore else if (openingState && closingState && stoppedState) { discoveryEntry.discovery_payload.state_opening = openingState; discoveryEntry.discovery_payload.state_closing = closingState; @@ -836,10 +831,11 @@ export default class HomeAssistant extends Extension { discoveryEntry.discovery_payload.state_stopped = 'STOP'; } - // istanbul ignore if + /* v8 ignore start */ if (!position && !tilt) { discoveryEntry.discovery_payload.optimistic = true; } + /* v8 ignore stop */ if (position) { discoveryEntry.discovery_payload = { @@ -879,7 +875,7 @@ export default class HomeAssistant extends Extension { }; const speed = (firstExpose as zhc.Fan).features.filter(isEnumExpose).find((e) => e.name === 'mode'); - // istanbul ignore else + if (speed) { // A fan entity in Home Assistant 2021.3 and above may have a speed, // controlled by a percentage from 1 to 100, and/or non-speed presets. @@ -943,7 +939,7 @@ export default class HomeAssistant extends Extension { mockProperties: [{property: firstExpose.property, value: null}], object_id: endpoint ? `switch_${firstExpose.name}_${endpoint}` : `switch_${firstExpose.name}`, discovery_payload: { - name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label, + name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, value_template: typeof firstExpose.value_on === 'boolean' ? `{% if value_json.${firstExpose.property} %}true{% else %}false{% endif %}` @@ -964,7 +960,7 @@ export default class HomeAssistant extends Extension { object_id: endpoint ? `${firstExpose.name}_${endpoint}` : `${firstExpose.name}`, mockProperties: [{property: firstExpose.property, value: null}], discovery_payload: { - name: endpoint ? `${firstExpose.label} ${endpoint}` : firstExpose.label, + name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, value_template: `{{ value_json.${firstExpose.property} }}`, payload_on: firstExpose.value_on, payload_off: firstExpose.value_off, @@ -1006,9 +1002,7 @@ export default class HomeAssistant extends Extension { delete discoveryEntry.discovery_payload.device_class; } - // istanbul ignore else if (firstExpose.value_min != null) discoveryEntry.discovery_payload.min = firstExpose.value_min; - // istanbul ignore else if (firstExpose.value_max != null) discoveryEntry.discovery_payload.max = firstExpose.value_max; discoveryEntries.push(discoveryEntry); @@ -1076,7 +1070,7 @@ export default class HomeAssistant extends Extension { object_id: firstExpose.property, mockProperties: [], discovery_payload: { - name: endpoint ? /* istanbul ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, + name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, state_topic: true, event_types: this.prepareActionEventTypes(firstExpose.values), value_template: this.actionValueTemplate, @@ -1100,7 +1094,7 @@ export default class HomeAssistant extends Extension { object_id: firstExpose.property, mockProperties: [{property: firstExpose.property, value: null}], discovery_payload: { - name: endpoint ? /* istanbul ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, + name: endpoint ? /* v8 ignore next */ `${firstExpose.label} ${endpoint}` : firstExpose.label, state_topic: false, command_topic_prefix: endpoint, command_topic: true, @@ -1137,7 +1131,6 @@ export default class HomeAssistant extends Extension { /** * Otherwise expose as SENSOR entity. */ - /* istanbul ignore else */ if (firstExpose.access & ACCESS_STATE) { discoveryEntries.push({ type: 'sensor', @@ -1189,9 +1182,10 @@ export default class HomeAssistant extends Extension { } break; } - /* istanbul ignore next */ + /* v8 ignore start */ default: throw new Error(`Unsupported exposes type: '${firstExpose.type}'`); + /* v8 ignore stop */ } // Exposes with category 'config' or 'diagnostic' are always added to the respective category. @@ -1249,14 +1243,16 @@ export default class HomeAssistant extends Extension { * zigbee2mqtt/mydevice/l1. */ const entity = this.zigbee.resolveEntity(data.entity.name)!; + if (entity.isDevice()) { for (const topic in this.getDiscovered(entity).messages) { const topicMatch = topic.match(this.discoveryRegexWoTopic); - // istanbul ignore if + /* v8 ignore start */ if (!topicMatch) { continue; } + /* v8 ignore stop */ const objectID = topicMatch[3]; const lightMatch = /^light_(.*)/.exec(objectID); @@ -1333,7 +1329,8 @@ export default class HomeAssistant extends Extension { private getConfigs(entity: Device | Group | Bridge): DiscoveryEntry[] { const isDevice = entity.isDevice(); const isGroup = entity.isGroup(); - /* istanbul ignore next */ + + /* v8 ignore next */ if (!entity || (isDevice && !entity.definition)) return []; let configs: DiscoveryEntry[] = []; @@ -1390,7 +1387,6 @@ export default class HomeAssistant extends Extension { }, }; - /* istanbul ignore else */ if (settings.get().advanced.last_seen.startsWith('ISO_8601')) { config.discovery_payload.device_class = 'timestamp'; } @@ -1491,7 +1487,6 @@ export default class HomeAssistant extends Extension { if (payload.state_topic === undefined || payload.state_topic) { payload.state_topic = stateTopic; } else { - /* istanbul ignore else */ if (payload.state_topic !== undefined) { delete payload.state_topic; } @@ -1744,7 +1739,6 @@ export default class HomeAssistant extends Extension { // Device was flagged to be excluded from homeassistant discovery clear = clear || Boolean(entity && entity.options.homeassistant !== undefined && !entity.options.homeassistant); - /* istanbul ignore else */ if (clear) { logger.debug(`Clearing outdated Home Assistant config '${data.topic}'`); await this.mqtt.publish(topic, '', {retain: true, qos: 1}, this.discoveryTopic, false, false); diff --git a/lib/extension/networkMap.ts b/lib/extension/networkMap.ts index f30d1781..d3eedaf4 100644 --- a/lib/extension/networkMap.ts +++ b/lib/extension/networkMap.ts @@ -217,7 +217,6 @@ export default class NetworkMap extends Extension { logger.debug((error as Error).stack!); } - /* istanbul ignore else */ if (includeRoutes) { try { const result = await requestWithRetry(async () => await device.zh.routingTable()); @@ -283,7 +282,6 @@ export default class NetworkMap extends Extension { if (neighbor.ieeeAddr === '0x0000000000000000') { const neighborDevice = this.zigbee.deviceByNetworkAddress(neighbor.networkAddress); - /* istanbul ignore else */ if (neighborDevice) { neighbor.ieeeAddr = neighborDevice.ieeeAddr; } @@ -305,7 +303,6 @@ export default class NetworkMap extends Extension { const routingTable = routingTables.get(device); - /* istanbul ignore else */ if (routingTable) { for (const entry of routingTable.table) { if (entry.status === 'ACTIVE' && entry.nextHop === neighbor.networkAddress) { diff --git a/lib/extension/publish.ts b/lib/extension/publish.ts index e31e235f..ac89ba53 100644 --- a/lib/extension/publish.ts +++ b/lib/extension/publish.ts @@ -199,7 +199,6 @@ export default class Publish extends Extension { } if (usedConverters[endpointOrGroupID] === undefined) usedConverters[endpointOrGroupID] = []; - /* istanbul ignore next */ // Match any key if the toZigbee converter defines no key. const converter = converters.find( (c) => diff --git a/lib/extension/receive.ts b/lib/extension/receive.ts index fc3e8474..a80cd5bd 100755 --- a/lib/extension/receive.ts +++ b/lib/extension/receive.ts @@ -102,7 +102,7 @@ export default class Receive extends Extension { } @bind async onDeviceMessage(data: eventdata.DeviceMessage): Promise { - /* istanbul ignore next */ + /* v8 ignore next */ if (!data.device) return; if (!data.device.definition || data.device.zh.interviewing) { @@ -171,10 +171,12 @@ export default class Receive extends Extension { if (converted) { payload = {...payload, ...converted}; } - } catch (error) /* istanbul ignore next */ { + /* v8 ignore start */ + } catch (error) { logger.error(`Exception while calling fromZigbee converter: ${(error as Error).message}}`); logger.debug((error as Error).stack!); } + /* v8 ignore stop */ } if (!utils.objectIsEmpty(payload)) { diff --git a/lib/model/device.ts b/lib/model/device.ts index 1b5f2811..18932d87 100644 --- a/lib/model/device.ts +++ b/lib/model/device.ts @@ -39,7 +39,6 @@ export default class Device { exposes(): zhc.Expose[] { assert(this.definition, 'Cannot retreive exposes before definition is resolved'); - /* istanbul ignore if */ if (typeof this.definition.exposes == 'function') { const options: KeyValue = this.options; return this.definition.exposes(this.zh, options); @@ -81,7 +80,6 @@ export default class Device { return undefined; } } else { - /* istanbul ignore next */ if (key !== 'default') { return undefined; } @@ -104,7 +102,7 @@ export default class Device { } } - /* istanbul ignore next */ + /* v8 ignore next */ return epName === 'default' ? undefined : epName; } @@ -123,7 +121,7 @@ export default class Device { isDevice(): this is Device { return true; } - /* istanbul ignore next */ + isGroup(): this is Group { return false; } diff --git a/lib/model/group.ts b/lib/model/group.ts index ad443ff7..2bcab78a 100644 --- a/lib/model/group.ts +++ b/lib/model/group.ts @@ -34,7 +34,6 @@ export default class Group { const definitions: zhc.Definition[] = []; for (const member of this.membersDevices()) { - /* istanbul ignore else */ if (member.definition) { definitions.push(member.definition); } diff --git a/lib/mqtt.ts b/lib/mqtt.ts index 49d6b7c4..ea8e94ff 100644 --- a/lib/mqtt.ts +++ b/lib/mqtt.ts @@ -191,7 +191,6 @@ export default class MQTT { this.eventBus.emitMQTTMessagePublished({topic, payload, options: {...defaultOptions, ...options}}); if (!this.isConnected()) { - /* istanbul ignore else */ if (!skipLog) { logger.error(`Not connected to MQTT server!`); logger.error(`Cannot send message: topic: '${topic}', payload: '${payload}`); @@ -213,7 +212,6 @@ export default class MQTT { try { await this.client.publishAsync(topic, payload, actualOptions); } catch (error) { - /* istanbul ignore else */ if (!skipLog) { logger.error(`MQTT server error: ${(error as Error).message}`); logger.error(`Could not send message: topic: '${topic}', payload: '${payload}`); diff --git a/lib/util/logger.ts b/lib/util/logger.ts index 53a3a1a0..25dca602 100644 --- a/lib/util/logger.ts +++ b/lib/util/logger.ts @@ -59,11 +59,9 @@ class Logger { // winston.config.syslog.levels sets 'warning' as 'red' format: winston.format.combine( winston.format.colorize({colors: {debug: 'blue', info: 'green', warning: 'yellow', error: 'red'}}), - winston.format.printf( - /* istanbul ignore next */ (info) => { - return `[${info.timestamp}] ${info.level}: \t${info.message}`; - }, - ), + winston.format.printf((info) => { + return `[${info.timestamp}] ${info.level}: \t${info.message}`; + }), ), }), ); @@ -77,10 +75,13 @@ class Logger { if (settings.get().advanced.log_symlink_current) { const current = settings.get().advanced.log_directory.replace('%TIMESTAMP%', 'current'); const actual = './' + timestamp; - /* istanbul ignore next */ + + /* v8 ignore start */ if (fs.existsSync(current)) { fs.unlinkSync(current); } + /* v8 ignore stop */ + fs.symlinkSync(actual, current); } @@ -88,11 +89,9 @@ class Logger { // NOTE: the initiation of the logger even when not added as transport tries to create the logging directory const transportFileOptions: winston.transports.FileTransportOptions = { filename: path.join(this.directory, logFilename), - format: winston.format.printf( - /* istanbul ignore next */ (info) => { - return `[${info.timestamp}] ${info.level}: \t${info.message}`; - }, - ), + format: winston.format.printf((info) => { + return `[${info.timestamp}] ${info.level}: \t${info.message}`; + }), }; if (settings.get().advanced.log_rotation) { @@ -106,7 +105,7 @@ class Logger { this.cleanup(); } - /* istanbul ignore next */ + /* v8 ignore start */ if (this.output.includes('syslog')) { logging += `, syslog`; // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unused-expressions @@ -125,6 +124,7 @@ class Logger { // @ts-expect-error untyped transport this.logger.add(new winston.transports.Syslog(options)); } + /* v8 ignore stop */ this.setDebugNamespaceIgnore(settings.get().advanced.log_debug_namespace_ignore); @@ -240,7 +240,7 @@ class Logger { // Workaround for https://github.com/winstonjs/winston/issues/1629. // https://github.com/Koenkk/zigbee2mqtt/pull/10905 - /* istanbul ignore next */ + /* v8 ignore start */ public async end(): Promise { this.logger.end(); @@ -259,6 +259,7 @@ class Logger { } }); } + /* v8 ignore stop */ } export default new Logger(); diff --git a/lib/util/settings.ts b/lib/util/settings.ts index 053091c1..69977134 100644 --- a/lib/util/settings.ts +++ b/lib/util/settings.ts @@ -87,7 +87,7 @@ export const defaults: RecursivePartial = { log_output: ['console', 'file'], log_directory: path.join(data.getPath(), 'log', '%TIMESTAMP%'), log_file: 'log.log', - log_level: /* istanbul ignore next */ process.env.DEBUG ? 'debug' : 'info', + log_level: /* v8 ignore next */ process.env.DEBUG ? 'debug' : 'info', log_namespaced_levels: {}, log_syslog: {}, log_debug_to_mqtt_frontend: false, @@ -353,7 +353,6 @@ function applyEnvironmentVariables(settings: Partial): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any setting[key as keyof Settings] = (envVariable.toLowerCase() === 'true') as any; } else { - /* istanbul ignore else */ if (type.indexOf('string') >= 0) { // eslint-disable-next-line @typescript-eslint/no-explicit-any setting[key as keyof Settings] = envVariable as any; diff --git a/lib/util/settingsMigration.ts b/lib/util/settingsMigration.ts index 101f79d6..99df9b54 100644 --- a/lib/util/settingsMigration.ts +++ b/lib/util/settingsMigration.ts @@ -54,14 +54,15 @@ function setValue(currentSettings: any, path: string[], value: unknown, createPa currentSettings[key] = value; } else { if (!currentSettings[key]) { - /* istanbul ignore else */ if (createPathIfNotExist) { currentSettings[key] = {}; + /* v8 ignore start */ } else { // invalid path // ignored in test since currently call is always guarded by get-validated path, so this is never reached return false; } + /* v8 ignore stop */ } currentSettings = currentSettings[key]; @@ -407,7 +408,6 @@ function migrateToThree( const changeToObject = (currentSettings: Partial, path: string[]): ReturnType => { const [validPath, previousValue] = getValue(currentSettings, path); - /* istanbul ignore else */ if (validPath) { if (typeof previousValue === 'boolean') { setValue(currentSettings, path, {enabled: previousValue}); @@ -456,8 +456,8 @@ export function migrateIfNecessary(): void { ); } - /* istanbul ignore next */ - const finalVersion = process.env.JEST_WORKER_ID ? settings.testing.CURRENT_VERSION : settings.CURRENT_VERSION; + /* v8 ignore next */ + const finalVersion = process.env.VITEST_WORKER_ID ? settings.testing.CURRENT_VERSION : settings.CURRENT_VERSION; // when same version as current, nothing left to do while (currentSettings.version !== finalVersion) { @@ -473,7 +473,6 @@ export function migrateIfNecessary(): void { backupSettings(currentSettings.version || 1); // each version should only bump to the next version so as to gradually migrate if necessary - /* istanbul ignore else */ if (currentSettings.version == undefined) { // migrating from 1 (`version` did not exist) to 2 migrationNotesFileName = 'migration-1-to-2.log'; @@ -518,13 +517,11 @@ export function migrateIfNecessary(): void { for (const customHandler of customHandlers) { const [validPath, previousValue, changed] = customHandler.execute(currentSettings); - /* istanbul ignore else */ if (validPath && changed && (!customHandler.noteIf || customHandler.noteIf(previousValue))) { migrationNotes.add(`[SPECIAL] ${customHandler.note}`); } } - /* istanbul ignore else */ if (migrationNotesFileName && migrationNotes.size > 0) { migrationNotes.add(`For more details, see https://github.com/Koenkk/zigbee2mqtt/discussions/24198`); const migrationNotesFilePath = data.joinPath(migrationNotesFileName); diff --git a/lib/util/utils.ts b/lib/util/utils.ts index e67a26a6..c605b008 100644 --- a/lib/util/utils.ts +++ b/lib/util/utils.ts @@ -61,10 +61,11 @@ async function getZigbee2MQTTVersion(includeCommitHash = true): Promise<{commitH if (err) { try { commitHash = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', '.hash'), 'utf-8'); + /* v8 ignore start */ } catch { - /* istanbul ignore next */ commitHash = 'unknown'; } + /* v8 ignore stop */ } else { commitHash = commit.shortHash; } @@ -369,7 +370,7 @@ function deviceNotCoordinator(device: zh.Device): boolean { return device.type !== 'Coordinator'; } -/* istanbul ignore next */ +/* v8 ignore next */ const noop = (): void => {}; export default { diff --git a/lib/zigbee.ts b/lib/zigbee.ts index e558d03a..ac248e7d 100644 --- a/lib/zigbee.ts +++ b/lib/zigbee.ts @@ -92,7 +92,7 @@ export default class Zigbee { }); this.herdsman.on('deviceInterview', async (data: ZHEvents.DeviceInterviewPayload) => { const device = this.resolveDevice(data.device.ieeeAddr); - /* istanbul ignore if */ if (!device) return; // Prevent potential race + /* v8 ignore next */ if (!device) return; // Prevent potential race await device.resolveDefinition(); const d = {device, status: data.status}; this.logDeviceInterview(d); @@ -100,7 +100,7 @@ export default class Zigbee { }); this.herdsman.on('deviceJoined', async (data: ZHEvents.DeviceJoinedPayload) => { const device = this.resolveDevice(data.device.ieeeAddr); - /* istanbul ignore if */ if (!device) return; // Prevent potential race + /* v8 ignore next */ if (!device) return; // Prevent potential race await device.resolveDefinition(); logger.info(`Device '${device.name}' joined`); this.eventBus.emitDeviceJoined({device}); @@ -309,7 +309,6 @@ export default class Zigbee { // First split the input token by the latest slash const match = ID.match(entityIDRegex); - /* istanbul ignore else */ if (match) { // Get the resulting IDs from the match entityName = match[1]; diff --git a/package.json b/package.json index 1c68906b..fa98d982 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,10 @@ "pretty:write": "prettier --write .", "pretty:check": "prettier --check .", "start": "node index.js", - "test-with-coverage": "jest test --silent --maxWorkers=50% --coverage", - "test": "jest test --silent --maxWorkers=50%", - "test-watch": "jest test --silent --maxWorkers=25% --watch" + "test": "vitest run --config ./test/vitest.config.mts", + "test:coverage": "vitest run --config ./test/vitest.config.mts --coverage", + "test:watch": "vitest watch --config ./test/vitest.config.mts", + "clean": "rimraf coverage dist tsconfig.tsbuildinfo" }, "author": "Koen Kanters", "license": "GPL-3.0", @@ -64,17 +65,12 @@ "zigbee2mqtt-frontend": "0.9.1" }, "devDependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-proposal-decorators": "^7.25.9", - "@babel/preset-env": "^7.26.0", - "@babel/preset-typescript": "^7.26.0", "@eslint/core": "^0.9.1", "@eslint/js": "^9.17.0", "@ianvs/prettier-plugin-sort-imports": "^4.4.0", "@types/eslint__js": "^8.42.3", "@types/finalhandler": "^1.2.3", "@types/humanize-duration": "^3.27.4", - "@types/jest": "^29.5.14", "@types/js-yaml": "^4.0.9", "@types/node": "^22.10.2", "@types/object-assign-deep": "^0.4.3", @@ -82,34 +78,20 @@ "@types/sd-notify": "^2.8.2", "@types/serve-static": "^1.15.7", "@types/ws": "8.5.13", - "babel-jest": "^29.7.0", + "@vitest/coverage-v8": "^2.1.8", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", - "jest": "^29.7.0", "prettier": "^3.4.2", "tmp": "^0.2.3", "typescript": "^5.7.2", - "typescript-eslint": "^8.18.0" + "typescript-eslint": "^8.18.0", + "vitest": "^2.1.8" }, "pnpm": { "overrides": { "zigbee-herdsman": "$zigbee-herdsman" } }, - "jest": { - "coverageThreshold": { - "global": { - "branches": 100, - "functions": 100, - "lines": 100, - "statements": 100 - } - }, - "collectCoverageFrom": [ - "lib/**/*.js", - "lib/**/*.ts" - ] - }, "bin": { "zigbee2mqtt": "cli.js" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec457f57..686e3acc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,18 +91,6 @@ importers: specifier: ^2.8.0 version: 2.8.0 devDependencies: - '@babel/core': - specifier: ^7.26.0 - version: 7.26.0 - '@babel/plugin-proposal-decorators': - specifier: ^7.25.9 - version: 7.25.9(@babel/core@7.26.0) - '@babel/preset-env': - specifier: ^7.26.0 - version: 7.26.0(@babel/core@7.26.0) - '@babel/preset-typescript': - specifier: ^7.26.0 - version: 7.26.0(@babel/core@7.26.0) '@eslint/core': specifier: ^0.9.1 version: 0.9.1 @@ -121,9 +109,6 @@ importers: '@types/humanize-duration': specifier: ^3.27.4 version: 3.27.4 - '@types/jest': - specifier: ^29.5.14 - version: 29.5.14 '@types/js-yaml': specifier: ^4.0.9 version: 4.0.9 @@ -145,18 +130,15 @@ importers: '@types/ws': specifier: 8.5.13 version: 8.5.13 - babel-jest: - specifier: ^29.7.0 - version: 29.7.0(@babel/core@7.26.0) + '@vitest/coverage-v8': + specifier: ^2.1.8 + version: 2.1.8(vitest@2.1.8(@types/node@22.10.2)) eslint: specifier: ^9.17.0 version: 9.17.0 eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@9.17.0) - jest: - specifier: ^29.7.0 - version: 29.7.0(@types/node@22.10.2) prettier: specifier: ^3.4.2 version: 3.4.2 @@ -169,6 +151,9 @@ importers: typescript-eslint: specifier: ^8.18.0 version: 8.18.0(eslint@9.17.0)(typescript@5.7.2) + vitest: + specifier: ^2.1.8 + version: 2.1.8(@types/node@22.10.2) packages: @@ -180,81 +165,10 @@ packages: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.3': - resolution: {integrity: sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.26.0': - resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.26.3': resolution: {integrity: sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.25.9': - resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.25.9': - resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.25.9': - resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-create-regexp-features-plugin@7.26.3': - resolution: {integrity: sha512-G7ZRb40uUgdKOQqPLjfD12ZmGA54PzqDFUv2BKImnC9QIfGhIHKvVML0oN8IUiDq4iRqpq74ABpvOaerfWdong==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-define-polyfill-provider@0.6.3': - resolution: {integrity: sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - - '@babel/helper-member-expression-to-functions@7.25.9': - resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.25.9': - resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.25.9': - resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-remap-async-to-generator@7.25.9': - resolution: {integrity: sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-replace-supers@7.25.9': - resolution: {integrity: sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': - resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.25.9': resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} engines: {node: '>=6.9.0'} @@ -263,497 +177,11 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-wrap-function@7.25.9': - resolution: {integrity: sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.26.0': - resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} - engines: {node: '>=6.9.0'} - '@babel/parser@7.26.3': resolution: {integrity: sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9': - resolution: {integrity: sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9': - resolution: {integrity: sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9': - resolution: {integrity: sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9': - resolution: {integrity: sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.13.0 - - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9': - resolution: {integrity: sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-proposal-decorators@7.25.9': - resolution: {integrity: sha512-smkNLL/O1ezy9Nhy4CNosc4Va+1wo5w4gzSZeLe6y6dM4mmHfYOCPolXQPHQxonZCF+ZyebxN9vqOolkYrSn5g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': - resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-async-generators@7.8.4': - resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-bigint@7.8.3': - resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-properties@7.12.13': - resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-decorators@7.25.9': - resolution: {integrity: sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-assertions@7.26.0': - resolution: {integrity: sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-attributes@7.26.0': - resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-import-meta@7.10.4': - resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-json-strings@7.8.3': - resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-jsx@7.25.9': - resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-typescript@7.25.9': - resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-syntax-unicode-sets-regex@7.18.6': - resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-arrow-functions@7.25.9': - resolution: {integrity: sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-async-generator-functions@7.25.9': - resolution: {integrity: sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-async-to-generator@7.25.9': - resolution: {integrity: sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-block-scoped-functions@7.25.9': - resolution: {integrity: sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-block-scoping@7.25.9': - resolution: {integrity: sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-class-properties@7.25.9': - resolution: {integrity: sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-class-static-block@7.26.0': - resolution: {integrity: sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.12.0 - - '@babel/plugin-transform-classes@7.25.9': - resolution: {integrity: sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-computed-properties@7.25.9': - resolution: {integrity: sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-destructuring@7.25.9': - resolution: {integrity: sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-dotall-regex@7.25.9': - resolution: {integrity: sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-duplicate-keys@7.25.9': - resolution: {integrity: sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9': - resolution: {integrity: sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-dynamic-import@7.25.9': - resolution: {integrity: sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-exponentiation-operator@7.26.3': - resolution: {integrity: sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-export-namespace-from@7.25.9': - resolution: {integrity: sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-for-of@7.25.9': - resolution: {integrity: sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-function-name@7.25.9': - resolution: {integrity: sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-json-strings@7.25.9': - resolution: {integrity: sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-literals@7.25.9': - resolution: {integrity: sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-logical-assignment-operators@7.25.9': - resolution: {integrity: sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-member-expression-literals@7.25.9': - resolution: {integrity: sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-amd@7.25.9': - resolution: {integrity: sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-commonjs@7.26.3': - resolution: {integrity: sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-systemjs@7.25.9': - resolution: {integrity: sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-modules-umd@7.25.9': - resolution: {integrity: sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-named-capturing-groups-regex@7.25.9': - resolution: {integrity: sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-new-target@7.25.9': - resolution: {integrity: sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-nullish-coalescing-operator@7.25.9': - resolution: {integrity: sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-numeric-separator@7.25.9': - resolution: {integrity: sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-object-rest-spread@7.25.9': - resolution: {integrity: sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-object-super@7.25.9': - resolution: {integrity: sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-optional-catch-binding@7.25.9': - resolution: {integrity: sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-optional-chaining@7.25.9': - resolution: {integrity: sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-parameters@7.25.9': - resolution: {integrity: sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-private-methods@7.25.9': - resolution: {integrity: sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-private-property-in-object@7.25.9': - resolution: {integrity: sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-property-literals@7.25.9': - resolution: {integrity: sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-regenerator@7.25.9': - resolution: {integrity: sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-regexp-modifiers@7.26.0': - resolution: {integrity: sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/plugin-transform-reserved-words@7.25.9': - resolution: {integrity: sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-shorthand-properties@7.25.9': - resolution: {integrity: sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-spread@7.25.9': - resolution: {integrity: sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-sticky-regex@7.25.9': - resolution: {integrity: sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-template-literals@7.25.9': - resolution: {integrity: sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typeof-symbol@7.25.9': - resolution: {integrity: sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.26.3': - resolution: {integrity: sha512-6+5hpdr6mETwSKjmJUdYw0EIkATiQhnELWlE3kJFBwSg/BGIVwVaVbX+gOXBCdc7Ln1RXZxyWGecIXhUfnl7oA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-escapes@7.25.9': - resolution: {integrity: sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-property-regex@7.25.9': - resolution: {integrity: sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-regex@7.25.9': - resolution: {integrity: sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-unicode-sets-regex@7.25.9': - resolution: {integrity: sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/preset-env@7.26.0': - resolution: {integrity: sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/preset-modules@0.1.6-no-external-plugins': - resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} - peerDependencies: - '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - - '@babel/preset-typescript@7.26.0': - resolution: {integrity: sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/runtime@7.26.0': resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} engines: {node: '>=6.9.0'} @@ -780,6 +208,144 @@ packages: '@dabh/diagnostics@2.0.3': resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -847,80 +413,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@istanbuljs/load-nyc-config@1.1.0': - resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} - engines: {node: '>=8'} - '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jest/console@29.7.0': - resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/core@29.7.0': - resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/environment@29.7.0': - resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect-utils@29.7.0': - resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/expect@29.7.0': - resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/fake-timers@29.7.0': - resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/globals@29.7.0': - resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/reporters@29.7.0': - resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - '@jest/schemas@29.6.3': - resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/source-map@29.6.3': - resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-result@29.7.0': - resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/test-sequencer@29.7.0': - resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/transform@29.7.0': - resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - '@jest/types@29.6.3': - resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -954,6 +450,105 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@rollup/rollup-android-arm-eabi@4.29.1': + resolution: {integrity: sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.29.1': + resolution: {integrity: sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.29.1': + resolution: {integrity: sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.29.1': + resolution: {integrity: sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.29.1': + resolution: {integrity: sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.29.1': + resolution: {integrity: sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.29.1': + resolution: {integrity: sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.29.1': + resolution: {integrity: sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.29.1': + resolution: {integrity: sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.29.1': + resolution: {integrity: sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.29.1': + resolution: {integrity: sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': + resolution: {integrity: sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.29.1': + resolution: {integrity: sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.29.1': + resolution: {integrity: sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.29.1': + resolution: {integrity: sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.29.1': + resolution: {integrity: sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.29.1': + resolution: {integrity: sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.29.1': + resolution: {integrity: sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.29.1': + resolution: {integrity: sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==} + cpu: [x64] + os: [win32] + '@serialport/bindings-cpp@12.0.1': resolution: {integrity: sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==} engines: {node: '>=16.0.0'} @@ -978,27 +573,6 @@ packages: resolution: {integrity: sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==} engines: {node: '>=12.0.0'} - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - - '@sinonjs/commons@3.0.1': - resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - - '@sinonjs/fake-timers@10.3.0': - resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} @@ -1011,27 +585,12 @@ packages: '@types/finalhandler@1.2.3': resolution: {integrity: sha512-I+Ba0JZEiuSr8LLjVmBhvLBEN8KG9GSITNXWwPCLeAvZj/k5pXEdOBEvnEEIgA038eeaauJ3BPxbuxeFBsqqUw==} - '@types/graceful-fs@4.1.9': - resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} - '@types/http-errors@2.0.4': resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} '@types/humanize-duration@3.27.4': resolution: {integrity: sha512-yaf7kan2Sq0goxpbcwTQ+8E9RP6HutFBPv74T/IA/ojcHKhuKVlk2YFYyHhWZeLvZPzzLE3aatuQB4h0iqyyUA==} - '@types/istanbul-lib-coverage@2.0.6': - resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} - - '@types/istanbul-lib-report@3.0.3': - resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} - - '@types/istanbul-reports@3.0.4': - resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} - - '@types/jest@29.5.14': - resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - '@types/js-yaml@4.0.9': resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} @@ -1059,21 +618,12 @@ packages: '@types/serve-static@1.15.7': resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} - '@types/stack-utils@2.0.3': - resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} '@types/ws@8.5.13': resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} - '@types/yargs-parser@21.0.3': - resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@typescript-eslint/eslint-plugin@8.18.0': resolution: {integrity: sha512-NR2yS7qUqCL7AIxdJUQf2MKKNDVNaig/dEB0GBLU7D+ZdHgK1NoH/3wsgO3OnPVipn51tG3MAwaODEGil70WEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1121,6 +671,44 @@ packages: resolution: {integrity: sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/coverage-v8@2.1.8': + resolution: {integrity: sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==} + peerDependencies: + '@vitest/browser': 2.1.8 + vitest: 2.1.8 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@2.1.8': + resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} + + '@vitest/mocker@2.1.8': + resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@2.1.8': + resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} + + '@vitest/runner@2.1.8': + resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} + + '@vitest/snapshot@2.1.8': + resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} + + '@vitest/spy@2.1.8': + resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} + + '@vitest/utils@2.1.8': + resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -1141,10 +729,6 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1157,67 +741,20 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - babel-jest@29.7.0: - resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.8.0 - - babel-plugin-istanbul@6.1.1: - resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} - engines: {node: '>=8'} - - babel-plugin-jest-hoist@29.6.3: - resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - babel-plugin-polyfill-corejs2@0.4.12: - resolution: {integrity: sha512-CPWT6BwvhrTO2d8QVorhTCQw9Y43zOu7G9HigcfxvepOU6b8o3tcWad6oVgZIsZCTt42FFv97aA7ZJsbM4+8og==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - - babel-plugin-polyfill-corejs3@0.10.6: - resolution: {integrity: sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - - babel-plugin-polyfill-regenerator@0.6.3: - resolution: {integrity: sha512-LiWSbl4CRSIa5x/JAU6jZiG9eit9w6mz+yVMFwDE83LAWvt0AfGBoZ7HS/mkhrKuh2ZlzfVZYKoLjXdqw6Yt7Q==} - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - - babel-preset-current-node-syntax@1.1.0: - resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} - peerDependencies: - '@babel/core': ^7.0.0 - - babel-preset-jest@29.6.3: - resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@babel/core': ^7.0.0 - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1246,14 +783,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.3: - resolution: {integrity: sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - bser@2.1.1: - resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} - buffer-crc32@1.0.0: resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} engines: {node: '>=8.0.0'} @@ -1264,46 +793,25 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - caniuse-lite@1.0.30001688: - resolution: {integrity: sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==} + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} + engines: {node: '>=12'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - char-regex@1.0.2: - resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} - engines: {node: '>=10'} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cjs-module-lexer@1.4.1: - resolution: {integrity: sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - co@4.6.0: - resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} - engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - - collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -1337,20 +845,9 @@ packages: resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} engines: {'0': node >= 6.0} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - core-js-compat@3.39.0: - resolution: {integrity: sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==} - core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - create-jest@29.7.0: - resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1385,21 +882,13 @@ packages: supports-color: optional: true - dedent@1.5.3: - resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} - peerDependencies: - babel-plugin-macros: ^3.1.0 - peerDependenciesMeta: - babel-plugin-macros: - optional: true + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -1408,14 +897,6 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dns-packet@5.6.1: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} @@ -1426,13 +907,6 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.73: - resolution: {integrity: sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==} - - emittery@0.13.1: - resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} - engines: {node: '>=12'} - emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1450,20 +924,17 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1500,11 +971,6 @@ packages: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esquery@1.6.0: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} @@ -1517,6 +983,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1533,17 +1002,9 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - exit@0.1.2: - resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} - engines: {node: '>= 0.8.0'} - - expect@29.7.0: - resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} express-static-gzip@2.2.0: resolution: {integrity: sha512-4ZQ0pHX0CAauxmzry2/8XFLM6aZA4NBvg9QezSlsEO1zLnl7vMFa48/WIcjzdfOiEUS4S1npPPKP2NHHYAp6qg==} @@ -1571,9 +1032,6 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - fb-watchman@2.0.2: - resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fecha@4.2.3: resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} @@ -1592,10 +1050,6 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -1618,33 +1072,11 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-package-type@0.1.0: - resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} - engines: {node: '>=8.0.0'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - git-last-commit@1.0.1: resolution: {integrity: sha512-FDSgeMqa7GnJDxt/q0AbrxbfeTyxp4ImxEw1e4nw6NUHA5FMhFUq33dTXI4Xdgcj1VQ1q5QLWF6WxFrJ8KCBOg==} @@ -1656,15 +1088,15 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + glob@11.0.0: resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} engines: {node: 20 || >=22} hasBin: true - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1677,9 +1109,6 @@ packages: resolution: {integrity: sha512-mTCC51QFadK75MvAhrL5nPVIP291NjML1guo10Sa7Yj04tJU4V++Vgm780NIddg9etQD9D8FM67hFGqM8EE2HQ==} engines: {node: '>= 0.2.5'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} @@ -1687,10 +1116,6 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -1701,10 +1126,6 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - humanize-duration@3.32.1: resolution: {integrity: sha512-inh5wue5XdfObhu/IGEMiA1nUXigSGcaKNemcbLRKa7jXYGDZXr3LoT9pTIzq2hPEbld7w/qv9h+ikWGz8fL1g==} @@ -1726,32 +1147,16 @@ packages: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} - import-local@3.2.0: - resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} - engines: {node: '>=8'} - hasBin: true - imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-core-module@2.16.0: - resolution: {integrity: sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==} - engines: {node: '>= 0.4'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1760,10 +1165,6 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-fn@2.1.0: - resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} - engines: {node: '>=6'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1786,178 +1187,35 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - istanbul-lib-instrument@5.2.1: - resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} - istanbul-lib-source-maps@4.0.1: - resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} istanbul-reports@3.1.7: resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} engines: {node: '>=8'} + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jackspeak@4.0.2: resolution: {integrity: sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==} engines: {node: 20 || >=22} - jest-changed-files@29.7.0: - resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-circus@29.7.0: - resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-cli@29.7.0: - resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - - jest-config@29.7.0: - resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - peerDependencies: - '@types/node': '*' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - ts-node: - optional: true - - jest-diff@29.7.0: - resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-docblock@29.7.0: - resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-each@29.7.0: - resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-environment-node@29.7.0: - resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-get-type@29.6.3: - resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-haste-map@29.7.0: - resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-leak-detector@29.7.0: - resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-matcher-utils@29.7.0: - resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-message-util@29.7.0: - resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-mock@29.7.0: - resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-regex-util@29.6.3: - resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve-dependencies@29.7.0: - resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-resolve@29.7.0: - resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runner@29.7.0: - resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-runtime@29.7.0: - resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-snapshot@29.7.0: - resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-util@29.7.0: - resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-validate@29.7.0: - resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-watcher@29.7.0: - resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest-worker@29.7.0: - resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - - jest@29.7.0: - resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - js-sdsl@4.3.0: resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1966,9 +1224,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1978,28 +1233,15 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - kuler@2.0.0: resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==} - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2007,20 +1249,10 @@ packages: lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.debounce@4.0.8: - resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -2028,6 +1260,9 @@ packages: resolution: {integrity: sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==} engines: {node: '>= 12.0.0'} + loupe@3.1.2: + resolution: {integrity: sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2035,19 +1270,16 @@ packages: resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} engines: {node: 20 || >=22} - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + magicast@0.3.5: + resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -2061,10 +1293,6 @@ packages: engines: {node: '>=4'} hasBin: true - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - minimatch@10.0.1: resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} engines: {node: 20 || >=22} @@ -2114,6 +1342,11 @@ packages: nan@2.22.0: resolution: {integrity: sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==} + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2124,20 +1357,6 @@ packages: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} hasBin: true - node-int64@0.4.0: - resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - number-allocator@1.0.14: resolution: {integrity: sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==} @@ -2149,40 +1368,21 @@ packages: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - one-time@1.0.0: resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -2193,10 +1393,6 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -2205,21 +1401,25 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-scurry@2.0.0: resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} engines: {node: 20 || >=22} + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2227,13 +1427,9 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} + postcss@8.4.49: + resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -2244,10 +1440,6 @@ packages: engines: {node: '>=14'} hasBin: true - pretty-format@29.7.0: - resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -2255,17 +1447,10 @@ packages: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} - queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -2273,9 +1458,6 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -2287,61 +1469,20 @@ packages: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - regenerate-unicode-properties@10.2.0: - resolution: {integrity: sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==} - engines: {node: '>=4'} - - regenerate@1.4.2: - resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} - regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - regenerator-transform@0.15.2: - resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} - - regexpu-core@6.2.0: - resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} - engines: {node: '>=4'} - - regjsgen@0.8.0: - resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - - regjsparser@0.12.0: - resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} - hasBin: true - reinterval@1.1.0: resolution: {integrity: sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==} - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - resolve-cwd@3.0.0: - resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} - engines: {node: '>=8'} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve.exports@2.0.3: - resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} - engines: {node: '>=10'} - - resolve@1.22.9: - resolution: {integrity: sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==} - hasBin: true - reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -2354,6 +1495,11 @@ packages: engines: {node: 20 || >=22} hasBin: true + rollup@4.29.1: + resolution: {integrity: sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2375,10 +1521,6 @@ packages: engines: {node: '>=8.0.0'} os: [linux, darwin, win32] - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - semver@7.6.3: resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} engines: {node: '>=10'} @@ -2406,8 +1548,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} @@ -2416,18 +1558,12 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - slip@1.0.2: resolution: {integrity: sha512-XrcHe3NAcyD3wO+O4I13RcS4/3AF+S9RvGNj9JhJeS02HyImwD2E3QWLrmn9hBfL+fB6yapagwxRkeyYzhk98g==} - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -2440,23 +1576,18 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stack-trace@0.0.10: resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - string-length@4.0.2: - resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} - engines: {node: '>=10'} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -2480,14 +1611,6 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-bom@4.0.0: - resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} - engines: {node: '>=8'} - - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2496,17 +1619,9 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} + test-exclude@7.0.1: + resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} + engines: {node: '>=18'} text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} @@ -2518,13 +1633,28 @@ packages: thunky@1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.1: + resolution: {integrity: sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@1.2.0: + resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + tmp@0.2.3: resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} engines: {node: '>=14.14'} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2550,14 +1680,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-detect@4.0.8: - resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} - engines: {node: '>=4'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - typedarray@0.0.6: resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} @@ -2576,22 +1698,6 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} - unicode-canonical-property-names-ecmascript@2.0.1: - resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} - engines: {node: '>=4'} - - unicode-match-property-ecmascript@2.0.0: - resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} - engines: {node: '>=4'} - - unicode-match-property-value-ecmascript@2.2.0: - resolution: {integrity: sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==} - engines: {node: '>=4'} - - unicode-property-aliases-ecmascript@2.1.0: - resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} - engines: {node: '>=4'} - unix-dgram@2.0.6: resolution: {integrity: sha512-AURroAsb73BZ6CdAyMrTk/hYKNj3DuYYEuOaB8bYMOHGKupRNScw90Q5C71tWJc3uE7dIeXRyuwN0xLLq3vDTg==} engines: {node: '>=0.10.48'} @@ -2600,30 +1706,83 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - update-browserslist-db@1.1.1: - resolution: {integrity: sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - v8-to-istanbul@9.3.0: - resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} - engines: {node: '>=10.12.0'} + vite-node@2.1.8: + resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + vite@5.4.11: + resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vitest@2.1.8: + resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.8 + '@vitest/ui': 2.1.8 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + winston-syslog@2.7.1: resolution: {integrity: sha512-MrU5gSwXgEbHwz5wFhn1rZtSCbRcP1PEB8zU49tgsfjQ63EjIVAkQpXmMocdbTLnTJ2cW3gLW6gmoOOOhyJZgg==} engines: {node: '>= 8'} @@ -2659,13 +1818,6 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - write-file-atomic@4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -2678,21 +1830,6 @@ packages: utf-8-validate: optional: true - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2720,28 +1857,6 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.3': {} - - '@babel/core@7.26.0': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.3 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.3 - '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 - convert-source-map: 2.0.0 - debug: 4.4.0 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/generator@7.26.3': dependencies: '@babel/parser': 7.26.3 @@ -2750,698 +1865,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.25.9': - dependencies: - '@babel/types': 7.26.3 - - '@babel/helper-compilation-targets@7.25.9': - dependencies: - '@babel/compat-data': 7.26.3 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.3 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/traverse': 7.26.4 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-create-regexp-features-plugin@7.26.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - regexpu-core: 6.2.0 - semver: 6.3.1 - - '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - debug: 4.4.0 - lodash.debounce: 4.0.8 - resolve: 1.22.9 - transitivePeerDependencies: - - supports-color - - '@babel/helper-member-expression-to-functions@7.25.9': - dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.25.9': - dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.25.9': - dependencies: - '@babel/types': 7.26.3 - - '@babel/helper-plugin-utils@7.25.9': {} - - '@babel/helper-remap-async-to-generator@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-wrap-function': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/helper-replace-supers@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': - dependencies: - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 - transitivePeerDependencies: - - supports-color - '@babel/helper-string-parser@7.25.9': {} '@babel/helper-validator-identifier@7.25.9': {} - '@babel/helper-validator-option@7.25.9': {} - - '@babel/helper-wrap-function@7.25.9': - dependencies: - '@babel/template': 7.25.9 - '@babel/traverse': 7.26.4 - '@babel/types': 7.26.3 - transitivePeerDependencies: - - supports-color - - '@babel/helpers@7.26.0': - dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.3 - '@babel/parser@7.26.3': dependencies: '@babel/types': 7.26.3 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-proposal-decorators@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-syntax-decorators': 7.25.9(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-decorators@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-import-assertions@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-arrow-functions@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-async-generator-functions@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-async-to-generator@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-remap-async-to-generator': 7.25.9(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-block-scoped-functions@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-block-scoping@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-class-properties@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-class-static-block@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-classes@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) - '@babel/traverse': 7.26.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-computed-properties@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/template': 7.25.9 - - '@babel/plugin-transform-destructuring@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-dotall-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-duplicate-keys@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-dynamic-import@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-exponentiation-operator@7.26.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-export-namespace-from@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-for-of@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-function-name@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-json-strings@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-literals@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-logical-assignment-operators@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-member-expression-literals@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-modules-amd@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-commonjs@7.26.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-systemjs@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.4 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-modules-umd@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-named-capturing-groups-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-new-target@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-nullish-coalescing-operator@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-numeric-separator@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-object-rest-spread@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) - - '@babel/plugin-transform-object-super@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-replace-supers': 7.25.9(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-optional-catch-binding@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-optional-chaining@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-parameters@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-private-methods@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-private-property-in-object@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-property-literals@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-regenerator@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - regenerator-transform: 0.15.2 - - '@babel/plugin-transform-regexp-modifiers@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-reserved-words@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-shorthand-properties@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-spread@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-sticky-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-template-literals@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-typeof-symbol@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-typescript@7.26.3(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-unicode-escapes@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-unicode-property-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-unicode-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/plugin-transform-unicode-sets-regex@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-create-regexp-features-plugin': 7.26.3(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.25.9 - - '@babel/preset-env@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/compat-data': 7.26.3 - '@babel/core': 7.26.0 - '@babel/helper-compilation-targets': 7.25.9 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-validator-option': 7.25.9 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.26.0) - '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.26.0) - '@babel/plugin-transform-arrow-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-async-generator-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-async-to-generator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-block-scoped-functions': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-block-scoping': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-class-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-class-static-block': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-transform-classes': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-computed-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-destructuring': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-dotall-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-duplicate-keys': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-dynamic-import': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-exponentiation-operator': 7.26.3(@babel/core@7.26.0) - '@babel/plugin-transform-export-namespace-from': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-for-of': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-function-name': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-json-strings': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-logical-assignment-operators': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-member-expression-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-amd': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) - '@babel/plugin-transform-modules-systemjs': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-umd': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-named-capturing-groups-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-new-target': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-nullish-coalescing-operator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-numeric-separator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-object-rest-spread': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-object-super': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-optional-catch-binding': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-optional-chaining': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-parameters': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-private-methods': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-private-property-in-object': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-property-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-regenerator': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-regexp-modifiers': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-transform-reserved-words': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-shorthand-properties': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-spread': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-sticky-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-template-literals': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-typeof-symbol': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-escapes': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-property-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-regex': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-unicode-sets-regex': 7.25.9(@babel/core@7.26.0) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.26.0) - babel-plugin-polyfill-corejs2: 0.4.12(@babel/core@7.26.0) - babel-plugin-polyfill-corejs3: 0.10.6(@babel/core@7.26.0) - babel-plugin-polyfill-regenerator: 0.6.3(@babel/core@7.26.0) - core-js-compat: 3.39.0 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/types': 7.26.3 - esutils: 2.0.3 - - '@babel/preset-typescript@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.25.9 - '@babel/helper-validator-option': 7.25.9 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-transform-modules-commonjs': 7.26.3(@babel/core@7.26.0) - '@babel/plugin-transform-typescript': 7.26.3(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - '@babel/runtime@7.26.0': dependencies: regenerator-runtime: 0.14.1 @@ -3479,6 +1910,75 @@ snapshots: enabled: 2.0.0 kuler: 2.0.0 + '@esbuild/aix-ppc64@0.21.5': + optional: true + + '@esbuild/android-arm64@0.21.5': + optional: true + + '@esbuild/android-arm@0.21.5': + optional: true + + '@esbuild/android-x64@0.21.5': + optional: true + + '@esbuild/darwin-arm64@0.21.5': + optional: true + + '@esbuild/darwin-x64@0.21.5': + optional: true + + '@esbuild/freebsd-arm64@0.21.5': + optional: true + + '@esbuild/freebsd-x64@0.21.5': + optional: true + + '@esbuild/linux-arm64@0.21.5': + optional: true + + '@esbuild/linux-arm@0.21.5': + optional: true + + '@esbuild/linux-ia32@0.21.5': + optional: true + + '@esbuild/linux-loong64@0.21.5': + optional: true + + '@esbuild/linux-mips64el@0.21.5': + optional: true + + '@esbuild/linux-ppc64@0.21.5': + optional: true + + '@esbuild/linux-riscv64@0.21.5': + optional: true + + '@esbuild/linux-s390x@0.21.5': + optional: true + + '@esbuild/linux-x64@0.21.5': + optional: true + + '@esbuild/netbsd-x64@0.21.5': + optional: true + + '@esbuild/openbsd-x64@0.21.5': + optional: true + + '@esbuild/sunos-x64@0.21.5': + optional: true + + '@esbuild/win32-arm64@0.21.5': + optional: true + + '@esbuild/win32-ia32@0.21.5': + optional: true + + '@esbuild/win32-x64@0.21.5': + optional: true + '@eslint-community/eslint-utils@4.4.1(eslint@9.17.0)': dependencies: eslint: 9.17.0 @@ -3553,178 +2053,8 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@istanbuljs/load-nyc-config@1.1.0': - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - '@istanbuljs/schema@0.1.3': {} - '@jest/console@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - chalk: 4.1.2 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - - '@jest/core@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.10.2) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - - '@jest/environment@29.7.0': - dependencies: - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - jest-mock: 29.7.0 - - '@jest/expect-utils@29.7.0': - dependencies: - jest-get-type: 29.6.3 - - '@jest/expect@29.7.0': - dependencies: - expect: 29.7.0 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/fake-timers@29.7.0': - dependencies: - '@jest/types': 29.6.3 - '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.10.2 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - '@jest/globals@29.7.0': - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/types': 29.6.3 - jest-mock: 29.7.0 - transitivePeerDependencies: - - supports-color - - '@jest/reporters@29.7.0': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.10.2 - chalk: 4.1.2 - collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.7 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - jest-worker: 29.7.0 - slash: 3.0.0 - string-length: 4.0.2 - strip-ansi: 6.0.1 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - - '@jest/schemas@29.6.3': - dependencies: - '@sinclair/typebox': 0.27.8 - - '@jest/source-map@29.6.3': - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - callsites: 3.1.0 - graceful-fs: 4.2.11 - - '@jest/test-result@29.7.0': - dependencies: - '@jest/console': 29.7.0 - '@jest/types': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.2 - - '@jest/test-sequencer@29.7.0': - dependencies: - '@jest/test-result': 29.7.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - slash: 3.0.0 - - '@jest/transform@29.7.0': - dependencies: - '@babel/core': 7.26.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.8 - pirates: 4.0.6 - slash: 3.0.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - - '@jest/types@29.6.3': - dependencies: - '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 22.10.2 - '@types/yargs': 17.0.33 - chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -3756,6 +2086,66 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@pkgjs/parseargs@0.11.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.29.1': + optional: true + + '@rollup/rollup-android-arm64@4.29.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.29.1': + optional: true + + '@rollup/rollup-darwin-x64@4.29.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.29.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.29.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.29.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.29.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.29.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.29.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.29.1': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.29.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.29.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.29.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.29.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.29.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.29.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.29.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.29.1': + optional: true + '@serialport/bindings-cpp@12.0.1': dependencies: '@serialport/bindings-interface': 1.2.2 @@ -3783,37 +2173,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@sinclair/typebox@0.27.8': {} - - '@sinonjs/commons@3.0.1': - dependencies: - type-detect: 4.0.8 - - '@sinonjs/fake-timers@10.3.0': - dependencies: - '@sinonjs/commons': 3.0.1 - - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 - '@types/babel__generator': 7.6.8 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 - - '@types/babel__generator@7.6.8': - dependencies: - '@babel/types': 7.26.3 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.26.3 - '@babel/types': 7.26.3 - - '@types/babel__traverse@7.20.6': - dependencies: - '@babel/types': 7.26.3 - '@types/eslint@9.6.1': dependencies: '@types/estree': 1.0.6 @@ -3829,29 +2188,10 @@ snapshots: dependencies: '@types/node': 22.10.2 - '@types/graceful-fs@4.1.9': - dependencies: - '@types/node': 22.10.2 - '@types/http-errors@2.0.4': {} '@types/humanize-duration@3.27.4': {} - '@types/istanbul-lib-coverage@2.0.6': {} - - '@types/istanbul-lib-report@3.0.3': - dependencies: - '@types/istanbul-lib-coverage': 2.0.6 - - '@types/istanbul-reports@3.0.4': - dependencies: - '@types/istanbul-lib-report': 3.0.3 - - '@types/jest@29.5.14': - dependencies: - expect: 29.7.0 - pretty-format: 29.7.0 - '@types/js-yaml@4.0.9': {} '@types/json-schema@7.0.15': {} @@ -3882,20 +2222,12 @@ snapshots: '@types/node': 22.10.2 '@types/send': 0.17.4 - '@types/stack-utils@2.0.3': {} - '@types/triple-beam@1.3.5': {} '@types/ws@8.5.13': dependencies: '@types/node': 22.10.2 - '@types/yargs-parser@21.0.3': {} - - '@types/yargs@17.0.33': - dependencies: - '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.18.0(@typescript-eslint/parser@8.18.0(eslint@9.17.0)(typescript@5.7.2))(eslint@9.17.0)(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -3973,6 +2305,64 @@ snapshots: '@typescript-eslint/types': 8.18.0 eslint-visitor-keys: 4.2.0 + '@vitest/coverage-v8@2.1.8(vitest@2.1.8(@types/node@22.10.2))': + dependencies: + '@ampproject/remapping': 2.3.0 + '@bcoe/v8-coverage': 0.2.3 + debug: 4.4.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.1.7 + magic-string: 0.30.17 + magicast: 0.3.5 + std-env: 3.8.0 + test-exclude: 7.0.1 + tinyrainbow: 1.2.0 + vitest: 2.1.8(@types/node@22.10.2) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@2.1.8': + dependencies: + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 + tinyrainbow: 1.2.0 + + '@vitest/mocker@2.1.8(vite@5.4.11(@types/node@22.10.2))': + dependencies: + '@vitest/spy': 2.1.8 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.11(@types/node@22.10.2) + + '@vitest/pretty-format@2.1.8': + dependencies: + tinyrainbow: 1.2.0 + + '@vitest/runner@2.1.8': + dependencies: + '@vitest/utils': 2.1.8 + pathe: 1.1.2 + + '@vitest/snapshot@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + magic-string: 0.30.17 + pathe: 1.1.2 + + '@vitest/spy@2.1.8': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@2.1.8': + dependencies: + '@vitest/pretty-format': 2.1.8 + loupe: 3.1.2 + tinyrainbow: 1.2.0 + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -3997,10 +2387,6 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -4009,102 +2395,14 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} + assertion-error@2.0.1: {} + async@3.2.6: {} - babel-jest@29.7.0(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.26.0) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-istanbul@6.1.1: - dependencies: - '@babel/helper-plugin-utils': 7.25.9 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-jest-hoist@29.6.3: - dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.3 - '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.6 - - babel-plugin-polyfill-corejs2@0.4.12(@babel/core@7.26.0): - dependencies: - '@babel/compat-data': 7.26.3 - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - babel-plugin-polyfill-corejs3@0.10.6(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) - core-js-compat: 3.39.0 - transitivePeerDependencies: - - supports-color - - babel-plugin-polyfill-regenerator@0.6.3(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-define-polyfill-provider': 0.6.3(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - - babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.0) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.0) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.0) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.0) - - babel-preset-jest@29.6.3(@babel/core@7.26.0): - dependencies: - '@babel/core': 7.26.0 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) - balanced-match@1.0.2: {} base64-js@1.5.1: {} @@ -4141,17 +2439,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.24.3: - dependencies: - caniuse-lite: 1.0.30001688 - electron-to-chromium: 1.5.73 - node-releases: 2.0.19 - update-browserslist-db: 1.1.1(browserslist@4.24.3) - - bser@2.1.1: - dependencies: - node-int64: 0.4.0 - buffer-crc32@1.0.0: {} buffer-from@1.1.2: {} @@ -4161,34 +2448,24 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 + cac@6.7.14: {} + callsites@3.1.0: {} - camelcase@5.3.1: {} - - camelcase@6.3.0: {} - - caniuse-lite@1.0.30001688: {} + chai@5.1.2: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.2 + pathval: 2.0.0 chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - char-regex@1.0.2: {} - - ci-info@3.9.0: {} - - cjs-module-lexer@1.4.1: {} - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - co@4.6.0: {} - - collect-v8-coverage@1.0.2: {} + check-error@2.1.1: {} color-convert@1.9.3: dependencies: @@ -4228,29 +2505,8 @@ snapshots: readable-stream: 3.6.2 typedarray: 0.0.6 - convert-source-map@2.0.0: {} - - core-js-compat@3.39.0: - dependencies: - browserslist: 4.24.3 - core-util-is@1.0.3: {} - create-jest@29.7.0(@types/node@22.10.2): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.10.2) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -4271,20 +2527,14 @@ snapshots: dependencies: ms: 2.1.3 - dedent@1.5.3: {} + deep-eql@5.0.2: {} deep-is@0.1.4: {} - deepmerge@4.3.1: {} - depd@2.0.0: {} destroy@1.2.0: {} - detect-newline@3.1.0: {} - - diff-sequences@29.6.3: {} - dns-packet@5.6.1: dependencies: '@leichtgewicht/ip-codec': 2.0.5 @@ -4293,10 +2543,6 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.73: {} - - emittery@0.13.1: {} - emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -4307,16 +2553,36 @@ snapshots: encodeurl@2.0.0: {} - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 + es-module-lexer@1.5.4: {} - escalade@3.2.0: {} + esbuild@0.21.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 escape-html@1.0.3: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@4.0.0: {} eslint-config-prettier@9.1.0(eslint@9.17.0): @@ -4377,8 +2643,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 4.2.0 - esprima@4.0.1: {} - esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -4389,6 +2653,10 @@ snapshots: estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + esutils@2.0.3: {} etag@1.8.1: {} @@ -4397,27 +2665,7 @@ snapshots: events@3.3.0: {} - execa@5.1.1: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - exit@0.1.2: {} - - expect@29.7.0: - dependencies: - '@jest/expect-utils': 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 + expect-type@1.1.0: {} express-static-gzip@2.2.0: dependencies: @@ -4451,10 +2699,6 @@ snapshots: dependencies: reusify: 1.0.4 - fb-watchman@2.0.2: - dependencies: - bser: 2.1.1 - fecha@4.2.3: {} file-entry-cache@8.0.0: @@ -4480,11 +2724,6 @@ snapshots: transitivePeerDependencies: - supports-color - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -4506,21 +2745,9 @@ snapshots: fresh@0.5.2: {} - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true - function-bind@1.1.2: {} - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-package-type@0.1.0: {} - - get-stream@6.0.1: {} - git-last-commit@1.0.1: {} glob-parent@5.1.2: @@ -4531,6 +2758,15 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + glob@11.0.0: dependencies: foreground-child: 3.3.0 @@ -4540,31 +2776,16 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.0 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - globals@11.12.0: {} globals@14.0.0: {} glossy@0.1.7: {} - graceful-fs@4.2.11: {} - graphemer@1.4.0: {} has-flag@4.0.0: {} - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - help-me@5.0.0: {} html-escaper@2.0.2: {} @@ -4577,8 +2798,6 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 - human-signals@2.1.0: {} - humanize-duration@3.32.1: {} iconv-lite@0.6.3: @@ -4596,34 +2815,16 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 - import-local@3.2.0: - dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - imurmurhash@0.1.4: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} - is-arrayish@0.2.1: {} - is-arrayish@0.3.2: {} - is-core-module@2.16.0: - dependencies: - hasown: 2.0.2 - is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} - is-generator-fn@2.1.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -4638,37 +2839,17 @@ snapshots: istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@5.2.1: - dependencies: - '@babel/core': 7.26.0 - '@babel/parser': 7.26.3 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.26.0 - '@babel/parser': 7.26.3 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.6.3 - transitivePeerDependencies: - - supports-color - istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1: + istanbul-lib-source-maps@5.0.6: dependencies: + '@jridgewell/trace-mapping': 0.3.25 debug: 4.4.0 istanbul-lib-coverage: 3.2.2 - source-map: 0.6.1 transitivePeerDependencies: - supports-color @@ -4677,347 +2858,34 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jackspeak@4.0.2: dependencies: '@isaacs/cliui': 8.0.2 - jest-changed-files@29.7.0: - dependencies: - execa: 5.1.1 - jest-util: 29.7.0 - p-limit: 3.1.0 - - jest-circus@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/expect': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.5.3 - is-generator-fn: 2.1.0 - jest-each: 29.7.0 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - p-limit: 3.1.0 - pretty-format: 29.7.0 - pure-rand: 6.1.0 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-cli@29.7.0(@types/node@22.10.2): - dependencies: - '@jest/core': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.10.2) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.10.2) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - - jest-config@29.7.0(@types/node@22.10.2): - dependencies: - '@babel/core': 7.26.0 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.0) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.10.2 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-diff@29.7.0: - dependencies: - chalk: 4.1.2 - diff-sequences: 29.6.3 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-docblock@29.7.0: - dependencies: - detect-newline: 3.1.0 - - jest-each@29.7.0: - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - jest-get-type: 29.6.3 - jest-util: 29.7.0 - pretty-format: 29.7.0 - - jest-environment-node@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - jest-mock: 29.7.0 - jest-util: 29.7.0 - - jest-get-type@29.6.3: {} - - jest-haste-map@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.9 - '@types/node': 22.10.2 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.8 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - - jest-leak-detector@29.7.0: - dependencies: - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-matcher-utils@29.7.0: - dependencies: - chalk: 4.1.2 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - pretty-format: 29.7.0 - - jest-message-util@29.7.0: - dependencies: - '@babel/code-frame': 7.26.2 - '@jest/types': 29.6.3 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - stack-utils: 2.0.6 - - jest-mock@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - jest-util: 29.7.0 - - jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): - optionalDependencies: - jest-resolve: 29.7.0 - - jest-regex-util@29.6.3: {} - - jest-resolve-dependencies@29.7.0: - dependencies: - jest-regex-util: 29.6.3 - jest-snapshot: 29.7.0 - transitivePeerDependencies: - - supports-color - - jest-resolve@29.7.0: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) - jest-util: 29.7.0 - jest-validate: 29.7.0 - resolve: 1.22.9 - resolve.exports: 2.0.3 - slash: 3.0.0 - - jest-runner@29.7.0: - dependencies: - '@jest/console': 29.7.0 - '@jest/environment': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - chalk: 4.1.2 - emittery: 0.13.1 - graceful-fs: 4.2.11 - jest-docblock: 29.7.0 - jest-environment-node: 29.7.0 - jest-haste-map: 29.7.0 - jest-leak-detector: 29.7.0 - jest-message-util: 29.7.0 - jest-resolve: 29.7.0 - jest-runtime: 29.7.0 - jest-util: 29.7.0 - jest-watcher: 29.7.0 - jest-worker: 29.7.0 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - - jest-runtime@29.7.0: - dependencies: - '@jest/environment': 29.7.0 - '@jest/fake-timers': 29.7.0 - '@jest/globals': 29.7.0 - '@jest/source-map': 29.6.3 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - chalk: 4.1.2 - cjs-module-lexer: 1.4.1 - collect-v8-coverage: 1.0.2 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-mock: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - - jest-snapshot@29.7.0: - dependencies: - '@babel/core': 7.26.0 - '@babel/generator': 7.26.3 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.0) - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) - '@babel/types': 7.26.3 - '@jest/expect-utils': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.0) - chalk: 4.1.2 - expect: 29.7.0 - graceful-fs: 4.2.11 - jest-diff: 29.7.0 - jest-get-type: 29.6.3 - jest-matcher-utils: 29.7.0 - jest-message-util: 29.7.0 - jest-util: 29.7.0 - natural-compare: 1.4.0 - pretty-format: 29.7.0 - semver: 7.6.3 - transitivePeerDependencies: - - supports-color - - jest-util@29.7.0: - dependencies: - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - chalk: 4.1.2 - ci-info: 3.9.0 - graceful-fs: 4.2.11 - picomatch: 2.3.1 - - jest-validate@29.7.0: - dependencies: - '@jest/types': 29.6.3 - camelcase: 6.3.0 - chalk: 4.1.2 - jest-get-type: 29.6.3 - leven: 3.1.0 - pretty-format: 29.7.0 - - jest-watcher@29.7.0: - dependencies: - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.10.2 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 29.7.0 - string-length: 4.0.2 - - jest-worker@29.7.0: - dependencies: - '@types/node': 22.10.2 - jest-util: 29.7.0 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - jest@29.7.0(@types/node@22.10.2): - dependencies: - '@jest/core': 29.7.0 - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.10.2) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - js-sdsl@4.3.0: {} js-tokens@4.0.0: {} - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.1.0: dependencies: argparse: 2.0.1 - jsesc@3.0.2: {} - jsesc@3.1.0: {} json-buffer@3.0.1: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} json-stable-stringify-without-jsonify@1.0.1: {} - json5@2.2.3: {} - jszip@3.10.1: dependencies: lie: 3.3.0 @@ -5029,12 +2897,8 @@ snapshots: dependencies: json-buffer: 3.0.1 - kleur@3.0.3: {} - kuler@2.0.0: {} - leven@3.1.0: {} - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -5044,18 +2908,10 @@ snapshots: dependencies: immediate: 3.0.6 - lines-and-columns@1.2.4: {} - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 - lodash.debounce@4.0.8: {} - lodash.merge@4.6.2: {} logform@2.7.0: @@ -5067,24 +2923,26 @@ snapshots: safe-stable-stringify: 2.5.0 triple-beam: 1.4.1 + loupe@3.1.2: {} + lru-cache@10.4.3: {} lru-cache@11.0.2: {} - lru-cache@5.1.1: + magic-string@0.30.17: dependencies: - yallist: 3.1.1 + '@jridgewell/sourcemap-codec': 1.5.0 + + magicast@0.3.5: + dependencies: + '@babel/parser': 7.26.3 + '@babel/types': 7.26.3 + source-map-js: 1.2.1 make-dir@4.0.0: dependencies: semver: 7.6.3 - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - - merge-stream@2.0.0: {} - merge2@1.4.1: {} micromatch@4.0.8: @@ -5094,8 +2952,6 @@ snapshots: mime@1.6.0: {} - mimic-fn@2.1.0: {} - minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 @@ -5161,22 +3017,14 @@ snapshots: nan@2.22.0: optional: true + nanoid@3.3.8: {} + natural-compare@1.4.0: {} node-addon-api@7.0.0: {} node-gyp-build@4.6.0: {} - node-int64@0.4.0: {} - - node-releases@2.0.19: {} - - normalize-path@3.0.0: {} - - npm-run-path@4.0.1: - dependencies: - path-key: 3.1.1 - number-allocator@1.0.14: dependencies: debug: 4.4.0 @@ -5190,18 +3038,10 @@ snapshots: dependencies: ee-first: 1.1.1 - once@1.4.0: - dependencies: - wrappy: 1.0.2 - one-time@1.0.0: dependencies: fn.name: 1.1.0 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5211,24 +3051,14 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 - p-try@2.2.0: {} - package-json-from-dist@1.0.1: {} pako@1.0.11: {} @@ -5237,67 +3067,50 @@ snapshots: dependencies: callsites: 3.1.0 - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.26.2 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parseurl@1.3.3: {} path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 path-scurry@2.0.0: dependencies: lru-cache: 11.0.2 minipass: 7.1.2 + pathe@1.1.2: {} + + pathval@2.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} - pirates@4.0.6: {} - - pkg-dir@4.2.0: + postcss@8.4.49: dependencies: - find-up: 4.1.0 + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 prelude-ls@1.2.1: {} prettier@3.4.2: {} - pretty-format@29.7.0: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.3.1 - process-nextick-args@2.0.1: {} process@0.11.10: {} - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - punycode@2.3.1: {} - pure-rand@6.1.0: {} - queue-microtask@1.2.3: {} range-parser@1.2.1: {} - react-is@18.3.1: {} - readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -5322,55 +3135,14 @@ snapshots: process: 0.11.10 string_decoder: 1.3.0 - regenerate-unicode-properties@10.2.0: - dependencies: - regenerate: 1.4.2 - - regenerate@1.4.2: {} - regenerator-runtime@0.14.1: {} - regenerator-transform@0.15.2: - dependencies: - '@babel/runtime': 7.26.0 - - regexpu-core@6.2.0: - dependencies: - regenerate: 1.4.2 - regenerate-unicode-properties: 10.2.0 - regjsgen: 0.8.0 - regjsparser: 0.12.0 - unicode-match-property-ecmascript: 2.0.0 - unicode-match-property-value-ecmascript: 2.2.0 - - regjsgen@0.8.0: {} - - regjsparser@0.12.0: - dependencies: - jsesc: 3.0.2 - reinterval@1.1.0: {} - require-directory@2.1.1: {} - require-from-string@2.0.2: {} - resolve-cwd@3.0.0: - dependencies: - resolve-from: 5.0.0 - resolve-from@4.0.0: {} - resolve-from@5.0.0: {} - - resolve.exports@2.0.3: {} - - resolve@1.22.9: - dependencies: - is-core-module: 2.16.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - reusify@1.0.4: {} rfdc@1.4.1: {} @@ -5380,6 +3152,31 @@ snapshots: glob: 11.0.0 package-json-from-dist: 1.0.1 + rollup@4.29.1: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.29.1 + '@rollup/rollup-android-arm64': 4.29.1 + '@rollup/rollup-darwin-arm64': 4.29.1 + '@rollup/rollup-darwin-x64': 4.29.1 + '@rollup/rollup-freebsd-arm64': 4.29.1 + '@rollup/rollup-freebsd-x64': 4.29.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.29.1 + '@rollup/rollup-linux-arm-musleabihf': 4.29.1 + '@rollup/rollup-linux-arm64-gnu': 4.29.1 + '@rollup/rollup-linux-arm64-musl': 4.29.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.29.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.29.1 + '@rollup/rollup-linux-riscv64-gnu': 4.29.1 + '@rollup/rollup-linux-s390x-gnu': 4.29.1 + '@rollup/rollup-linux-x64-gnu': 4.29.1 + '@rollup/rollup-linux-x64-musl': 4.29.1 + '@rollup/rollup-win32-arm64-msvc': 4.29.1 + '@rollup/rollup-win32-ia32-msvc': 4.29.1 + '@rollup/rollup-win32-x64-msvc': 4.29.1 + fsevents: 2.3.3 + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -5397,8 +3194,6 @@ snapshots: bindings: 1.5.0 optional: true - semver@6.3.1: {} - semver@7.6.3: {} send@0.19.0: @@ -5438,7 +3233,7 @@ snapshots: shebang-regex@3.0.0: {} - signal-exit@3.0.7: {} + siginfo@2.0.0: {} signal-exit@4.1.0: {} @@ -5446,16 +3241,9 @@ snapshots: dependencies: is-arrayish: 0.3.2 - sisteransi@1.0.5: {} - - slash@3.0.0: {} - slip@1.0.2: {} - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + source-map-js@1.2.1: {} source-map-support@0.5.21: dependencies: @@ -5466,20 +3254,13 @@ snapshots: split2@4.2.0: {} - sprintf-js@1.0.3: {} - stack-trace@0.0.10: {} - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 + stackback@0.0.2: {} statuses@2.0.1: {} - string-length@4.0.2: - dependencies: - char-regex: 1.0.2 - strip-ansi: 6.0.1 + std-env@3.8.0: {} string-width@4.2.3: dependencies: @@ -5509,27 +3290,17 @@ snapshots: dependencies: ansi-regex: 6.1.0 - strip-bom@4.0.0: {} - - strip-final-newline@2.0.0: {} - strip-json-comments@3.1.1: {} supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - test-exclude@6.0.0: + test-exclude@7.0.1: dependencies: '@istanbuljs/schema': 0.1.3 - glob: 7.2.3 - minimatch: 3.1.2 + glob: 10.4.5 + minimatch: 9.0.5 text-hex@1.0.0: {} @@ -5537,9 +3308,17 @@ snapshots: thunky@1.1.0: {} - tmp@0.2.3: {} + tinybench@2.9.0: {} - tmpl@1.0.5: {} + tinyexec@0.3.1: {} + + tinypool@1.0.2: {} + + tinyrainbow@1.2.0: {} + + tinyspy@3.0.2: {} + + tmp@0.2.3: {} to-regex-range@5.0.1: dependencies: @@ -5559,10 +3338,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-detect@4.0.8: {} - - type-fest@0.21.3: {} - typedarray@0.0.6: {} typescript-eslint@8.18.0(eslint@9.17.0)(typescript@5.7.2): @@ -5579,17 +3354,6 @@ snapshots: undici-types@6.20.0: {} - unicode-canonical-property-names-ecmascript@2.0.1: {} - - unicode-match-property-ecmascript@2.0.0: - dependencies: - unicode-canonical-property-names-ecmascript: 2.0.1 - unicode-property-aliases-ecmascript: 2.1.0 - - unicode-match-property-value-ecmascript@2.2.0: {} - - unicode-property-aliases-ecmascript@2.1.0: {} - unix-dgram@2.0.6: dependencies: bindings: 1.5.0 @@ -5598,32 +3362,83 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.1.1(browserslist@4.24.3): - dependencies: - browserslist: 4.24.3 - escalade: 3.2.0 - picocolors: 1.1.1 - uri-js@4.4.1: dependencies: punycode: 2.3.1 util-deprecate@1.0.2: {} - v8-to-istanbul@9.3.0: + vite-node@2.1.8(@types/node@22.10.2): dependencies: - '@jridgewell/trace-mapping': 0.3.25 - '@types/istanbul-lib-coverage': 2.0.6 - convert-source-map: 2.0.0 + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.5.4 + pathe: 1.1.2 + vite: 5.4.11(@types/node@22.10.2) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser - walker@1.0.8: + vite@5.4.11(@types/node@22.10.2): dependencies: - makeerror: 1.0.12 + esbuild: 0.21.5 + postcss: 8.4.49 + rollup: 4.29.1 + optionalDependencies: + '@types/node': 22.10.2 + fsevents: 2.3.3 + + vitest@2.1.8(@types/node@22.10.2): + dependencies: + '@vitest/expect': 2.1.8 + '@vitest/mocker': 2.1.8(vite@5.4.11(@types/node@22.10.2)) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.8 + '@vitest/snapshot': 2.1.8 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 + pathe: 1.1.2 + std-env: 3.8.0 + tinybench: 2.9.0 + tinyexec: 0.3.1 + tinypool: 1.0.2 + tinyrainbow: 1.2.0 + vite: 5.4.11(@types/node@22.10.2) + vite-node: 2.1.8(@types/node@22.10.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.10.2 + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser which@2.0.2: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + winston-syslog@2.7.1(winston@3.17.0): dependencies: glossy: 0.1.7 @@ -5686,31 +3501,8 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 - wrappy@1.0.2: {} - - write-file-atomic@4.0.2: - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - ws@8.18.0: {} - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - yocto-queue@0.1.0: {} zigbee-herdsman-converters@21.7.0: diff --git a/test/controller.test.ts b/test/controller.test.ts index 67a65851..cf0c990d 100644 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -1,9 +1,18 @@ import * as data from './mocks/data'; import {mockLogger} from './mocks/logger'; -import {mockMQTT, mockMQTTConnectAsync, events as mockMQTTEvents} from './mocks/mqtt'; -import {flushPromises, JestMockAny} from './mocks/utils'; +import { + mockMQTTConnectAsync, + mockMQTTEndAsync, + events as mockMQTTEvents, + mockMQTTPublishAsync, + mockMQTTSubscribeAsync, + mockMQTTUnsubscribeAsync, +} from './mocks/mqtt'; +import {flushPromises} from './mocks/utils'; import {devices, mockController as mockZHController, events as mockZHEvents, returnDevices} from './mocks/zigbeeHerdsman'; +import type {Mock, MockInstance} from 'vitest'; + import fs from 'node:fs'; import path from 'node:path'; @@ -18,27 +27,21 @@ import * as settings from '../lib/util/settings'; process.env.NOTIFY_SOCKET = 'mocked'; const LOG_MQTT_NS = 'z2m:mqtt'; -jest.mock( - 'sd-notify', - () => { - return { - watchdogInterval: jest.fn(() => 3000), - startWatchdogMode: jest.fn(), - stopWatchdogMode: jest.fn(), - ready: jest.fn(), - stopping: jest.fn(), - }; - }, - {virtual: true}, -); +vi.mock('sd-notify', () => ({ + watchdogInterval: vi.fn(() => 3000), + startWatchdogMode: vi.fn(), + stopWatchdogMode: vi.fn(), + ready: vi.fn(), + stopping: vi.fn(), +})); const mocksClear = [ mockZHController.stop, - mockMQTT.endAsync, - mockMQTT.publishAsync, - mockMQTT.subscribeAsync, - mockMQTT.unsubscribeAsync, - mockMQTT.endAsync, + mockMQTTEndAsync, + mockMQTTPublishAsync, + mockMQTTSubscribeAsync, + mockMQTTUnsubscribeAsync, + mockMQTTEndAsync, mockMQTTConnectAsync, devices.bulb_color.removeFromNetwork, devices.bulb.removeFromNetwork, @@ -50,25 +53,25 @@ const mocksClear = [ describe('Controller', () => { let controller: Controller; - let mockExit: JestMockAny; + let mockExit: Mock; beforeAll(async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); beforeEach(() => { returnDevices.splice(0); - mockExit = jest.fn(); + mockExit = vi.fn(); data.writeDefaultConfiguration(); settings.reRead(); - controller = new Controller(jest.fn(), mockExit); + controller = new Controller(vi.fn(), mockExit); mocksClear.forEach((m) => m.mockClear()); settings.reRead(); data.writeDefaultState(); }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); afterEach(async () => { @@ -104,12 +107,12 @@ describe('Controller', () => { will: {payload: Buffer.from('{"state":"offline"}'), retain: true, topic: 'zigbee2mqtt/bridge/state', qos: 1}, properties: {maximumPacketSize: 1048576}, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}), {retain: true, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {retain: true, qos: 0}); }); it('Start controller with specific MQTT settings', async () => { @@ -160,9 +163,9 @@ describe('Controller', () => { settings.set(['advanced', 'ext_pan_id'], 'GENERATE'); await controller.start(); await flushPromises(); - expect((ZHController as unknown as jest.Mock).mock.calls[0][0].network.networkKey.length).toStrictEqual(16); - expect((ZHController as unknown as jest.Mock).mock.calls[0][0].network.extendedPanID.length).toStrictEqual(8); - expect((ZHController as unknown as jest.Mock).mock.calls[0][0].network.panID).toStrictEqual(expect.any(Number)); + expect((ZHController as unknown as MockInstance).mock.calls[0][0].network.networkKey.length).toStrictEqual(16); + expect((ZHController as unknown as MockInstance).mock.calls[0][0].network.extendedPanID.length).toStrictEqual(8); + expect((ZHController as unknown as MockInstance).mock.calls[0][0].network.panID).toStrictEqual(expect.any(Number)); expect(data.read().advanced.network_key.length).toStrictEqual(16); expect(data.read().advanced.ext_pan_id.length).toStrictEqual(8); expect(data.read().advanced.pan_id).toStrictEqual(expect.any(Number)); @@ -172,13 +175,13 @@ describe('Controller', () => { data.writeDefaultState(); await controller.start(); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}), {qos: 0, retain: true}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {qos: 0, retain: false}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255}), {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {qos: 0, retain: false}); }); it('Start controller should not publish cached states when disabled', async () => { @@ -186,7 +189,7 @@ describe('Controller', () => { data.writeDefaultState(); await controller.start(); await flushPromises(); - const publishedTopics = mockMQTT.publishAsync.mock.calls.map((m) => m[0]); + const publishedTopics = mockMQTTPublishAsync.mock.calls.map((m) => m[0]); expect(publishedTopics).toEqual(expect.not.arrayContaining(['zigbee2mqtt/bulb', 'zigbee2mqtt/remote'])); }); @@ -195,12 +198,12 @@ describe('Controller', () => { data.writeDefaultState(); await controller.start(); await flushPromises(); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith( 'zigbee2mqtt/bulb', `{"state":"ON","brightness":50,"color_temp":370,"linkquality":99}`, {qos: 0, retain: true}, ); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/remote', `{"brightness":255}`, {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/remote', `{"brightness":255}`, {qos: 0, retain: true}); }); it('Log when MQTT client is unavailable', async () => { @@ -209,7 +212,7 @@ describe('Controller', () => { mockLogger.error.mockClear(); // @ts-expect-error private controller.mqtt.client.reconnecting = true; - jest.advanceTimersByTime(11 * 1000); + vi.advanceTimersByTime(11 * 1000); expect(mockLogger.error).toHaveBeenCalledWith('Not connected to MQTT server!'); // @ts-expect-error private controller.mqtt.client.reconnecting = false; @@ -289,7 +292,7 @@ describe('Controller', () => { it('Start controller and stop with restart', async () => { await controller.start(); await controller.stop(true); - expect(mockMQTT.endAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTEndAsync).toHaveBeenCalledTimes(1); expect(mockZHController.stop).toHaveBeenCalledTimes(1); expect(mockExit).toHaveBeenCalledTimes(1); expect(mockExit).toHaveBeenCalledWith(0, true); @@ -299,7 +302,7 @@ describe('Controller', () => { mockZHController.stop.mockRejectedValueOnce('failed'); await controller.start(); await controller.stop(); - expect(mockMQTT.endAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTEndAsync).toHaveBeenCalledTimes(1); expect(mockZHController.stop).toHaveBeenCalledTimes(1); expect(mockExit).toHaveBeenCalledTimes(1); expect(mockExit).toHaveBeenCalledWith(1, false); @@ -310,7 +313,7 @@ describe('Controller', () => { await controller.start(); await mockZHEvents.adapterDisconnected(); await flushPromises(); - expect(mockMQTT.endAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTEndAsync).toHaveBeenCalledTimes(1); expect(mockZHController.stop).toHaveBeenCalledTimes(1); expect(mockExit).toHaveBeenCalledTimes(1); expect(mockExit).toHaveBeenCalledWith(1, false); @@ -324,13 +327,15 @@ describe('Controller', () => { mockLogger.info.mockClear(); mockMQTTEvents.error(new Error('ECONNRESET')); - mockMQTT.reconnecting = true; + // @ts-expect-error private + controller.mqtt.client.reconnecting = true; expect(mockLogger.error).toHaveBeenCalledWith('MQTT error: ECONNRESET'); - await jest.advanceTimersByTimeAsync(11000); + await vi.advanceTimersByTimeAsync(11000); expect(mockLogger.error).toHaveBeenCalledWith('Not connected to MQTT server!'); - mockMQTT.reconnecting = false; + // @ts-expect-error private + controller.mqtt.client.reconnecting = false; await mockMQTTEvents.connect(); expect(mockLogger.info).toHaveBeenCalledWith('Connected to MQTT server'); }); @@ -346,13 +351,15 @@ describe('Controller', () => { reasonCode: 149, properties: {reasonString: 'Maximum packet size was exceeded'}, }); - mockMQTT.disconnecting = true; + // @ts-expect-error private + controller.mqtt.client.disconnecting = true; expect(mockLogger.error).toHaveBeenCalledWith('MQTT disconnect: reason 149 (Maximum packet size was exceeded)'); - await jest.advanceTimersByTimeAsync(11000); + await vi.advanceTimersByTimeAsync(11000); expect(mockLogger.error).toHaveBeenCalledWith('Not connected to MQTT server!'); - mockMQTT.disconnecting = false; + // @ts-expect-error private + controller.mqtt.client.disconnecting = false; await mockMQTTEvents.connect(); expect(mockLogger.info).toHaveBeenCalledWith('Connected to MQTT server'); }); @@ -360,9 +367,9 @@ describe('Controller', () => { it('Handles MQTT publish error', async () => { await controller.start(); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // fail on device_joined (has skipLog=false) - mockMQTT.publishAsync.mockImplementationOnce(mockMQTT.publishAsync.getMockImplementation()!).mockImplementationOnce(() => { + mockMQTTPublishAsync.mockImplementationOnce(mockMQTTPublishAsync.getMockImplementation()!).mockImplementationOnce(() => { throw new Error('client disconnecting'); }); await mockZHEvents.deviceJoined({device: devices.bulb}); @@ -373,7 +380,7 @@ describe('Controller', () => { it('Handle mqtt message', async () => { // @ts-expect-error private - const spyEventbusEmitMQTTMessage = jest.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation(); + const spyEventbusEmitMQTTMessage = vi.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation(vi.fn()); await controller.start(); mockLogger.debug.mockClear(); @@ -384,7 +391,7 @@ describe('Controller', () => { it('Skip MQTT messages on topic we published to', async () => { // @ts-expect-error private - const spyEventbusEmitMQTTMessage = jest.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation(); + const spyEventbusEmitMQTTMessage = vi.spyOn(controller.eventBus, 'emitMQTTMessage').mockImplementation(vi.fn()); await controller.start(); mockLogger.debug.mockClear(); @@ -450,7 +457,7 @@ describe('Controller', () => { const payload = {device}; await mockZHEvents.deviceJoined(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_joined', data: {friendly_name: 'bulb', ieee_address: device.ieeeAddr}}), {retain: false, qos: 0}, @@ -461,7 +468,7 @@ describe('Controller', () => { await controller.start(); const device = devices.bulb; settings.set(['blocklist'], [device.ieeeAddr]); - const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler; + const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler; expect(await handler(device.ieeeAddr)).toBe(false); }); @@ -469,7 +476,7 @@ describe('Controller', () => { await controller.start(); const device = devices.bulb; settings.set(['blocklist'], ['123']); - const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler; + const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler; expect(await handler(device.ieeeAddr)).toBe(true); }); @@ -477,7 +484,7 @@ describe('Controller', () => { await controller.start(); const device = devices.bulb; settings.set(['passlist'], [device.ieeeAddr]); - const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler; + const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler; expect(await handler(device.ieeeAddr)).toBe(true); }); @@ -485,7 +492,7 @@ describe('Controller', () => { await controller.start(); const device = devices.bulb; settings.set(['passlist'], ['123']); - const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler; + const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler; expect(await handler(device.ieeeAddr)).toBe(false); }); @@ -494,14 +501,14 @@ describe('Controller', () => { const device = devices.bulb; settings.set(['passlist'], [device.ieeeAddr]); settings.set(['blocklist'], [device.ieeeAddr]); - const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler; + const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler; expect(await handler(device.ieeeAddr)).toBe(true); }); it('acceptJoiningDeviceHandler accept when not on blocklist and passlist', async () => { await controller.start(); const device = devices.bulb; - const handler = (ZHController as unknown as jest.Mock).mock.calls[0][0].acceptJoiningDeviceHandler; + const handler = (ZHController as unknown as MockInstance).mock.calls[0][0].acceptJoiningDeviceHandler; expect(await handler(device.ieeeAddr)).toBe(true); }); @@ -512,7 +519,7 @@ describe('Controller', () => { mockZHEvents.deviceJoined(payload); mockZHEvents.deviceJoined(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_joined', data: {friendly_name: 'bulb', ieee_address: device.ieeeAddr}}), {retain: false, qos: 0}, @@ -525,7 +532,7 @@ describe('Controller', () => { const payload = {device, status: 'started'}; await mockZHEvents.deviceInterview(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_interview', data: {friendly_name: 'bulb', status: 'started', ieee_address: device.ieeeAddr}}), {retain: false, qos: 0}, @@ -538,7 +545,7 @@ describe('Controller', () => { const payload = {device, status: 'failed'}; await mockZHEvents.deviceInterview(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_interview', data: {friendly_name: 'bulb', status: 'failed', ieee_address: device.ieeeAddr}}), {retain: false, qos: 0}, @@ -547,13 +554,13 @@ describe('Controller', () => { it('On zigbee deviceInterview successful supported', async () => { await controller.start(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const device = devices.bulb; const payload = {device, status: 'successful'}; await mockZHEvents.deviceInterview(payload); await flushPromises(); - expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event'); - const parsedMessage = JSON.parse(mockMQTT.publishAsync.mock.calls[1][1]); + expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event'); + const parsedMessage = JSON.parse(mockMQTTPublishAsync.mock.calls[1][1]); expect(parsedMessage.type).toStrictEqual('device_interview'); expect(parsedMessage.data.friendly_name).toStrictEqual('bulb'); expect(parsedMessage.data.status).toStrictEqual('successful'); @@ -564,18 +571,18 @@ describe('Controller', () => { expect(parsedMessage.data.definition.description).toStrictEqual('TRADFRI bulb E26/E27, white spectrum, globe, opal, 980 lm'); expect(parsedMessage.data.definition.exposes).toStrictEqual(expect.any(Array)); expect(parsedMessage.data.definition.options).toStrictEqual(expect.any(Array)); - expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0}); + expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0}); }); it('On zigbee deviceInterview successful not supported', async () => { await controller.start(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const device = devices.unsupported; const payload = {device, status: 'successful'}; await mockZHEvents.deviceInterview(payload); await flushPromises(); - expect(mockMQTT.publishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event'); - const parsedMessage = JSON.parse(mockMQTT.publishAsync.mock.calls[1][1]); + expect(mockMQTTPublishAsync.mock.calls[1][0]).toStrictEqual('zigbee2mqtt/bridge/event'); + const parsedMessage = JSON.parse(mockMQTTPublishAsync.mock.calls[1][1]); expect(parsedMessage.type).toStrictEqual('device_interview'); expect(parsedMessage.data.friendly_name).toStrictEqual(device.ieeeAddr); expect(parsedMessage.data.status).toStrictEqual('successful'); @@ -586,7 +593,7 @@ describe('Controller', () => { expect(parsedMessage.data.definition.description).toStrictEqual('Automatically generated definition'); expect(parsedMessage.data.definition.exposes).toStrictEqual(expect.any(Array)); expect(parsedMessage.data.definition.options).toStrictEqual(expect.any(Array)); - expect(mockMQTT.publishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0}); + expect(mockMQTTPublishAsync.mock.calls[1][2]).toStrictEqual({retain: false, qos: 0}); }); it('On zigbee event device announce', async () => { @@ -596,7 +603,7 @@ describe('Controller', () => { await mockZHEvents.deviceAnnounce(payload); await flushPromises(); expect(mockLogger.debug).toHaveBeenCalledWith(`Device 'bulb' announced itself`); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_announce', data: {friendly_name: 'bulb', ieee_address: device.ieeeAddr}}), {retain: false, qos: 0}, @@ -607,12 +614,12 @@ describe('Controller', () => { await controller.start(); returnDevices.push('0x00124b00120144ae'); settings.set(['devices'], {}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const device = devices.bulb; const payload = {ieeeAddr: device.ieeeAddr}; await mockZHEvents.deviceLeave(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_leave', data: {ieee_address: device.ieeeAddr, friendly_name: device.ieeeAddr}}), {retain: false, qos: 0}, @@ -623,11 +630,11 @@ describe('Controller', () => { await controller.start(); returnDevices.push('0x00124b00120144ae'); const device = devices.bulb; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const payload = {ieeeAddr: device.ieeeAddr}; await mockZHEvents.deviceLeave(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_leave', data: {ieee_address: device.ieeeAddr, friendly_name: 'bulb'}}), {retain: false, qos: 0}, @@ -637,7 +644,7 @@ describe('Controller', () => { it('Publish entity state attribute output', async () => { await controller.start(); settings.set(['advanced', 'output'], 'attribute'); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, { @@ -650,30 +657,30 @@ describe('Controller', () => { brightness: 50, }); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '50', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color', '100,50,10', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-1', 'yes', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-2', 'no', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test1', '', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test', '', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '50', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color', '100,50,10', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-1', 'yes', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/dummy-2', 'no', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test1', '', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/test', '', {qos: 0, retain: true}); }); it('Publish entity state attribute_json output', async () => { await controller.start(); settings.set(['advanced', 'output'], 'attribute_and_json'); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '99', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/color_temp', '370', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '99', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200, color_temp: 370, linkquality: 99}), {qos: 0, retain: true}, @@ -684,37 +691,37 @@ describe('Controller', () => { await controller.start(); settings.set(['advanced', 'output'], 'attribute_and_json'); settings.set(['devices', devices.bulb.ieeeAddr, 'filtered_attributes'], ['color_temp', 'linkquality']); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true}); }); it('Publish entity state attribute_json output filtered (device_options)', async () => { await controller.start(); settings.set(['advanced', 'output'], 'attribute_and_json'); settings.set(['device_options', 'filtered_attributes'], ['color_temp', 'linkquality']); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON', brightness: 200, color_temp: 370, linkquality: 99}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true}); }); it('Publish entity state attribute_json output filtered cache', async () => { await controller.start(); settings.set(['advanced', 'output'], 'attribute_and_json'); settings.set(['devices', devices.bulb.ieeeAddr, 'filtered_cache'], ['linkquality']); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; @@ -726,11 +733,11 @@ describe('Controller', () => { // @ts-expect-error private expect(controller.state.state[device.ieeeAddr]).toStrictEqual({brightness: 200, color_temp: 370, state: 'ON'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200, color_temp: 370, linkquality: 87}), {qos: 0, retain: true}, @@ -741,7 +748,7 @@ describe('Controller', () => { await controller.start(); settings.set(['advanced', 'output'], 'attribute_and_json'); settings.set(['device_options', 'filtered_cache'], ['linkquality']); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; @@ -753,11 +760,11 @@ describe('Controller', () => { // @ts-expect-error private expect(controller.state.state[device.ieeeAddr]).toStrictEqual({brightness: 200, color_temp: 370, state: 'ON'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/state', 'ON', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/brightness', '200', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb/linkquality', '87', {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200, color_temp: 370, linkquality: 87}), {qos: 0, retain: true}, @@ -767,12 +774,12 @@ describe('Controller', () => { it('Publish entity state with device information', async () => { await controller.start(); settings.set(['mqtt', 'include_device_information'], true); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private let device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({ state: 'ON', @@ -797,7 +804,7 @@ describe('Controller', () => { device = controller.zigbee.resolveEntity('unsupported2')!; await controller.publishEntityState(device, {state: 'ON'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/unsupported2', stringify({ state: 'ON', @@ -818,12 +825,12 @@ describe('Controller', () => { it('Should publish entity state without retain', async () => { await controller.start(); settings.set(['devices', devices.bulb.ieeeAddr, 'retain'], false); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}), {qos: 0, retain: false}, @@ -833,12 +840,12 @@ describe('Controller', () => { it('Should publish entity state with retain', async () => { await controller.start(); settings.set(['devices', devices.bulb.ieeeAddr, 'retain'], true); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}), {qos: 0, retain: true}, @@ -850,12 +857,12 @@ describe('Controller', () => { settings.set(['mqtt', 'version'], 5); settings.set(['devices', devices.bulb.ieeeAddr, 'retain'], true); settings.set(['devices', devices.bulb.ieeeAddr, 'retention'], 37); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 50, color_temp: 370, linkquality: 99}), {qos: 0, retain: true, properties: {messageExpiryInterval: 37}}, @@ -865,27 +872,27 @@ describe('Controller', () => { it('Publish entity state no empty messages', async () => { data.writeEmptyState(); await controller.start(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0); }); it('Should allow to disable state persistency', async () => { settings.set(['advanced', 'cache_state_persistent'], false); data.removeState(); await controller.start(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON'}); await controller.publishEntityState(device, {brightness: 200}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON', brightness: 200}), {qos: 0, retain: true}); await controller.stop(); expect(data.stateExists()).toBeFalsy(); }); @@ -905,15 +912,15 @@ describe('Controller', () => { settings.set(['advanced', 'cache_state'], false); data.writeEmptyState(); await controller.start(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private const device = controller.zigbee.resolveEntity('bulb')!; await controller.publishEntityState(device, {state: 'ON'}); await controller.publishEntityState(device, {brightness: 200}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({brightness: 200}), {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'ON'}), {qos: 0, retain: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({brightness: 200}), {qos: 0, retain: true}); }); it('Should start when state is corrupted', async () => { @@ -945,44 +952,44 @@ describe('Controller', () => { controller.mqtt.retainedMessages, ).length; - mockMQTT.publishAsync.mockClear(); - await jest.advanceTimersByTimeAsync(2500); // before any startup configure triggers + mockMQTTPublishAsync.mockClear(); + await vi.advanceTimersByTimeAsync(2500); // before any startup configure triggers - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(retainedMessages); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(retainedMessages); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); }); it('Should not republish retained messages on MQTT initial connect when retained message are sent', async () => { await controller.start(); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bridge/info', 'dummy'); - await jest.advanceTimersByTimeAsync(2500); // before any startup configure triggers + await vi.advanceTimersByTimeAsync(2500); // before any startup configure triggers - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0); }); it('Should prevent any message being published with retain flag when force_disable_retain is set', async () => { settings.set(['mqtt', 'force_disable_retain'], true); // @ts-expect-error private await controller.mqtt.connect(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private await controller.mqtt.publish('fo', 'bar', {retain: true}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/fo', 'bar', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/fo', 'bar', {retain: false, qos: 0}); }); it('Should publish last seen changes', async () => { settings.set(['advanced', 'last_seen'], 'epoch'); await controller.start(); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const device = devices.remote; await mockZHEvents.lastSeenChanged({device, reason: 'deviceAnnounce'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255, last_seen: 1000}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({brightness: 255, last_seen: 1000}), { qos: 0, retain: true, }); @@ -992,10 +999,10 @@ describe('Controller', () => { settings.set(['advanced', 'last_seen'], 'epoch'); await controller.start(); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const device = devices.remote; await mockZHEvents.lastSeenChanged({device, reason: 'messageEmitted'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0); }); it('Ignore messages from coordinator', async () => { @@ -1034,7 +1041,7 @@ describe('Controller', () => { it('EventBus should handle errors', async () => { // @ts-expect-error private const eventbus = controller.eventBus; - const callback = jest.fn().mockImplementation(async () => { + const callback = vi.fn().mockImplementation(async () => { throw new Error('Whoops!'); }); eventbus.onStateChange({constructor: {name: 'Test'}}, callback); diff --git a/test/extensions/availability.test.ts b/test/extensions/availability.test.ts index b89edae1..e88302be 100644 --- a/test/extensions/availability.test.ts +++ b/test/extensions/availability.test.ts @@ -1,6 +1,6 @@ import * as data from '../mocks/data'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; +import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt'; import {flushPromises} from '../mocks/utils'; import {devices, events as mockZHEvents, returnDevices} from '../mocks/zigbeeHerdsman'; @@ -13,7 +13,7 @@ import Availability from '../../lib/extension/availability'; import * as settings from '../../lib/util/settings'; import utils from '../../lib/util/utils'; -const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.info]; +const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.info]; returnDevices.push( devices.bulb_color.ieeeAddr, @@ -37,22 +37,22 @@ describe('Extension: Availability', () => { }; const setTimeAndAdvanceTimers = async (value: number): Promise => { - jest.setSystemTime(Date.now() + value); - await jest.advanceTimersByTimeAsync(value); + vi.setSystemTime(Date.now() + value); + await vi.advanceTimersByTimeAsync(value); }; beforeAll(async () => { - jest.spyOn(utils, 'sleep').mockImplementation(); - jest.useFakeTimers(); + vi.spyOn(utils, 'sleep').mockImplementation(vi.fn()); + vi.useFakeTimers(); settings.reRead(); settings.set(['availability'], {enabled: true}); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); await flushPromises(); }); beforeEach(async () => { - jest.setSystemTime(utils.minutes(1)); + vi.setSystemTime(utils.minutes(1)); data.writeDefaultConfiguration(); settings.reRead(); settings.set(['availability'], {enabled: true}); @@ -67,16 +67,16 @@ describe('Extension: Availability', () => { afterAll(async () => { await controller.stop(); - jest.useRealTimers(); + vi.useRealTimers(); }); it('Should publish availability on startup for device where it is enabled for', async () => { - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), { retain: true, qos: 1, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'online'}), {retain: true, qos: 1}); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability', stringify({state: 'online'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'online'}), {retain: true, qos: 1}); + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability', stringify({state: 'online'}), { retain: true, qos: 1, }); @@ -106,14 +106,14 @@ describe('Extension: Availability', () => { }); it('Should publish offline for active device when not seen for 10 minutes', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await setTimeAndAdvanceTimers(utils.minutes(5)); expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0); await setTimeAndAdvanceTimers(utils.minutes(7)); expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1); expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), { retain: true, qos: 1, }); @@ -127,20 +127,20 @@ describe('Extension: Availability', () => { }); it('Should publish offline for passive device when not seen for 25 hours', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await setTimeAndAdvanceTimers(utils.hours(26)); expect(devices.remote.ping).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1}); }); it('Should reset ping timer when device last seen changes for active device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await setTimeAndAdvanceTimers(utils.minutes(5)); expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0); await mockZHEvents.lastSeenChanged({device: devices.bulb_color}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), { retain: true, qos: 1, }); @@ -154,7 +154,7 @@ describe('Extension: Availability', () => { }); it('Should ping again when first ping fails', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.lastSeenChanged({device: devices.bulb_color}); @@ -169,13 +169,13 @@ describe('Extension: Availability', () => { }); it('Should reset ping timer when device last seen changes for passive device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await setTimeAndAdvanceTimers(utils.hours(24)); expect(devices.remote.ping).toHaveBeenCalledTimes(0); await mockZHEvents.lastSeenChanged({device: devices.remote}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1}); await setTimeAndAdvanceTimers(utils.hours(25)); expect(devices.remote.ping).toHaveBeenCalledTimes(0); @@ -185,10 +185,10 @@ describe('Extension: Availability', () => { }); it('Should immediately mark device as online when it lastSeen changes', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await setTimeAndAdvanceTimers(utils.minutes(15)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'offline'}), { retain: true, qos: 1, }); @@ -196,7 +196,7 @@ describe('Extension: Availability', () => { devices.bulb_color.lastSeen = Date.now(); await mockZHEvents.lastSeenChanged({device: devices.bulb_color}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', stringify({state: 'online'}), { retain: true, qos: 1, }); @@ -310,18 +310,18 @@ describe('Extension: Availability', () => { }); it('Should republish availability when device is renamed', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb_color', to: 'bulb_new_name'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', '', {retain: true, qos: 1}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'online'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability', '', {retain: true, qos: 1}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'online'}), { retain: true, qos: 1, }); await setTimeAndAdvanceTimers(utils.hours(12)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'offline'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability', stringify({state: 'offline'}), { retain: true, qos: 1, }); @@ -330,10 +330,10 @@ describe('Extension: Availability', () => { it('Should publish availability payload in JSON format', async () => { await resetExtension(); devices.remote.ping.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await setTimeAndAdvanceTimers(utils.hours(26)); expect(devices.remote.ping).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/remote/availability', stringify({state: 'offline'}), {retain: true, qos: 1}); }); it('Should publish availability for groups', async () => { @@ -341,21 +341,21 @@ describe('Extension: Availability', () => { await resetExtension(); devices.bulb_color_2.ping.mockClear(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), { retain: true, qos: 1, }); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await setTimeAndAdvanceTimers(utils.minutes(12)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'offline'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'offline'}), { retain: true, qos: 1, }); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); devices.bulb_color_2.lastSeen = Date.now(); await mockZHEvents.lastSeenChanged({device: devices.bulb_color_2}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability', stringify({state: 'online'}), { retain: true, qos: 1, }); @@ -365,7 +365,7 @@ describe('Extension: Availability', () => { // @ts-expect-error private const availability = controller.extensions.find((extension) => extension instanceof Availability)!; // @ts-expect-error private - const publishAvailabilitySpy = jest.spyOn(availability, 'publishAvailability'); + const publishAvailabilitySpy = vi.spyOn(availability, 'publishAvailability'); devices.bulb_color.ping.mockImplementationOnce(() => new Promise((resolve) => setTimeout(resolve, 1000))); // @ts-expect-error private @@ -380,7 +380,7 @@ describe('Extension: Availability', () => { expect(availability.pingQueue).toEqual([]); // Validate the stop-interrupt implicitly by checking that it prevents further function invocations expect(publishAvailabilitySpy).not.toHaveBeenCalled(); - devices.bulb_color.ping = jest.fn(); // ensure reset + devices.bulb_color.ping = vi.fn(); // ensure reset }); it('Should prevent instance restart', async () => { diff --git a/test/extensions/bind.test.ts b/test/extensions/bind.test.ts index 13980129..565c099f 100644 --- a/test/extensions/bind.test.ts +++ b/test/extensions/bind.test.ts @@ -1,7 +1,7 @@ import * as data from '../mocks/data'; import {mockDebounce} from '../mocks/debounce'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; +import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt'; import {flushPromises} from '../mocks/utils'; import {Device, devices, groups, events as mockZHEvents} from '../mocks/zigbeeHerdsman'; @@ -12,7 +12,7 @@ import * as settings from '../../lib/util/settings'; const mocksClear = [ mockDebounce, - mockMQTT.publishAsync, + mockMQTTPublishAsync, devices.bulb_color.getEndpoint(1)!.configureReporting, devices.bulb_color.getEndpoint(1)!.bind, devices.bulb_color_2.getEndpoint(1)!.read, @@ -31,15 +31,15 @@ describe('Extension: Bind', () => { endpoint.read.mockClear(); endpoint.write.mockClear(); endpoint.configureReporting.mockClear(); - endpoint.bind = jest.fn(); + endpoint.bind = vi.fn(); endpoint.bind.mockClear(); endpoint.unbind.mockClear(); } }; beforeAll(async () => { - jest.useFakeTimers(); - controller = new Controller(jest.fn(), jest.fn()); + vi.useFakeTimers(); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); await flushPromises(); }); @@ -53,7 +53,7 @@ describe('Extension: Bind', () => { }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); it('Should bind to device and configure reporting', async () => { @@ -92,7 +92,7 @@ describe('Extension: Bind', () => { {attribute: 'currentX', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1}, {attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1}, ]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ transaction: '1234', @@ -108,7 +108,7 @@ describe('Extension: Bind', () => { {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); // Teardown target.binds = originalTargetBinds; @@ -116,10 +116,10 @@ describe('Extension: Bind', () => { }); it('Should throw error on invalid payload', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({fromz: 'remote', to: 'bulb_color'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -168,7 +168,7 @@ describe('Extension: Bind', () => { {attribute: 'currentX', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1}, {attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1}, ]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ transaction: '1234', @@ -184,7 +184,7 @@ describe('Extension: Bind', () => { {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); // Teardown target.binds = originalTargetBinds; @@ -238,7 +238,7 @@ describe('Extension: Bind', () => { {attribute: 'currentX', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1}, {attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 3600, reportableChange: 1}, ]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ transaction: '1234', @@ -254,7 +254,7 @@ describe('Extension: Bind', () => { {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); // Teardown target.configuredReportings = originalTargetCR; @@ -271,7 +271,7 @@ describe('Extension: Bind', () => { await flushPromises(); expect(endpoint.bind).toHaveBeenCalledTimes(1); expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {from: 'remote', from_endpoint: 'default', to: 'bulb_color', clusters: ['genOnOff'], failed: []}, status: 'ok'}), {retain: false, qos: 0}, @@ -286,7 +286,7 @@ describe('Extension: Bind', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'button'})); await flushPromises(); expect(endpoint.bind).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {}, status: 'error', error: 'Nothing to bind'}), {retain: false, qos: 0}, @@ -335,7 +335,7 @@ describe('Extension: Bind', () => { {attribute: 'currentY', minimumReportInterval: 5, maximumReportInterval: 0xffff, reportableChange: 1}, ]); expect(devices.bulb_color.meta.configured).toBe(332242049); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/unbind', stringify({ data: {from: 'remote', from_endpoint: 'default', to: 'bulb_color', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []}, @@ -361,7 +361,7 @@ describe('Extension: Bind', () => { expect(endpoint.unbind).toHaveBeenCalledWith('genOnOff', target); expect(endpoint.unbind).toHaveBeenCalledWith('genLevelCtrl', target); expect(endpoint.unbind).toHaveBeenCalledWith('genScenes', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/unbind', stringify({ data: {from: 'remote', from_endpoint: 'default', to: 'Coordinator', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []}, @@ -392,7 +392,7 @@ describe('Extension: Bind', () => { expect(target1Member.configureReporting).toHaveBeenCalledWith('genLevelCtrl', [ {attribute: 'currentLevel', maximumReportInterval: 3600, minimumReportInterval: 5, reportableChange: 1}, ]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ data: {from: 'remote', from_endpoint: 'default', to: 'group_1', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []}, @@ -428,7 +428,7 @@ describe('Extension: Bind', () => { expect(endpoint.unbind).toHaveBeenCalledWith('genOnOff', target); expect(endpoint.unbind).toHaveBeenCalledWith('genLevelCtrl', target); expect(endpoint.unbind).toHaveBeenCalledWith('genScenes', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/unbind', stringify({ data: {from: 'remote', from_endpoint: 'default', to: 'group_1', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []}, @@ -505,7 +505,7 @@ describe('Extension: Bind', () => { expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target); expect(endpoint.bind).toHaveBeenCalledWith('genLevelCtrl', target); expect(endpoint.bind).toHaveBeenCalledWith('genScenes', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ data: {from: 'remote', from_endpoint: 'default', to: '1', clusters: ['genScenes', 'genOnOff', 'genLevelCtrl'], failed: []}, @@ -526,7 +526,7 @@ describe('Extension: Bind', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color'})); await flushPromises(); expect(endpoint.bind).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {}, status: 'error', error: 'Failed to bind'}), {retain: false, qos: 0}, @@ -545,7 +545,7 @@ describe('Extension: Bind', () => { await flushPromises(); expect(endpoint.bind).toHaveBeenCalledTimes(1); expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ data: {from: 'remote', from_endpoint: 'ep2', to: 'wall_switch_double', to_endpoint: 'right', clusters: ['genOnOff'], failed: []}, @@ -567,7 +567,7 @@ describe('Extension: Bind', () => { await flushPromises(); expect(endpoint.bind).toHaveBeenCalledTimes(1); expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ data: {from: 'remote', from_endpoint: 2, to: 'wall_switch_double', to_endpoint: 3, clusters: ['genOnOff'], failed: []}, @@ -586,7 +586,7 @@ describe('Extension: Bind', () => { await flushPromises(); expect(endpoint.bind).toHaveBeenCalledTimes(1); expect(endpoint.bind).toHaveBeenCalledWith('msTemperatureMeasurement', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({ data: { @@ -611,7 +611,7 @@ describe('Extension: Bind', () => { await flushPromises(); expect(endpoint.bind).toHaveBeenCalledTimes(1); expect(endpoint.bind).toHaveBeenCalledWith('genOnOff', target); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {from: 'remote', from_endpoint: 'ep2', to: 'wall_switch', clusters: ['genOnOff'], failed: []}, status: 'ok'}), {retain: false, qos: 0}, @@ -629,7 +629,7 @@ describe('Extension: Bind', () => { expect(endpoint.unbind).toHaveBeenCalledWith('genOnOff', 901); expect(endpoint.unbind).toHaveBeenCalledWith('genLevelCtrl', 901); expect(endpoint.unbind).toHaveBeenCalledWith('genScenes', 901); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/unbind', stringify({ data: { @@ -650,7 +650,7 @@ describe('Extension: Bind', () => { mockClear(device); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote_not_existing', to: 'bulb_color'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {}, status: 'error', error: "Source device 'remote_not_existing' does not exist"}), {retain: false, qos: 0}, @@ -665,7 +665,7 @@ describe('Extension: Bind', () => { stringify({from: 'remote', from_endpoint: 'not_existing_endpoint', to: 'bulb_color'}), ); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {}, status: 'error', error: "Source device 'remote' does not have endpoint 'not_existing_endpoint'"}), {retain: false, qos: 0}, @@ -677,7 +677,7 @@ describe('Extension: Bind', () => { mockClear(device); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color_not_existing'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {}, status: 'error', error: "Target device or group 'bulb_color_not_existing' does not exist"}), {retain: false, qos: 0}, @@ -692,7 +692,7 @@ describe('Extension: Bind', () => { stringify({from: 'remote', to: 'bulb_color', to_endpoint: 'not_existing_endpoint'}), ); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/bind', stringify({data: {}, status: 'error', error: "Target device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}), {retain: false, qos: 0}, diff --git a/test/extensions/bridge.test.ts b/test/extensions/bridge.test.ts index e6332a3f..c883a29b 100644 --- a/test/extensions/bridge.test.ts +++ b/test/extensions/bridge.test.ts @@ -1,10 +1,12 @@ import * as data from '../mocks/data'; import {mockJSZipFile, mockJSZipGenerateAsync} from '../mocks/jszip'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; -import {flushPromises, JestMockAny} from '../mocks/utils'; +import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt'; +import {flushPromises} from '../mocks/utils'; import {CUSTOM_CLUSTERS, devices, groups, mockController as mockZHController, events as mockZHEvents, returnDevices} from '../mocks/zigbeeHerdsman'; +import type {Mock} from 'vitest'; + import type Bridge from '../../lib/extension/bridge'; import assert from 'node:assert'; @@ -31,7 +33,7 @@ returnDevices.push(devices.bulb_custom_cluster.ieeeAddr); const mocksClear = [ mockLogger.info, mockLogger.warning, - mockMQTT.publishAsync, + mockMQTTPublishAsync, mockZHController.permitJoin, devices.bulb.interview, devices.bulb.removeFromDatabase, @@ -40,7 +42,7 @@ const mocksClear = [ describe('Extension: Bridge', () => { let controller: Controller; - let mockRestart: JestMockAny; + let mockRestart: Mock; let extension: Bridge; const resetExtension = async (): Promise => { @@ -51,9 +53,9 @@ describe('Extension: Bridge', () => { }; beforeAll(async () => { - jest.useFakeTimers(); - mockRestart = jest.fn(); - controller = new Controller(mockRestart, jest.fn()); + vi.useFakeTimers(); + mockRestart = vi.fn(); + controller = new Controller(mockRestart, vi.fn()); await controller.start(); await flushPromises(); // @ts-expect-error private @@ -61,7 +63,12 @@ describe('Extension: Bridge', () => { }); beforeEach(async () => { - mockMQTT.reconnecting = false; + // @ts-expect-error private + controller.mqtt.client.reconnecting = false; + // @ts-expect-error private + controller.mqtt.client.disconnecting = false; + // @ts-expect-error private + controller.mqtt.client.disconnected = false; data.writeDefaultConfiguration(); settings.reRead(); data.writeDefaultState(); @@ -76,7 +83,7 @@ describe('Extension: Bridge', () => { }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); it('Should publish bridge info on startup', async () => { @@ -85,8 +92,8 @@ describe('Extension: Bridge', () => { const zhVersion = await utils.getDependencyVersion('zigbee-herdsman'); const zhcVersion = await utils.getDependencyVersion('zigbee-herdsman-converters'); const directory = settings.get().advanced.log_directory; - // console.log(mockMQTT.publishAsync.mock.calls.find((c) => c[0] === 'zigbee2mqtt/bridge/info')![1]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + // console.log(mockMQTTPublishAsync.mock.calls.find((c) => c[0] === 'zigbee2mqtt/bridge/info')![1]); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/info', stringify({ commit: version.commitHash, @@ -305,7 +312,7 @@ describe('Extension: Bridge', () => { it('Should publish devices on startup', async () => { await resetExtension(); // console.log(mockMQTT.publish.mock.calls.find((c) => c[0] === 'zigbee2mqtt/bridge/devices')[1]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/devices', stringify([ { @@ -2140,7 +2147,7 @@ describe('Extension: Bridge', () => { it('Should publish definitions on startup', async () => { await resetExtension(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.stringContaining(stringify(CUSTOM_CLUSTERS)), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.stringContaining(stringify(CUSTOM_CLUSTERS)), { retain: true, qos: 0, }); @@ -2148,21 +2155,21 @@ describe('Extension: Bridge', () => { it('Should log to MQTT', async () => { mockLogger.setTransportsEnabled(true); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockLogger.info.mockClear(); mockLogger.info('this is a test'); mockLogger.info('this is a test'); // Should not publish dupes - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/logging', stringify({message: 'this is a test', level: 'info', namespace: 'z2m'}), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); // Should not publish debug logging - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockLogger.debug('this is a test'); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0); }); it('Should log to MQTT including debug when enabled', async () => { @@ -2170,21 +2177,21 @@ describe('Extension: Bridge', () => { await resetExtension(); mockLogger.setTransportsEnabled(true); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockLogger.info.mockClear(); mockLogger.info('this is a test'); mockLogger.info('this is a test'); // Should not publish dupes - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/logging', stringify({message: 'this is a test', level: 'info', namespace: 'z2m'}), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); // Should publish debug logging - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockLogger.debug('this is a test'); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); settings.set(['advanced', 'log_debug_to_mqtt_frontend'], false); settings.reRead(); @@ -2192,12 +2199,13 @@ describe('Extension: Bridge', () => { it('Shouldnt log to MQTT when not connected', async () => { mockLogger.setTransportsEnabled(true); - mockMQTT.reconnecting = true; - mockMQTT.publishAsync.mockClear(); + // @ts-expect-error private + controller.mqtt.client.reconnecting = true; + mockMQTTPublishAsync.mockClear(); mockLogger.info.mockClear(); mockLogger.error.mockClear(); mockLogger.info('this is a test'); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(0); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(0); expect(mockLogger.info).toHaveBeenCalledTimes(1); expect(mockLogger.error).toHaveBeenCalledTimes(0); }); @@ -2206,7 +2214,7 @@ describe('Extension: Bridge', () => { await resetExtension(); mockLogger.setTransportsEnabled(true); // console.log(MQTT.publish.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/groups')); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/groups', stringify([ {friendly_name: 'group_1', id: 1, members: [], scenes: []}, @@ -2256,10 +2264,10 @@ describe('Extension: Bridge', () => { }); it('Should publish event when device joined', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceJoined({device: devices.bulb}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_joined', data: {friendly_name: 'bulb', ieee_address: '0x000b57fffec6a5b2'}}), {retain: false, qos: 0}, @@ -2267,18 +2275,18 @@ describe('Extension: Bridge', () => { }); it('Should publish devices when device joined', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceNetworkAddressChanged({device: devices.bulb}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); }); it('Should publish event when device announces', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceAnnounce({device: devices.bulb}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_announce', data: {friendly_name: 'bulb', ieee_address: '0x000b57fffec6a5b2'}}), {retain: false, qos: 0}, @@ -2286,11 +2294,11 @@ describe('Extension: Bridge', () => { }); it('Should publish event when device interview started', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceInterview({device: devices.bulb, status: 'started'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_interview', data: {friendly_name: 'bulb', status: 'started', ieee_address: '0x000b57fffec6a5b2'}}), {retain: false, qos: 0}, @@ -2298,26 +2306,26 @@ describe('Extension: Bridge', () => { }); it('Should publish event and devices when device interview failed', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceInterview({device: devices.bulb, status: 'failed'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_interview', data: {friendly_name: 'bulb', status: 'failed', ieee_address: '0x000b57fffec6a5b2'}}), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); }); it('Should publish event and devices when device interview successful', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceInterview({device: devices.bulb, status: 'successful'}); await mockZHEvents.deviceInterview({device: devices.unsupported, status: 'successful'}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(7); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(7); // console.log(mockMQTT.publish.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/event')); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({ data: { @@ -2541,7 +2549,7 @@ describe('Extension: Bridge', () => { }), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({ data: { @@ -2626,22 +2634,22 @@ describe('Extension: Bridge', () => { }), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.any(String), {retain: true, qos: 0}); }); it('Should publish event and devices when device leaves', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceLeave({ieeeAddr: devices.bulb.ieeeAddr}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({type: 'device_leave', data: {ieee_address: '0x000b57fffec6a5b2', friendly_name: 'bulb'}}), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( // Defintitions should be updated on device event 'zigbee2mqtt/bridge/definitions', expect.any(String), @@ -2654,7 +2662,7 @@ describe('Extension: Bridge', () => { await flushPromises(); expect(mockZHController.permitJoin).toHaveBeenCalledTimes(1); expect(mockZHController.permitJoin).toHaveBeenCalledWith(1, undefined); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 1}, status: 'ok'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 1}, status: 'ok'}), { retain: false, qos: 0, }); @@ -2665,7 +2673,7 @@ describe('Extension: Bridge', () => { await flushPromises(); expect(mockZHController.permitJoin).toHaveBeenCalledTimes(1); expect(mockZHController.permitJoin).toHaveBeenCalledWith(0, undefined); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 0}, status: 'ok'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 0}, status: 'ok'}), { retain: false, qos: 0, }); @@ -2676,7 +2684,7 @@ describe('Extension: Bridge', () => { await flushPromises(); expect(mockZHController.permitJoin).toHaveBeenCalledTimes(1); expect(mockZHController.permitJoin).toHaveBeenCalledWith(1, undefined); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 1}, status: 'ok'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 1}, status: 'ok'}), { retain: false, qos: 0, }); @@ -2686,7 +2694,7 @@ describe('Extension: Bridge', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/permit_join', stringify({time_bla: false})); await flushPromises(); expect(mockZHController.permitJoin).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/permit_join', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -2694,28 +2702,28 @@ describe('Extension: Bridge', () => { }); it('Should republish bridge info when permit join changes', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.permitJoinChanged({permitted: false, timeout: 10}); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); }); it('Shouldnt republish bridge info when permit join changes and hersman is stopping', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.isStopping.mockImplementationOnce(() => true); await mockZHEvents.permitJoinChanged({permitted: false, timeout: 10}); await flushPromises(); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); }); it('Should allow permit join via device', async () => { const device = devices.bulb; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 123, device: 'bulb'})); await flushPromises(); expect(mockZHController.permitJoin).toHaveBeenCalledTimes(1); expect(mockZHController.permitJoin).toHaveBeenCalledWith(123, device); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 123, device: 'bulb'}, status: 'ok'}), {retain: false, qos: 0}, @@ -2723,11 +2731,11 @@ describe('Extension: Bridge', () => { }); it('Should not allow permit join via non-existing device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 123, device: 'bulb_not_existing_woeeee'})); await flushPromises(); expect(mockZHController.permitJoin).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/permit_join', stringify({data: {}, status: 'error', error: "Device 'bulb_not_existing_woeeee' does not exist"}), {retain: false, qos: 0}, @@ -2735,10 +2743,10 @@ describe('Extension: Bridge', () => { }); it('Should put transaction in response when request is done with transaction', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 0, transaction: 22})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/permit_join', stringify({data: {time: 0}, status: 'ok', transaction: 22}), {retain: false, qos: 0}, @@ -2749,10 +2757,10 @@ describe('Extension: Bridge', () => { mockZHController.permitJoin.mockImplementationOnce(() => { throw new Error('Failed to connect to adapter'); }); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/permit_join', stringify({time: 0})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/permit_join', stringify({data: {}, status: 'error', error: 'Failed to connect to adapter'}), {retain: false, qos: 0}, @@ -2760,10 +2768,10 @@ describe('Extension: Bridge', () => { }); it('Should put error in response when format is incorrect', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: false})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -2771,7 +2779,7 @@ describe('Extension: Bridge', () => { }); it('Coverage satisfaction', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/random', stringify({value: false})); const device = devices.bulb; await mockZHEvents.message({ @@ -2786,10 +2794,10 @@ describe('Extension: Bridge', () => { }); it('Should allow a healthcheck', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/health_check', ''); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/health_check', stringify({data: {healthy: true}, status: 'ok'}), {retain: false, qos: 0}, @@ -2797,11 +2805,11 @@ describe('Extension: Bridge', () => { }); it('Should allow a coordinator check', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.coordinatorCheck.mockReturnValueOnce({missingRouters: [mockZHController.getDeviceByIeeeAddr('0x000b57fffec6a5b2')]}); mockMQTTEvents.message('zigbee2mqtt/bridge/request/coordinator_check', ''); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/coordinator_check', stringify({data: {missing_routers: [{friendly_name: 'bulb', ieee_address: '0x000b57fffec6a5b2'}]}, status: 'ok'}), {retain: false, qos: 0}, @@ -2810,7 +2818,7 @@ describe('Extension: Bridge', () => { it('Should allow to remove device by string', async () => { const device = devices.bulb; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/remove', 'bulb'); await flushPromises(); // @ts-expect-error private @@ -2818,28 +2826,28 @@ describe('Extension: Bridge', () => { expect(device.removeFromNetwork).toHaveBeenCalledTimes(1); expect(device.removeFromDatabase).not.toHaveBeenCalled(); expect(settings.getDevice('bulb')).toBeUndefined(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', '', {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', '', {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/remove', stringify({data: {id: 'bulb', block: false, force: false}, status: 'ok'}), {retain: false, qos: 0}, ); expect(settings.get().blocklist).toStrictEqual([]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); }); it('Should allow to remove device by object ID', async () => { const device = devices.bulb; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: 'bulb'})); await flushPromises(); expect(device.removeFromNetwork).toHaveBeenCalledTimes(1); expect(device.removeFromDatabase).not.toHaveBeenCalled(); expect(settings.getDevice('bulb')).toBeUndefined(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/remove', stringify({data: {id: 'bulb', block: false, force: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -2848,14 +2856,14 @@ describe('Extension: Bridge', () => { it('Should allow to force remove device', async () => { const device = devices.bulb; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: 'bulb', force: true})); await flushPromises(); expect(device.removeFromDatabase).toHaveBeenCalledTimes(1); expect(device.removeFromNetwork).not.toHaveBeenCalled(); expect(settings.getDevice('bulb')).toBeUndefined(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/remove', stringify({data: {id: 'bulb', block: false, force: true}, status: 'ok'}), {retain: false, qos: 0}, @@ -2864,13 +2872,13 @@ describe('Extension: Bridge', () => { it('Should allow to block device', async () => { const device = devices.bulb; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: 'bulb', block: true, force: true})); await flushPromises(); expect(device.removeFromDatabase).toHaveBeenCalledTimes(1); expect(settings.getDevice('bulb')).toBeUndefined(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/remove', stringify({data: {id: 'bulb', block: true, force: true}, status: 'ok'}), {retain: false, qos: 0}, @@ -2880,13 +2888,13 @@ describe('Extension: Bridge', () => { it('Should allow to remove group', async () => { const group = groups.group_1; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/remove', 'group_1'); await flushPromises(); expect(group.removeFromNetwork).toHaveBeenCalledTimes(1); expect(settings.getGroup('group_1')).toBeUndefined(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/remove', stringify({data: {id: 'group_1', force: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -2895,13 +2903,13 @@ describe('Extension: Bridge', () => { it('Should allow to force remove group', async () => { const group = groups.group_1; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/remove', stringify({id: 'group_1', force: true})); await flushPromises(); expect(group.removeFromDatabase).toHaveBeenCalledTimes(1); expect(settings.getGroup('group_1')).toBeUndefined(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/remove', stringify({data: {id: 'group_1', force: true}, status: 'ok'}), {retain: false, qos: 0}, @@ -2920,10 +2928,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error on removing non-existing device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: 'non-existing-device'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/remove', stringify({data: {}, status: 'error', error: "Device 'non-existing-device' does not exist"}), {retain: false, qos: 0}, @@ -2932,13 +2940,13 @@ describe('Extension: Bridge', () => { it('Should throw error when remove device fails', async () => { const device = devices.bulb; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); device.removeFromNetwork.mockImplementationOnce(() => { throw new Error('device timeout'); }); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/remove', stringify({id: 'bulb'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/remove', stringify({data: {}, status: 'error', error: "Failed to remove device 'bulb' (block: false, force: false) (Error: device timeout)"}), {retain: false, qos: 0}, @@ -2946,7 +2954,7 @@ describe('Extension: Bridge', () => { }); it('Should allow rename device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb', to: 'bulb_new_name'})); await flushPromises(); expect(settings.getDevice('bulb')).toBeUndefined(); @@ -2956,10 +2964,10 @@ describe('Extension: Bridge', () => { retain: true, description: 'this is my bulb', }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', '', {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name', stringify({brightness: 50}), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', '', {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name', stringify({brightness: 50}), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/rename', stringify({data: {from: 'bulb', to: 'bulb_new_name', homeassistant_rename: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -2967,10 +2975,10 @@ describe('Extension: Bridge', () => { }); it('Shouldnt allow rename device with to not allowed name containing a wildcard', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb', to: 'living_room/blinds#'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/rename', stringify({data: {}, status: 'error', error: "MQTT wildcard (+ and #) not allowed in friendly_name ('living_room/blinds#')"}), {retain: false, qos: 0}, @@ -2978,13 +2986,13 @@ describe('Extension: Bridge', () => { }); it('Should allow rename group', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/rename', stringify({from: 'group_1', to: 'group_new_name'})); await flushPromises(); expect(settings.getGroup('group_1')).toBeUndefined(); expect(settings.getGroup('group_new_name')).toStrictEqual({ID: 1, friendly_name: 'group_new_name', retain: false}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/rename', stringify({data: {from: 'group_1', to: 'group_new_name', homeassistant_rename: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -2992,10 +3000,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error on invalid device rename payload', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({from_bla: 'bulb', to: 'bulb_new_name'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/rename', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -3003,10 +3011,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error on non-existing device rename', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb_not_existing', to: 'bulb_new_name'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/rename', stringify({data: {}, status: 'error', error: "Device 'bulb_not_existing' does not exist"}), {retain: false, qos: 0}, @@ -3014,7 +3022,7 @@ describe('Extension: Bridge', () => { }); it('Should allow to rename last joined device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceJoined({device: devices.bulb}); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({last: true, to: 'bulb_new_name'})); await flushPromises(); @@ -3025,8 +3033,8 @@ describe('Extension: Bridge', () => { retain: true, description: 'this is my bulb', }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/rename', stringify({data: {from: 'bulb', to: 'bulb_new_name', homeassistant_rename: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -3034,10 +3042,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error when renaming device through not allowed friendlyName', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({from: 'bulb', to: 'bulb_new_name/1'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/rename', stringify({data: {}, status: 'error', error: `Friendly name cannot end with a "/DIGIT" ('bulb_new_name/1')`}), {retain: false, qos: 0}, @@ -3045,10 +3053,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error when renaming last joined device but none has joined', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/rename', stringify({last: true, to: 'bulb_new_name'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/rename', stringify({data: {}, status: 'error', error: 'No device has joined since start'}), {retain: false, qos: 0}, @@ -3056,48 +3064,48 @@ describe('Extension: Bridge', () => { }); it('Should allow interviewing a device by friendly name', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); devices.bulb.interview.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/interview', stringify({id: 'bulb'})); await flushPromises(); expect(devices.bulb.interview).toHaveBeenCalled(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/interview', stringify({data: {id: 'bulb'}, status: 'ok'}), {retain: false, qos: 0}, ); // The following indicates that devices have published. - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); }); it('Should allow interviewing a device by ieeeAddr', async () => { // @ts-expect-error private const device = controller.zigbee.resolveEntity(devices.bulb)!; assert('resolveDefinition' in device); - device.resolveDefinition = jest.fn(); - mockMQTT.publishAsync.mockClear(); + device.resolveDefinition = vi.fn(); + mockMQTTPublishAsync.mockClear(); devices.bulb.interview.mockClear(); expect(device.resolveDefinition).toHaveBeenCalledTimes(0); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/interview', stringify({id: '0x000b57fffec6a5b2'})); await flushPromises(); expect(devices.bulb.interview).toHaveBeenCalledWith(true); expect(device.resolveDefinition).toHaveBeenCalledWith(true); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/interview', stringify({data: {id: '0x000b57fffec6a5b2'}, status: 'ok'}), {retain: false, qos: 0}, ); // The following indicates that devices have published. - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); }); it('Should throw error on invalid device interview payload', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/interview', stringify({foo: 'bulb'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/interview', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -3105,10 +3113,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error on non-existing device interview', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/interview', stringify({id: 'bulb_not_existing'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/interview', stringify({data: {}, status: 'error', error: "Device 'bulb_not_existing' does not exist"}), {retain: false, qos: 0}, @@ -3116,10 +3124,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error on id is device endpoint', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/interview', stringify({id: 'bulb/1'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/interview', stringify({data: {}, status: 'error', error: "Device 'bulb/1' does not exist"}), {retain: false, qos: 0}, @@ -3127,10 +3135,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error on id is a group', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/interview', stringify({id: 'group_1'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/interview', stringify({data: {}, status: 'error', error: "Device 'group_1' does not exist"}), {retain: false, qos: 0}, @@ -3138,12 +3146,12 @@ describe('Extension: Bridge', () => { }); it('Should throw error on when interview fails', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); devices.bulb.interview.mockClear(); devices.bulb.interview.mockImplementation(() => Promise.reject(new Error('something went wrong'))); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/interview', stringify({id: 'bulb'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/interview', stringify({data: {}, status: 'error', error: "interview of 'bulb' (0x000b57fffec6a5b2) failed: Error: something went wrong"}), {retain: false, qos: 0}, @@ -3151,10 +3159,10 @@ describe('Extension: Bridge', () => { }); it('Should error when generate_external_definition is invalid', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/generate_external_definition', stringify({wrong: devices.ZNCZ02LM.ieeeAddr})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/generate_external_definition', stringify({data: {}, error: 'Invalid payload', status: 'error'}), {retain: false, qos: 0}, @@ -3162,10 +3170,10 @@ describe('Extension: Bridge', () => { }); it('Should error when generate_external_definition requested for unknown device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/generate_external_definition', stringify({id: 'non_existing_device'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/generate_external_definition', stringify({data: {}, error: "Device 'non_existing_device' does not exist", status: 'error'}), {retain: false, qos: 0}, @@ -3173,10 +3181,10 @@ describe('Extension: Bridge', () => { }); it('Should allow to generate device definition', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/generate_external_definition', stringify({id: devices.ZNCZ02LM.ieeeAddr})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/generate_external_definition', stringify({ data: { @@ -3202,7 +3210,7 @@ describe('Extension: Bridge', () => { }); it('Should allow change device options', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); expect(settings.getDevice('bulb')).toStrictEqual({ ID: '0x000b57fffec6a5b2', friendly_name: 'bulb', @@ -3218,7 +3226,7 @@ describe('Extension: Bridge', () => { transition: 1, description: 'this is my bulb', }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/options', stringify({ data: { @@ -3234,7 +3242,7 @@ describe('Extension: Bridge', () => { }); it('Should allow to remove device option', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); settings.set(['devices', '0x000b57fffec6a5b2', 'qos'], 1); expect(settings.getDevice('bulb')).toStrictEqual({ ID: '0x000b57fffec6a5b2', @@ -3251,7 +3259,7 @@ describe('Extension: Bridge', () => { retain: true, description: 'this is my bulb', }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/options', stringify({ data: { @@ -3267,7 +3275,7 @@ describe('Extension: Bridge', () => { }); it('Should allow change device options with restart required', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); expect(settings.getDevice('bulb')).toStrictEqual({ ID: '0x000b57fffec6a5b2', friendly_name: 'bulb', @@ -3283,7 +3291,7 @@ describe('Extension: Bridge', () => { disabled: true, description: 'this is my bulb', }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/options', stringify({ data: { @@ -3299,12 +3307,12 @@ describe('Extension: Bridge', () => { }); it('Should allow change group options', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, friendly_name: 'group_1', retain: false}); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/options', stringify({options: {retain: true, transition: 1}, id: 'group_1'})); await flushPromises(); expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, friendly_name: 'group_1', retain: true, transition: 1}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/options', stringify({data: {from: {retain: false}, to: {retain: true, transition: 1}, restart_required: false, id: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -3312,7 +3320,7 @@ describe('Extension: Bridge', () => { }); it('Should allow change group options with restart required', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); expect(settings.getGroup('group_1')).toStrictEqual({ID: 1, friendly_name: 'group_1', retain: false}); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/options', stringify({options: {off_state: 'all_members_off'}, id: 'group_1'})); await flushPromises(); @@ -3322,7 +3330,7 @@ describe('Extension: Bridge', () => { retain: false, off_state: 'all_members_off', }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/options', stringify({ data: {from: {retain: false}, to: {retain: false, off_state: 'all_members_off'}, restart_required: true, id: 'group_1'}, @@ -3333,10 +3341,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error on invalid device change options payload', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/options', stringify({options_: {retain: true, transition: 1}, id: 'bulb'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/options', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -3344,12 +3352,12 @@ describe('Extension: Bridge', () => { }); it('Should allow to add group by string', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/add', 'group_193'); await flushPromises(); expect(settings.getGroup('group_193')).toStrictEqual({ID: 3, friendly_name: 'group_193'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/add', stringify({data: {friendly_name: 'group_193', id: 3}, status: 'ok'}), {retain: false, qos: 0}, @@ -3357,12 +3365,12 @@ describe('Extension: Bridge', () => { }); it('Should allow to add group with ID', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name: 'group_193', id: 92})); await flushPromises(); expect(settings.getGroup('group_193')).toStrictEqual({ID: 92, friendly_name: 'group_193'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/add', stringify({data: {friendly_name: 'group_193', id: 92}, status: 'ok'}), {retain: false, qos: 0}, @@ -3370,10 +3378,10 @@ describe('Extension: Bridge', () => { }); it('Shouldnt allow to add group with empty name', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name: '', id: 9})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/add', stringify({data: {}, status: 'error', error: 'friendly_name must be at least 1 char long'}), {retain: false, qos: 0}, @@ -3381,10 +3389,10 @@ describe('Extension: Bridge', () => { }); it('Should throw error when add with invalid payload', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/add', stringify({friendly_name9: 'group_193'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/add', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -3392,13 +3400,13 @@ describe('Extension: Bridge', () => { }); it('Should allow to touchlink factory reset (succeeds)', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.touchlinkFactoryResetFirst.mockClear(); mockZHController.touchlinkFactoryResetFirst.mockReturnValueOnce(true); mockMQTTEvents.message('zigbee2mqtt/bridge/request/touchlink/factory_reset', ''); await flushPromises(); expect(mockZHController.touchlinkFactoryResetFirst).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/touchlink/factory_reset', stringify({data: {}, status: 'ok'}), {retain: false, qos: 0}, @@ -3406,14 +3414,14 @@ describe('Extension: Bridge', () => { }); it('Should allow to touchlink factory reset specific device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.touchlinkFactoryReset.mockClear(); mockZHController.touchlinkFactoryReset.mockReturnValueOnce(true); mockMQTTEvents.message('zigbee2mqtt/bridge/request/touchlink/factory_reset', stringify({ieee_address: '0x1239', channel: 12})); await flushPromises(); expect(mockZHController.touchlinkFactoryReset).toHaveBeenCalledTimes(1); expect(mockZHController.touchlinkFactoryReset).toHaveBeenCalledWith('0x1239', 12); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/touchlink/factory_reset', stringify({data: {ieee_address: '0x1239', channel: 12}, status: 'ok'}), {retain: false, qos: 0}, @@ -3421,7 +3429,7 @@ describe('Extension: Bridge', () => { }); it('Add install code', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // By object mockZHController.addInstallCode.mockClear(); @@ -3429,7 +3437,7 @@ describe('Extension: Bridge', () => { await flushPromises(); expect(mockZHController.addInstallCode).toHaveBeenCalledTimes(1); expect(mockZHController.addInstallCode).toHaveBeenCalledWith('my-code'); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/install_code/add', stringify({data: {value: 'my-code'}, status: 'ok'}), {retain: false, qos: 0}, @@ -3441,7 +3449,7 @@ describe('Extension: Bridge', () => { await flushPromises(); expect(mockZHController.addInstallCode).toHaveBeenCalledTimes(1); expect(mockZHController.addInstallCode).toHaveBeenCalledWith('my-string-code'); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/install_code/add', stringify({data: {value: 'my-code'}, status: 'ok'}), {retain: false, qos: 0}, @@ -3449,12 +3457,12 @@ describe('Extension: Bridge', () => { }); it('Add install code error', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.addInstallCode.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/install_code/add', stringify({wrong: 'my-code'})); await flushPromises(); expect(mockZHController.addInstallCode).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/install_code/add', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -3462,13 +3470,13 @@ describe('Extension: Bridge', () => { }); it('Should allow to touchlink identify specific device', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.touchlinkIdentify.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/touchlink/identify', stringify({ieee_address: '0x1239', channel: 12})); await flushPromises(); expect(mockZHController.touchlinkIdentify).toHaveBeenCalledTimes(1); expect(mockZHController.touchlinkIdentify).toHaveBeenCalledWith('0x1239', 12); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/touchlink/identify', stringify({data: {ieee_address: '0x1239', channel: 12}, status: 'ok'}), {retain: false, qos: 0}, @@ -3476,12 +3484,12 @@ describe('Extension: Bridge', () => { }); it('Touchlink identify fails when payload is invalid', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.touchlinkIdentify.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/touchlink/identify', stringify({ieee_address: '0x1239'})); await flushPromises(); expect(mockZHController.touchlinkIdentify).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/touchlink/identify', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -3489,13 +3497,13 @@ describe('Extension: Bridge', () => { }); it('Should allow to touchlink factory reset (fails)', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.touchlinkFactoryResetFirst.mockClear(); mockZHController.touchlinkFactoryResetFirst.mockReturnValueOnce(false); mockMQTTEvents.message('zigbee2mqtt/bridge/request/touchlink/factory_reset', ''); await flushPromises(); expect(mockZHController.touchlinkFactoryResetFirst).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/touchlink/factory_reset', stringify({data: {}, status: 'error', error: 'Failed to factory reset device through Touchlink'}), {retain: false, qos: 0}, @@ -3503,7 +3511,7 @@ describe('Extension: Bridge', () => { }); it('Should allow to touchlink scan', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockZHController.touchlinkScan.mockClear(); mockZHController.touchlinkScan.mockReturnValueOnce([ {ieeeAddr: '0x123', channel: 12}, @@ -3512,7 +3520,7 @@ describe('Extension: Bridge', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/touchlink/scan', ''); await flushPromises(); expect(mockZHController.touchlinkScan).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/touchlink/scan', stringify({ data: { @@ -3532,7 +3540,7 @@ describe('Extension: Bridge', () => { const endpoint = device.getEndpoint(1)!; endpoint.bind.mockClear(); endpoint.configureReporting.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/device/configure_reporting', stringify({ @@ -3554,7 +3562,7 @@ describe('Extension: Bridge', () => { [{attribute: 'currentLevel', maximumReportInterval: 10, minimumReportInterval: 1, reportableChange: 1}], undefined, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure_reporting', stringify({ data: { @@ -3570,7 +3578,7 @@ describe('Extension: Bridge', () => { }), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); }); it('Should allow to configure reporting with endpoint as string', async () => { @@ -3578,7 +3586,7 @@ describe('Extension: Bridge', () => { const endpoint = device.getEndpoint(1)!; endpoint.bind.mockClear(); endpoint.configureReporting.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/device/configure_reporting', stringify({ @@ -3600,7 +3608,7 @@ describe('Extension: Bridge', () => { [{attribute: 'currentLevel', maximumReportInterval: 10, minimumReportInterval: 1, reportableChange: 1}], undefined, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure_reporting', stringify({ data: { @@ -3616,14 +3624,14 @@ describe('Extension: Bridge', () => { }), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); }); it('Should throw error when configure reporting is called with malformed payload', async () => { const device = devices.bulb; const endpoint = device.getEndpoint(1)!; endpoint.configureReporting.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/device/configure_reporting', stringify({ @@ -3638,7 +3646,7 @@ describe('Extension: Bridge', () => { ); await flushPromises(); expect(endpoint.configureReporting).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure_reporting', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -3649,7 +3657,7 @@ describe('Extension: Bridge', () => { const device = devices.bulb; const endpoint = device.getEndpoint(1)!; endpoint.configureReporting.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/device/configure_reporting', stringify({ @@ -3664,7 +3672,7 @@ describe('Extension: Bridge', () => { ); await flushPromises(); expect(endpoint.configureReporting).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure_reporting', stringify({data: {}, status: 'error', error: "Device 'non_existing_device' does not exist"}), {retain: false, qos: 0}, @@ -3675,7 +3683,7 @@ describe('Extension: Bridge', () => { const device = devices.bulb; const endpoint = device.getEndpoint(1)!; endpoint.configureReporting.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/device/configure_reporting', stringify({ @@ -3690,7 +3698,7 @@ describe('Extension: Bridge', () => { ); await flushPromises(); expect(endpoint.configureReporting).toHaveBeenCalledTimes(0); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure_reporting', stringify({data: {}, status: 'error', error: "Device '0x000b57fffec6a5b2' does not have endpoint 'non_existing_endpoint'"}), {retain: false, qos: 0}, @@ -3704,7 +3712,7 @@ describe('Extension: Bridge', () => { fs.writeFileSync(path.join(data.mockDir, 'log', 'log.log'), 'test123'); fs.mkdirSync(path.join(data.mockDir, 'ext_converters', '123')); fs.writeFileSync(path.join(data.mockDir, 'ext_converters', '123', 'myfile.js'), 'test123'); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/backup', ''); await flushPromises(); expect(mockZHController.backup).toHaveBeenCalledTimes(1); @@ -3715,7 +3723,7 @@ describe('Extension: Bridge', () => { expect(mockJSZipFile).toHaveBeenNthCalledWith(4, 'state.json', expect.any(Object)); expect(mockJSZipGenerateAsync).toHaveBeenCalledTimes(1); expect(mockJSZipGenerateAsync).toHaveBeenNthCalledWith(1, {type: 'base64'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/backup', stringify({data: {zip: 'THISISBASE64'}, status: 'ok'}), {retain: false, qos: 0}, @@ -3723,12 +3731,12 @@ describe('Extension: Bridge', () => { }); it('Should allow to restart', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/restart', ''); await flushPromises(); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(mockRestart).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/restart', stringify({data: {}, status: 'ok'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/restart', stringify({data: {}, status: 'ok'}), { retain: false, qos: 0, }); @@ -3737,27 +3745,33 @@ describe('Extension: Bridge', () => { it('Change options and apply - homeassistant', async () => { // @ts-expect-error private expect(controller.extensions.find((e) => e.constructor.name === 'HomeAssistant')).toBeUndefined(); - mockMQTT.publishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: {homeassistant: {enabled: true}}})); + // TODO: there appears to be a race condition somewhere in here, calls in `bridgeOptions` are not properly ordered when logged + await vi.advanceTimersByTimeAsync(10000); await flushPromises(); // @ts-expect-error private expect(controller.extensions.find((e) => e.constructor.name === 'HomeAssistant')).not.toBeUndefined(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {restart_required: true}, status: 'ok'}), {retain: false, qos: 0}, ); + // revert + mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: {homeassistant: {enabled: false}}})); + await flushPromises(); + // @ts-expect-error private + expect(controller.extensions.find((e) => e.constructor.name === 'HomeAssistant')).toBeUndefined(); }); it('Change options and apply - log_level', async () => { mockLogger.setLevel('info'); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: {advanced: {log_level: 'debug'}}})); await flushPromises(); expect(mockLogger.getLevel()).toStrictEqual('debug'); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {restart_required: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -3765,13 +3779,13 @@ describe('Extension: Bridge', () => { }); it('Change options and apply - log_debug_namespace_ignore', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const nsIgnore = '^zhc:legacy:fz:(tuya|moes)|^zh:ember:uart:|^zh:controller'; mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: {advanced: {log_debug_namespace_ignore: nsIgnore}}})); await flushPromises(); expect(mockLogger.getDebugNamespaceIgnore()).toStrictEqual(nsIgnore); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {restart_required: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -3781,7 +3795,7 @@ describe('Extension: Bridge', () => { it('Change options and apply - log_namespaced_levels', async () => { mockLogger.setLevel('info'); settings.apply({advanced: {log_namespaced_levels: {'zh:zstack': 'warning', 'z2m:mqtt': 'debug'}}}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/options', stringify({options: {advanced: {log_namespaced_levels: {'z2m:mqtt': 'warning', 'zh:zstack': null}}}}), @@ -3789,8 +3803,8 @@ describe('Extension: Bridge', () => { await flushPromises(); expect(settings.get().advanced.log_namespaced_levels).toStrictEqual({'z2m:mqtt': 'warning'}); expect(mockLogger.getNamespacedLevels()).toStrictEqual({'z2m:mqtt': 'warning'}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {restart_required: false}, status: 'ok'}), {retain: false, qos: 0}, @@ -3804,11 +3818,11 @@ describe('Extension: Bridge', () => { it('Change options restart required', async () => { settings.apply({serial: {port: '123'}}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: {serial: {port: '/dev/newport'}}})); await flushPromises(); expect(settings.get().serial.port).toBe('/dev/newport'); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {restart_required: true}, status: 'ok'}), {retain: false, qos: 0}, @@ -3817,14 +3831,14 @@ describe('Extension: Bridge', () => { it('Change options array', async () => { expect(settings.get().advanced.ext_pan_id).toStrictEqual([221, 221, 221, 221, 221, 221, 221, 221]); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/options', stringify({options: {advanced: {ext_pan_id: [220, 221, 221, 221, 221, 221, 221, 221]}}}), ); await flushPromises(); expect(settings.get().advanced.ext_pan_id).toStrictEqual([220, 221, 221, 221, 221, 221, 221, 221]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {restart_required: true}, status: 'ok'}), {retain: false, qos: 0}, @@ -3833,11 +3847,11 @@ describe('Extension: Bridge', () => { it('Change options with null', async () => { expect(settings.get().serial).toStrictEqual({disable_led: false, port: '/dev/dummy'}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: {serial: {disable_led: false, port: null}}})); await flushPromises(); expect(settings.get().serial).toStrictEqual({disable_led: false}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {restart_required: true}, status: 'ok'}), {retain: false, qos: 0}, @@ -3845,10 +3859,10 @@ describe('Extension: Bridge', () => { }); it('Change options invalid payload', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', 'I am invalid'); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {}, error: 'Invalid payload', status: 'error'}), {retain: false, qos: 0}, @@ -3856,10 +3870,10 @@ describe('Extension: Bridge', () => { }); it('Change options not valid against schema', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/options', stringify({options: {advanced: {log_level: 123}}})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/options', stringify({data: {}, error: 'advanced/log_level must be string', status: 'error'}), {retain: false, qos: 0}, @@ -3930,19 +3944,19 @@ describe('Extension: Bridge', () => { }); it('Should publish bridge info, devices and definitions when a device with custom_clusters joined', async () => { - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.deviceJoined({device: devices.bulb_custom_cluster}); await flushPromises(); // console.log(mockMQTT.publish.mock.calls); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(5); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.stringContaining(stringify(CUSTOM_CLUSTERS)), { + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(5); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.stringContaining(stringify(CUSTOM_CLUSTERS)), { retain: true, qos: 0, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/event', stringify({data: {friendly_name: '0x000b57fffec6a5c2', ieee_address: '0x000b57fffec6a5c2'}, type: 'device_joined'}), {retain: false, qos: 0}, @@ -3953,21 +3967,21 @@ describe('Extension: Bridge', () => { // Adding a device first await mockZHEvents.deviceJoined({device: devices.bulb_custom_cluster}); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // After cleaning, reconfigure it mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', devices.bulb_custom_cluster.ieeeAddr); await flushPromises(); // console.log(mockMQTT.publish.mock.calls); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(4); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.stringContaining(stringify(CUSTOM_CLUSTERS)), { + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(4); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/info', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/devices', expect.any(String), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/definitions', expect.stringContaining(stringify(CUSTOM_CLUSTERS)), { retain: true, qos: 0, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/device/configure', expect.any(String), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/device/configure', expect.any(String), { retain: false, qos: 0, }); diff --git a/test/extensions/configure.test.ts b/test/extensions/configure.test.ts index 44c4c2e7..3719f68f 100644 --- a/test/extensions/configure.test.ts +++ b/test/extensions/configure.test.ts @@ -1,6 +1,6 @@ import * as data from '../mocks/data'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; +import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt'; import {flushPromises} from '../mocks/utils'; import {Device, devices, Endpoint, events as mockZHEvents} from '../mocks/zigbeeHerdsman'; @@ -9,7 +9,7 @@ import stringify from 'json-stable-stringify-without-jsonify'; import {Controller} from '../../lib/controller'; import * as settings from '../../lib/util/settings'; -const mocksClear = [mockMQTT.publishAsync, mockLogger.warning, mockLogger.debug]; +const mocksClear = [mockMQTTPublishAsync, mockLogger.warning, mockLogger.debug]; describe('Extension: Configure', () => { let controller: Controller; @@ -65,10 +65,10 @@ describe('Extension: Configure', () => { const wait = async (ms: number): Promise => await new Promise((resolve) => setTimeout(resolve, ms)); beforeAll(async () => { - jest.useFakeTimers(); - controller = new Controller(jest.fn(), jest.fn()); + vi.useFakeTimers(); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); - await jest.runOnlyPendingTimersAsync(); + await vi.runOnlyPendingTimersAsync(); }); beforeEach(async () => { @@ -77,11 +77,11 @@ describe('Extension: Configure', () => { mocksClear.forEach((m) => m.mockClear()); coordinatorEndpoint = devices.coordinator.getEndpoint(1)!; await resetExtension(); - await jest.runOnlyPendingTimersAsync(); + await vi.runOnlyPendingTimersAsync(); }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); it('Should configure Router on startup', async () => { @@ -150,7 +150,7 @@ describe('Extension: Configure', () => { await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', 'remote'); await flushPromises(); expectRemoteConfigured(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure', stringify({data: {id: 'remote'}, status: 'ok'}), {retain: false, qos: 0}, @@ -160,7 +160,7 @@ describe('Extension: Configure', () => { it('Fail to configure via MQTT when device does not exist', async () => { await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: 'not_existing_device'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure', stringify({data: {}, status: 'error', error: "Device 'not_existing_device' does not exist"}), {retain: false, qos: 0}, @@ -173,7 +173,7 @@ describe('Extension: Configure', () => { }); await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: 'remote'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure', stringify({data: {}, status: 'error', error: 'Failed to configure (Bind timeout after 10s)'}), {retain: false, qos: 0}, @@ -183,7 +183,7 @@ describe('Extension: Configure', () => { it('Fail to configure via MQTT when device has no configure', async () => { await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: '0x0017882104a44559', transaction: 20})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure', stringify({data: {}, status: 'error', error: "Device 'TS0601_thermostat' cannot be configured", transaction: 20}), {retain: false, qos: 0}, @@ -193,7 +193,7 @@ describe('Extension: Configure', () => { it('Handles invalid payload for configure via MQTT', async () => { await mockMQTTEvents.message('zigbee2mqtt/bridge/request/device/configure', stringify({idx: '0x0017882104a44559'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/device/configure', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, diff --git a/test/extensions/externalConverters.test.ts b/test/extensions/externalConverters.test.ts index 6322bc83..0fa50e6a 100644 --- a/test/extensions/externalConverters.test.ts +++ b/test/extensions/externalConverters.test.ts @@ -1,6 +1,6 @@ import * as data from '../mocks/data'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; +import {mockMQTTEndAsync, events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt'; import {flushPromises} from '../mocks/utils'; import {devices, mockController as mockZHController, returnDevices} from '../mocks/zigbeeHerdsman'; @@ -22,18 +22,18 @@ describe('Extension: ExternalConverters', () => { const mockBasePath = path.join(data.mockDir, BASE_DIR); let controller: Controller; - const existsSyncSpy = jest.spyOn(fs, 'existsSync'); - const readdirSyncSpy = jest.spyOn(fs, 'readdirSync'); - const mkdirSyncSpy = jest.spyOn(fs, 'mkdirSync'); - const rmSyncSpy = jest.spyOn(fs, 'rmSync'); - const writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync'); + const existsSyncSpy = vi.spyOn(fs, 'existsSync'); + const readdirSyncSpy = vi.spyOn(fs, 'readdirSync'); + const mkdirSyncSpy = vi.spyOn(fs, 'mkdirSync'); + const rmSyncSpy = vi.spyOn(fs, 'rmSync'); + const writeFileSyncSpy = vi.spyOn(fs, 'writeFileSync'); - const zhcAddDefinitionSpy = jest.spyOn(zhc, 'addDefinition'); - const zhcRemoveExternalDefinitionsSpy = jest.spyOn(zhc, 'removeExternalDefinitions'); + const zhcAddDefinitionSpy = vi.spyOn(zhc, 'addDefinition'); + const zhcRemoveExternalDefinitionsSpy = vi.spyOn(zhc, 'removeExternalDefinitions'); const mocksClear = [ - mockMQTT.endAsync, - mockMQTT.publishAsync, + mockMQTTEndAsync, + mockMQTTPublishAsync, mockLogger.debug, mockLogger.error, mockZHController.stop, @@ -60,12 +60,21 @@ describe('Extension: ExternalConverters', () => { return controller.zigbee.resolveEntity(zhDevice)! as Device; }; + const resetExtension = async (): Promise => { + await controller.enableDisableExtension(false, 'ExternalConverters'); + await controller.enableDisableExtension(true, 'ExternalConverters'); + }; + beforeAll(async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); + + controller = new Controller(vi.fn(), vi.fn()); + await controller.start(); + await flushPromises(); }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); beforeEach(async () => { @@ -75,8 +84,6 @@ describe('Extension: ExternalConverters', () => { data.writeDefaultState(); settings.reRead(); returnDevices.push(devices.external_converter_device.ieeeAddr, devices.coordinator.ieeeAddr); - - controller = new Controller(jest.fn(), jest.fn()); }); afterEach(async () => { @@ -85,143 +92,153 @@ describe('Extension: ExternalConverters', () => { await controller?.stop(); }); - it('loads nothing from folder', async () => { - await controller.start(); - await flushPromises(); - - expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath); - expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0}); - }); - - it('loads from folder', async () => { - useAssets(); - - await controller.start(); - await flushPromises(); - - expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ - description: 'external', - model: 'external_converter_device', - vendor: 'external', - zigbeeModel: ['external_converter_device'], + describe('from folder', () => { + beforeEach(async () => { + controller = new Controller(vi.fn(), vi.fn()); }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( - 'zigbee2mqtt/bridge/converters', - stringify([ - {name: 'mock-external-converter-multiple.js', code: getFileCode('mock-external-converter-multiple.js')}, - {name: 'mock-external-converter.js', code: getFileCode('mock-external-converter.js')}, - ]), - {retain: true, qos: 0}, - ); - expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2); - expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, 'mock-external-converter-multiple.js'); - expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, 'mock-external-converter.js'); - expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - mock: 1, - model: 'external_converters_device_1', - zigbeeModel: ['external_converter_device_1'], - vendor: 'external_1', - description: 'external_1', - }), - ); - expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - mock: 2, - model: 'external_converters_device_2', - zigbeeModel: ['external_converter_device_2'], - vendor: 'external_2', - description: 'external_2', - }), - ); - expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith( - 3, - expect.objectContaining({ - mock: true, - zigbeeModel: ['external_converter_device'], - vendor: 'external', - model: 'external_converter_device', - description: 'external', - }), - ); - const bridgeDevices = mockMQTT.publishAsync.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/devices'); - expect(bridgeDevices.length).toBe(1); - expect(JSON.parse(bridgeDevices[0][1])).toEqual( - expect.arrayContaining([ + it('loads nothing', async () => { + await controller.start(); + await flushPromises(); + + expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath); + expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0}); + }); + + it('loads converters', async () => { + useAssets(); + + await controller.start(); + await flushPromises(); + + expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ + description: 'external', + model: 'external_converter_device', + vendor: 'external', + zigbeeModel: ['external_converter_device'], + }); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/converters', + stringify([ + {name: 'mock-external-converter-multiple.js', code: getFileCode('mock-external-converter-multiple.js')}, + {name: 'mock-external-converter.js', code: getFileCode('mock-external-converter.js')}, + ]), + {retain: true, qos: 0}, + ); + expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2); + expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, 'mock-external-converter-multiple.js'); + expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, 'mock-external-converter.js'); + expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith( + 1, expect.objectContaining({ - model_id: 'external_converter_device', - supported: true, - definition: expect.objectContaining({ - description: 'external', - model: 'external_converter_device', - }), + mock: 1, + model: 'external_converters_device_1', + zigbeeModel: ['external_converter_device_1'], + vendor: 'external_1', + description: 'external_1', }), - ]), - ); - }); + ); + expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + mock: 2, + model: 'external_converters_device_2', + zigbeeModel: ['external_converter_device_2'], + vendor: 'external_2', + description: 'external_2', + }), + ); + expect(zhcAddDefinitionSpy).toHaveBeenNthCalledWith( + 3, + expect.objectContaining({ + mock: true, + zigbeeModel: ['external_converter_device'], + vendor: 'external', + model: 'external_converter_device', + description: 'external', + }), + ); - it('saves and removes from MQTT', async () => { - const converterName = 'foo.js'; - const converterCode = getFileCode('mock-external-converter.js'); - const converterFilePath = path.join(mockBasePath, converterName); - - await controller.start(); - await flushPromises(); - mocksClear.forEach((m) => m.mockClear()); - - expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ - description: 'Automatically generated definition', - model: 'external_converter_device', - vendor: '', - zigbeeModel: ['external_converter_device'], + const bridgeDevices = mockMQTTPublishAsync.mock.calls.filter((c) => c[0] === 'zigbee2mqtt/bridge/devices'); + expect(bridgeDevices.length).toBe(1); + expect(JSON.parse(bridgeDevices[0][1])).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + model_id: 'external_converter_device', + supported: true, + definition: expect.objectContaining({ + description: 'external', + model: 'external_converter_device', + }), + }), + ]), + ); }); - //-- SAVE - mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode})); - await flushPromises(); + it('saves and removes from MQTT', async () => { + const converterName = 'foo.js'; + const converterCode = getFileCode('mock-external-converter.js'); + const converterFilePath = path.join(mockBasePath, converterName); - expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ - description: 'external', - model: 'external_converter_device', - vendor: 'external', - zigbeeModel: ['external_converter_device'], - }); - expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true}); - expect(writeFileSyncSpy).toHaveBeenCalledWith(converterFilePath, converterCode, 'utf8'); - expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(1); - expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, converterName); - expect(zhcAddDefinitionSpy).toHaveBeenCalledWith( - expect.objectContaining({ - mock: true, - zigbeeModel: ['external_converter_device'], - vendor: 'external', + await controller.start(); + await flushPromises(); + mocksClear.forEach((m) => m.mockClear()); + + expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ + description: 'Automatically generated definition', model: 'external_converter_device', + vendor: '', + zigbeeModel: ['external_converter_device'], + }); + + //-- SAVE + mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode})); + await flushPromises(); + + expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ description: 'external', - }), - ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([{name: converterName, code: converterCode}]), { - retain: true, - qos: 0, - }); + model: 'external_converter_device', + vendor: 'external', + zigbeeModel: ['external_converter_device'], + }); + expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true}); + expect(writeFileSyncSpy).toHaveBeenCalledWith(converterFilePath, converterCode, 'utf8'); + expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(1); + expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(1, converterName); + expect(zhcAddDefinitionSpy).toHaveBeenCalledWith( + expect.objectContaining({ + mock: true, + zigbeeModel: ['external_converter_device'], + vendor: 'external', + model: 'external_converter_device', + description: 'external', + }), + ); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/converters', + stringify([{name: converterName, code: converterCode}]), + { + retain: true, + qos: 0, + }, + ); - //-- REMOVE - mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName})); - await flushPromises(); + //-- REMOVE + mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName})); + await flushPromises(); - expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ - description: 'Automatically generated definition', - model: 'external_converter_device', - vendor: '', - zigbeeModel: ['external_converter_device'], + expect(getZ2MDevice(devices.external_converter_device).definition).toMatchObject({ + description: 'Automatically generated definition', + model: 'external_converter_device', + vendor: '', + zigbeeModel: ['external_converter_device'], + }); + expect(rmSyncSpy).toHaveBeenCalledWith(converterFilePath, {force: true}); + expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2); + expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, converterName); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0}); }); - expect(rmSyncSpy).toHaveBeenCalledWith(converterFilePath, {force: true}); - expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenCalledTimes(2); - expect(zhcRemoveExternalDefinitionsSpy).toHaveBeenNthCalledWith(2, converterName); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/converters', stringify([]), {retain: true, qos: 0}); }); it('returns error on invalid code', async () => { @@ -229,14 +246,13 @@ describe('Extension: ExternalConverters', () => { const converterCode = 'definetly not a correct javascript code'; const converterFilePath = path.join(mockBasePath, converterName); - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/converter/save', expect.stringContaining(`"error":"foo.js contains invalid code`), {retain: false, qos: 0}, @@ -248,14 +264,13 @@ describe('Extension: ExternalConverters', () => { const converterName = 'invalid.js'; const converterFilePath = path.join(mockBasePath, converterName); - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/converter/remove', stringify({data: {}, status: 'error', error: `${converterName} (${converterFilePath}) doesn't exists`}), {retain: false, qos: 0}, @@ -268,8 +283,7 @@ describe('Extension: ExternalConverters', () => { const converterCode = getFileCode('mock-external-converter.js'); const converterFilePath = path.join(mockBasePath, converterName); - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); const errorMsg = `Invalid definition`; @@ -281,7 +295,7 @@ describe('Extension: ExternalConverters', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: converterName, code: converterCode})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/converter/save', expect.stringContaining(errorMsg), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/response/converter/save', expect.stringContaining(errorMsg), { retain: false, qos: 0, }); @@ -293,8 +307,7 @@ describe('Extension: ExternalConverters', () => { const converterCode = getFileCode('mock-external-converter.js'); const converterFilePath = path.join(mockBasePath, converterName); - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); //-- SAVE @@ -311,7 +324,7 @@ describe('Extension: ExternalConverters', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({name: converterName})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/converter/remove', stringify({data: {}, status: 'error', error: errorMsg}), {retain: false, qos: 0}, @@ -320,14 +333,13 @@ describe('Extension: ExternalConverters', () => { }); it('handles invalid payloads', async () => { - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/save', stringify({name: 'test.js', transaction: 1 /* code */})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/converter/save', stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 1}), {retain: false, qos: 0}, @@ -336,7 +348,7 @@ describe('Extension: ExternalConverters', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/converter/remove', stringify({namex: 'test.js', transaction: 2})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/converter/remove', stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 2}), {retain: false, qos: 0}, diff --git a/test/extensions/externalExtensions.test.ts b/test/extensions/externalExtensions.test.ts index 0190208a..f6a54630 100644 --- a/test/extensions/externalExtensions.test.ts +++ b/test/extensions/externalExtensions.test.ts @@ -1,6 +1,6 @@ import * as data from '../mocks/data'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; +import {mockMQTTEndAsync, events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt'; import {flushPromises} from '../mocks/utils'; import {devices, mockController as mockZHController, returnDevices} from '../mocks/zigbeeHerdsman'; @@ -18,15 +18,15 @@ describe('Extension: ExternalExtensions', () => { let controller: Controller; const mockBasePath = path.join(data.mockDir, BASE_DIR); - const existsSyncSpy = jest.spyOn(fs, 'existsSync'); - const readdirSyncSpy = jest.spyOn(fs, 'readdirSync'); - const mkdirSyncSpy = jest.spyOn(fs, 'mkdirSync'); - const rmSyncSpy = jest.spyOn(fs, 'rmSync'); - const writeFileSyncSpy = jest.spyOn(fs, 'writeFileSync'); + const existsSyncSpy = vi.spyOn(fs, 'existsSync'); + const readdirSyncSpy = vi.spyOn(fs, 'readdirSync'); + const mkdirSyncSpy = vi.spyOn(fs, 'mkdirSync'); + const rmSyncSpy = vi.spyOn(fs, 'rmSync'); + const writeFileSyncSpy = vi.spyOn(fs, 'writeFileSync'); const mocksClear = [ - mockMQTT.endAsync, - mockMQTT.publishAsync, + mockMQTTEndAsync, + mockMQTTPublishAsync, mockLogger.debug, mockLogger.error, mockZHController.stop, @@ -46,12 +46,21 @@ describe('Extension: ExternalExtensions', () => { return fs.readFileSync(path.join(__dirname, '..', 'assets', BASE_DIR, fileName), 'utf8'); }; + const resetExtension = async (): Promise => { + await controller.enableDisableExtension(false, 'ExternalExtensions'); + await controller.enableDisableExtension(true, 'ExternalExtensions'); + }; + beforeAll(async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); + + controller = new Controller(vi.fn(), vi.fn()); + await controller.start(); + await flushPromises(); }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); beforeEach(async () => { @@ -60,72 +69,80 @@ describe('Extension: ExternalExtensions', () => { data.writeDefaultState(); settings.reRead(); returnDevices.splice(0); - - controller = new Controller(jest.fn(), jest.fn()); }); afterEach(() => { fs.rmSync(mockBasePath, {recursive: true, force: true}); }); - it('loads nothing from folder', async () => { - await controller.start(); - await flushPromises(); - - expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath); - expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0}); - }); - - it('loads from folder', async () => { - useAssets(); - - await controller.start(); - await flushPromises(); - - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from constructor', {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from start', {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( - 'zigbee2mqtt/bridge/extensions', - stringify([ - {name: 'example2Extension.js', code: getFileCode('example2Extension.js')}, - {name: 'exampleExtension.js', code: getFileCode('exampleExtension.js')}, - ]), - {retain: true, qos: 0}, - ); - }); - - it('saves and removes from MQTT', async () => { - const extensionName = 'foo.js'; - const extensionCode = getFileCode('exampleExtension.js'); - const extensionFilePath = path.join(mockBasePath, extensionName); - - await controller.start(); - await flushPromises(); - mocksClear.forEach((m) => m.mockClear()); - - //-- SAVE - mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: extensionName, code: extensionCode})); - await flushPromises(); - - expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true}); - expect(writeFileSyncSpy).toHaveBeenCalledWith(extensionFilePath, extensionCode, 'utf8'); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([{name: extensionName, code: extensionCode}]), { - retain: true, - qos: 0, + describe('from folder', () => { + beforeEach(async () => { + controller = new Controller(vi.fn(), vi.fn()); }); - //-- REMOVE - mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({name: extensionName})); - await flushPromises(); + it('loads nothing', async () => { + await controller.start(); + await flushPromises(); - expect(rmSyncSpy).toHaveBeenCalledWith(extensionFilePath, {force: true}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from stop', {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0}); + expect(existsSyncSpy).toHaveBeenCalledWith(mockBasePath); + expect(readdirSyncSpy).not.toHaveBeenCalledWith(mockBasePath); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0}); + }); + + it('loads extensions', async () => { + useAssets(); + + await controller.start(); + await flushPromises(); + + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from constructor', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example2/extension', 'call2 from start', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/extensions', + stringify([ + {name: 'example2Extension.js', code: getFileCode('example2Extension.js')}, + {name: 'exampleExtension.js', code: getFileCode('exampleExtension.js')}, + ]), + {retain: true, qos: 0}, + ); + }); + + it('saves and removes from MQTT', async () => { + const extensionName = 'foo.js'; + const extensionCode = getFileCode('exampleExtension.js'); + const extensionFilePath = path.join(mockBasePath, extensionName); + + await controller.start(); + await flushPromises(); + mocksClear.forEach((m) => m.mockClear()); + + //-- SAVE + mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: extensionName, code: extensionCode})); + await flushPromises(); + + expect(mkdirSyncSpy).toHaveBeenCalledWith(mockBasePath, {recursive: true}); + expect(writeFileSyncSpy).toHaveBeenCalledWith(extensionFilePath, extensionCode, 'utf8'); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from constructor', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from start', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( + 'zigbee2mqtt/bridge/extensions', + stringify([{name: extensionName, code: extensionCode}]), + { + retain: true, + qos: 0, + }, + ); + + //-- REMOVE + mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({name: extensionName})); + await flushPromises(); + + expect(rmSyncSpy).toHaveBeenCalledWith(extensionFilePath, {force: true}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/example/extension', 'call from stop', {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/extensions', stringify([]), {retain: true, qos: 0}); + }); }); it('returns error on invalid code', async () => { @@ -133,14 +150,13 @@ describe('Extension: ExternalExtensions', () => { const extensionCode = 'definetly not a correct javascript code'; const extensionFilePath = path.join(mockBasePath, extensionName); - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: extensionName, code: extensionCode})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/extension/save', expect.stringContaining(`"error":"${extensionName} contains invalid code`), {retain: false, qos: 0}, @@ -152,14 +168,13 @@ describe('Extension: ExternalExtensions', () => { const converterName = 'invalid.js'; const converterFilePath = path.join(mockBasePath, converterName); - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({name: converterName})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/extension/remove', stringify({data: {}, status: 'error', error: `${converterName} (${converterFilePath}) doesn't exists`}), {retain: false, qos: 0}, @@ -168,14 +183,13 @@ describe('Extension: ExternalExtensions', () => { }); it('handles invalid payloads', async () => { - await controller.start(); - await flushPromises(); + await resetExtension(); mocksClear.forEach((m) => m.mockClear()); mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/save', stringify({name: 'test.js', transaction: 1 /* code */})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/extension/save', stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 1}), {retain: false, qos: 0}, @@ -184,7 +198,7 @@ describe('Extension: ExternalExtensions', () => { mockMQTTEvents.message('zigbee2mqtt/bridge/request/extension/remove', stringify({namex: 'test.js', transaction: 2})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/extension/remove', stringify({data: {}, status: 'error', error: `Invalid payload`, transaction: 2}), {retain: false, qos: 0}, diff --git a/test/extensions/frontend.test.ts b/test/extensions/frontend.test.ts index 15ed616d..81d3f717 100644 --- a/test/extensions/frontend.test.ts +++ b/test/extensions/frontend.test.ts @@ -1,6 +1,6 @@ import * as data from '../mocks/data'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT} from '../mocks/mqtt'; +import {mockMQTTPublishAsync} from '../mocks/mqtt'; import {EventHandler, flushPromises} from '../mocks/utils'; import {devices} from '../mocks/zigbeeHerdsman'; @@ -15,26 +15,26 @@ import * as settings from '../../lib/util/settings'; let mockHTTPOnRequest: (request: {url: string}, response: number) => void; const mockHTTPEvents: Record = {}; const mockHTTP = { - listen: jest.fn(), + listen: vi.fn(), on: (event: string, handler: EventHandler): void => { mockHTTPEvents[event] = handler; }, - close: jest.fn().mockImplementation((cb) => cb()), + close: vi.fn<(cb: (err?: Error) => void) => void>((cb) => cb()), }; // eslint-disable-next-line @typescript-eslint/no-unused-vars let mockHTTPSOnRequest: (request: {url: string}, response: number) => void; const mockHTTPSEvents: Record = {}; const mockHTTPS = { - listen: jest.fn(), + listen: vi.fn(), on: (event: string, handler: EventHandler): void => { mockHTTPSEvents[event] = handler; }, - close: jest.fn().mockImplementation((cb) => cb()), + close: vi.fn<(cb: (err?: Error) => void) => void>((cb) => cb()), }; const mockWSocket = { - close: jest.fn(), + close: vi.fn<(cb: (err?: Error) => void) => void>(), }; const mockWSClientEvents: Record = {}; @@ -42,8 +42,8 @@ const mockWSClient = { on: (event: string, handler: EventHandler): void => { mockWSClientEvents[event] = handler; }, - send: jest.fn(), - terminate: jest.fn(), + send: vi.fn<(data: string) => void>(), + terminate: vi.fn<() => void>(), readyState: 'close', }; const mockWSEvents: Record = {}; @@ -53,57 +53,62 @@ const mockWS = { on: (event: string, handler: EventHandler): void => { mockWSEvents[event] = handler; }, - handleUpgrade: jest.fn().mockImplementation((request, socket, head, cb) => { + handleUpgrade: vi.fn().mockImplementation((request, socket, head, cb) => { cb(mockWSocket); }), - emit: jest.fn(), - close: jest.fn(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + emit: vi.fn<(eventName: string, ...args: any[]) => void>(), + close: vi.fn<(code?: number, data?: string | Buffer) => void>(), }; let mockNodeStaticPath: string = ''; -const mockNodeStatic = jest.fn(); +const mockNodeStatic = vi.fn(); -const mockFinalHandler = jest.fn(); +const mockFinalHandler = vi.fn(); -jest.mock('node:http', () => ({ - createServer: jest.fn().mockImplementation((onRequest) => { +vi.mock('node:http', () => ({ + createServer: vi.fn().mockImplementation((onRequest) => { mockHTTPOnRequest = onRequest; return mockHTTP; }), - Agent: jest.fn(), + Agent: vi.fn(), })); -jest.mock('node:https', () => ({ - createServer: jest.fn().mockImplementation((onRequest) => { +vi.mock('node:https', () => ({ + createServer: vi.fn().mockImplementation((onRequest) => { mockHTTPSOnRequest = onRequest; return mockHTTPS; }), - Agent: jest.fn(), + Agent: vi.fn(), })); -jest.mock('express-static-gzip', () => - jest.fn().mockImplementation((path) => { +vi.mock('express-static-gzip', () => ({ + default: vi.fn().mockImplementation((path) => { mockNodeStaticPath = path; return mockNodeStatic; }), -); - -jest.mock('zigbee2mqtt-frontend', () => ({ - getPath: (): string => 'my/dummy/path', })); -jest.mock('ws', () => ({ - OPEN: 'open', - Server: jest.fn().mockImplementation(() => { - return mockWS; - }), +vi.mock('zigbee2mqtt-frontend', () => ({ + default: { + getPath: (): string => 'my/dummy/path', + }, })); -jest.mock('finalhandler', () => - jest.fn().mockImplementation(() => { +vi.mock('ws', () => ({ + default: { + OPEN: 'open', + Server: vi.fn().mockImplementation(() => { + return mockWS; + }), + }, +})); + +vi.mock('finalhandler', () => ({ + default: vi.fn().mockImplementation(() => { return mockFinalHandler; }), -); +})); const mocksClear = [ mockHTTP.close, @@ -118,7 +123,7 @@ const mocksClear = [ mockWSClient.terminate, mockNodeStatic, mockFinalHandler, - mockMQTT.publishAsync, + mockMQTTPublishAsync, mockLogger.error, ]; @@ -126,7 +131,7 @@ describe('Extension: Frontend', () => { let controller: Controller; beforeAll(async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); beforeEach(async () => { @@ -142,7 +147,7 @@ describe('Extension: Frontend', () => { }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); afterEach(async () => { @@ -150,7 +155,7 @@ describe('Extension: Frontend', () => { }); it('Start/stop with defaults', async () => { - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(mockNodeStaticPath).toBe('my/dummy/path'); expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1'); @@ -163,7 +168,7 @@ describe('Extension: Frontend', () => { it('Start/stop without host', async () => { settings.set(['frontend'], {enabled: true, port: 8081}); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(mockNodeStaticPath).toBe('my/dummy/path'); expect(mockHTTP.listen).toHaveBeenCalledWith(8081); @@ -176,7 +181,7 @@ describe('Extension: Frontend', () => { it('Start/stop unix socket', async () => { settings.set(['frontend', 'host'], '/tmp/zigbee2mqtt.sock'); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(mockNodeStaticPath).toBe('my/dummy/path'); expect(mockHTTP.listen).toHaveBeenCalledWith('/tmp/zigbee2mqtt.sock'); @@ -190,7 +195,7 @@ describe('Extension: Frontend', () => { it('Start/stop HTTPS valid', async () => { settings.set(['frontend', 'ssl_cert'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.crt')); settings.set(['frontend', 'ssl_key'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.key')); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(mockHTTP.listen).not.toHaveBeenCalledWith(8081, '127.0.0.1'); expect(mockHTTPS.listen).toHaveBeenCalledWith(8081, '127.0.0.1'); @@ -199,7 +204,7 @@ describe('Extension: Frontend', () => { it('Start/stop HTTPS invalid : missing config', async () => { settings.set(['frontend', 'ssl_cert'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.crt')); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1'); expect(mockHTTPS.listen).not.toHaveBeenCalledWith(8081, '127.0.0.1'); @@ -209,7 +214,7 @@ describe('Extension: Frontend', () => { it('Start/stop HTTPS invalid : missing file', async () => { settings.set(['frontend', 'ssl_cert'], 'filesNotExists.crt'); settings.set(['frontend', 'ssl_key'], path.join(__dirname, '..', 'assets', 'certs', 'dummy.key')); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1'); expect(mockHTTPS.listen).not.toHaveBeenCalledWith(8081, '127.0.0.1'); @@ -217,25 +222,25 @@ describe('Extension: Frontend', () => { }); it('Websocket interaction', async () => { - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); mockWSClient.readyState = 'open'; mockWS.clients.push(mockWSClient); await mockWSEvents.connection(mockWSClient); - const allTopics = mockWSClient.send.mock.calls.map((m) => JSON.parse(m).topic); + const allTopics = mockWSClient.send.mock.calls.map(([m]) => JSON.parse(m).topic); expect(allTopics).toContain('bridge/devices'); expect(allTopics).toContain('bridge/info'); expect(mockWSClient.send).toHaveBeenCalledWith(stringify({topic: 'bridge/state', payload: {state: 'online'}})); expect(mockWSClient.send).toHaveBeenCalledWith(stringify({topic: 'remote', payload: {brightness: 255}})); // Message - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockWSClient.send.mockClear(); mockWSClientEvents.message(stringify({topic: 'bulb_color/set', payload: {state: 'ON'}}), false); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb_color', stringify({ state: 'ON', @@ -288,10 +293,10 @@ describe('Extension: Frontend', () => { }); it('onRequest/onUpgrade', async () => { - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); - const mockSocket = {destroy: jest.fn()}; + const mockSocket = {destroy: vi.fn()}; mockHTTPEvents.upgrade({url: 'http://localhost:8080/api'}, mockSocket, 3); expect(mockWS.handleUpgrade).toHaveBeenCalledTimes(1); expect(mockSocket.destroy).toHaveBeenCalledTimes(0); @@ -305,7 +310,7 @@ describe('Extension: Frontend', () => { }); it('Static server', async () => { - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(mockHTTP.listen).toHaveBeenCalledWith(8081, '127.0.0.1'); @@ -314,10 +319,10 @@ describe('Extension: Frontend', () => { it('Authentification', async () => { const authToken = 'sample-secure-token'; settings.set(['frontend', 'auth_token'], authToken); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); - const mockSocket = {destroy: jest.fn()}; + const mockSocket = {destroy: vi.fn()}; mockHTTPEvents.upgrade({url: '/api'}, mockSocket, mockWSocket); expect(mockWS.handleUpgrade).toHaveBeenCalledTimes(1); expect(mockSocket.destroy).toHaveBeenCalledTimes(0); @@ -340,7 +345,7 @@ describe('Extension: Frontend', () => { it.each(['/z2m/', '/z2m'])('Works with non-default base url %s', async (baseUrl) => { settings.set(['frontend', 'base_url'], baseUrl); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(ws.Server).toHaveBeenCalledWith({noServer: true, path: '/z2m/api'}); @@ -366,7 +371,7 @@ describe('Extension: Frontend', () => { it('Works with non-default complex base url', async () => { const baseUrl = '/z2m-more++/c0mplex.url/'; settings.set(['frontend', 'base_url'], baseUrl); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); expect(ws.Server).toHaveBeenCalledWith({noServer: true, path: '/z2m-more++/c0mplex.url/api'}); diff --git a/test/extensions/groups.test.ts b/test/extensions/groups.test.ts index 7db66bcd..dea89444 100644 --- a/test/extensions/groups.test.ts +++ b/test/extensions/groups.test.ts @@ -1,6 +1,6 @@ import * as data from '../mocks/data'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; +import {events as mockMQTTEvents, mockMQTTPublishAsync} from '../mocks/mqtt'; import {flushPromises} from '../mocks/utils'; import {devices, groups, events as mockZHEvents, resetGroupMembers, returnDevices} from '../mocks/zigbeeHerdsman'; @@ -25,21 +25,21 @@ describe('Extension: Groups', () => { let controller: Controller; beforeAll(async () => { - jest.useFakeTimers(); - controller = new Controller(jest.fn(), jest.fn()); + vi.useFakeTimers(); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); await flushPromises(); }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); }); beforeEach(() => { resetGroupMembers(); data.writeDefaultConfiguration(); settings.reRead(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); groups.gledopto_group.command.mockClear(); zhcToZigbee.__clearStore__(); // @ts-expect-error private @@ -52,21 +52,21 @@ describe('Extension: Groups', () => { const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint, type: 'attributeReport', linkquality: 10}; await mockZHEvents.message(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Should not republish identical optimistic group states', async () => { const device1 = devices.bulb_2; const device2 = devices.bulb_color_2; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockZHEvents.message({ data: {onOff: 1}, cluster: 'genOnOff', @@ -84,13 +84,13 @@ describe('Extension: Groups', () => { linkquality: 10, }); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(6); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_2', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_with_tradfri', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/ha_discovery_group', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(6); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_2', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_with_tradfri', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/ha_discovery_group', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/switch_group', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Should publish state change of all members when a group changes its state', async () => { @@ -99,12 +99,12 @@ describe('Extension: Groups', () => { const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Should not publish state change when group changes state and device is disabled', async () => { @@ -114,11 +114,11 @@ describe('Extension: Groups', () => { group.members.push(endpoint); settings.set(['devices', device.ieeeAddr, 'disabled'], true); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Should publish state change for group when members state change', async () => { @@ -128,37 +128,37 @@ describe('Extension: Groups', () => { const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Should publish state of device with endpoint name', async () => { const group = groups.gledopto_group; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/gledopto_group/set', stringify({state: 'ON'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0}); expect(group.command).toHaveBeenCalledTimes(1); expect(group.command).toHaveBeenCalledWith('genOnOff', 'on', {}, {}); }); @@ -166,12 +166,12 @@ describe('Extension: Groups', () => { it('Should publish state of group when specific state of specific endpoint is changed', async () => { const group = groups.gledopto_group; - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/GLEDOPTO_2ID/set', stringify({state_cct: 'ON'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/GLEDOPTO_2ID', stringify({state_cct: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/gledopto_group', stringify({state: 'ON'}), {retain: false, qos: 0}); expect(group.command).toHaveBeenCalledTimes(0); }); @@ -182,15 +182,15 @@ describe('Extension: Groups', () => { group.members.push(endpoint); settings.set(['groups'], {1: {friendly_name: 'group_1', retain: false, filtered_attributes: ['brightness']}}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON', brightness: 100})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 100}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON', brightness: 100}), { retain: false, qos: 0, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Shouldnt publish group state change when a group is not optimistic', async () => { @@ -200,13 +200,13 @@ describe('Extension: Groups', () => { group.members.push(endpoint); settings.set(['groups'], {1: {friendly_name: 'group_1', optimistic: false, retain: false}}); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); const payload = {data: {onOff: 1}, cluster: 'genOnOff', device, endpoint, type: 'attributeReport', linkquality: 10}; await mockZHEvents.message(payload); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Should publish state change of another group with shared device when a group changes its state', async () => { @@ -215,13 +215,13 @@ describe('Extension: Groups', () => { groups.group_1.members.push(endpoint); groups.group_2.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'ON'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'ON'}), {retain: false, qos: 0}); }); it('Should not publish state change off if any lights within are still on when changed via device', async () => { @@ -235,12 +235,12 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); it('Should publish state change off if any lights within are still on when changed via device when off_state: last_member_state is used', async () => { @@ -257,13 +257,13 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(1, 'zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenNthCalledWith(2, 'zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); it('Should not publish state change off if any lights within with non default-ep are still on when changed via device', async () => { @@ -277,12 +277,12 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); it('Should not publish state change off if any lights within are still on when changed via device with non default-ep', async () => { @@ -299,12 +299,12 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/wall_switch_double/set', stringify({state_left: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(1); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF', state_right: 'ON'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(1); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF', state_right: 'ON'}), { retain: false, qos: 0, }); @@ -324,15 +324,15 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'})); await mockMQTTEvents.message('zigbee2mqtt/wall_switch_double/set', stringify({state_left: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/wall_switch_double', stringify({state_left: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); it('Should not publish state change off if any lights within are still on when changed via shared group', async () => { @@ -346,13 +346,13 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_2/set', stringify({state: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'OFF'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_2', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); it('Should publish state change off if all lights within turn off', async () => { @@ -369,15 +369,15 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'})); await mockMQTTEvents.message('zigbee2mqtt/bulb/set', stringify({state: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'OFF'}), {retain: true, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({state: 'OFF'}), {retain: true, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); it('Should only update group state with changed properties', async () => { @@ -391,26 +391,26 @@ describe('Extension: Groups', () => { settings.set(['groups'], { 1: {friendly_name: 'group_1', retain: false}, }); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF', color_temp: 200})); await mockMQTTEvents.message('zigbee2mqtt/bulb/set', stringify({state: 'ON', color_temp: 250})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({color_temp: 300})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb_color', stringify({color_mode: 'color_temp', color_temp: 300, state: 'OFF'}), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 300, state: 'ON'}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 300, state: 'ON'}), { retain: true, qos: 0, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/group_1', stringify({color_mode: 'color_temp', color_temp: 300, state: 'ON'}), {retain: false, qos: 0}, @@ -431,16 +431,16 @@ describe('Extension: Groups', () => { await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({state: 'ON'})); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); // @ts-expect-error private controller.state.state = {}; await mockMQTTEvents.message('zigbee2mqtt/bulb_color/set', stringify({state: 'OFF'})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(2); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(2); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({state: 'OFF'}), {retain: false, qos: 0}); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({state: 'OFF'}), {retain: false, qos: 0}); }); it('Add to group via MQTT', async () => { @@ -448,15 +448,15 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(1); const group = groups.group_1; expect(group.members.length).toBe(0); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/add', stringify({transaction: '123', group: 'group_1', device: 'bulb_color'}), ); await flushPromises(); expect(group.members).toStrictEqual([endpoint]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group_1'}, transaction: '123', status: 'ok'}), {retain: false, qos: 0}, @@ -472,12 +472,12 @@ describe('Extension: Groups', () => { throw new Error('timeout'); }); await flushPromises(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color'})); await flushPromises(); expect(group.members).toStrictEqual([]); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {}, status: 'error', error: 'Failed to add from group (timeout)'}), {retain: false, qos: 0}, @@ -490,12 +490,12 @@ describe('Extension: Groups', () => { const group = groups['group/with/slashes']; settings.set(['groups'], {99: {friendly_name: 'group/with/slashes', retain: false}}); expect(group.members.length).toBe(0); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group/with/slashes', device: 'bulb_color'})); await flushPromises(); expect(group.members).toStrictEqual([endpoint]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group/with/slashes'}, status: 'ok'}), {retain: false, qos: 0}, @@ -507,15 +507,15 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(3)!; const group = groups.group_1; expect(group.members.length).toBe(0); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'wall_switch_double', endpoint: 'right'}), ); await flushPromises(); expect(group.members).toStrictEqual([endpoint]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {device: 'wall_switch_double', endpoint: 'right', group: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -527,7 +527,7 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(3)!; const group = groups.group_1; expect(group.members.length).toBe(0); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'wall_switch_double', endpoint: 'right'}), @@ -539,8 +539,8 @@ describe('Extension: Groups', () => { ); await flushPromises(); expect(group.members).toStrictEqual([endpoint]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {device: 'wall_switch_double', endpoint: 'right', group: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -552,12 +552,12 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(1)!; const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color'})); await flushPromises(); expect(group.members).toStrictEqual([]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/remove', stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -569,15 +569,15 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(1)!; const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'bulb_color', skip_disable_reporting: true}), ); await flushPromises(); expect(group.members).toStrictEqual([]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/remove', stringify({data: {device: 'bulb_color', endpoint: 'default', group: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -589,15 +589,15 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(3)!; const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: '0x0017880104e45542', endpoint: '3'}), ); await flushPromises(); expect(group.members).toStrictEqual([]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/remove', stringify({data: {device: '0x0017880104e45542', endpoint: '3', group: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -609,15 +609,15 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(3)!; const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: 'wall_switch_double', endpoint: '3'}), ); await flushPromises(); expect(group.members).toStrictEqual([]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/remove', stringify({data: {device: 'wall_switch_double', endpoint: '3', group: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -629,15 +629,15 @@ describe('Extension: Groups', () => { const endpoint = device.getEndpoint(3)!; const group = groups.group_1; group.members.push(endpoint); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1', device: '0x0017880104e45542', endpoint: 'right'}), ); await flushPromises(); expect(group.members).toStrictEqual([]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/remove', stringify({data: {device: '0x0017880104e45542', endpoint: 'right', group: 'group_1'}, status: 'ok'}), {retain: false, qos: 0}, @@ -647,12 +647,12 @@ describe('Extension: Groups', () => { it('Remove from group all', async () => { const group = groups.group_1; groups.group_1.members.push(devices.QBKG03LM.endpoints[2]); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/remove_all', stringify({device: '0x0017880104e45542', endpoint: 'right'})); await flushPromises(); expect(group.members).toStrictEqual([]); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/remove_all', stringify({data: {device: '0x0017880104e45542', endpoint: 'right'}, status: 'ok'}), {retain: false, qos: 0}, @@ -661,11 +661,11 @@ describe('Extension: Groups', () => { it('Error when adding to non-existing group', async () => { mockLogger.error.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/remove', stringify({group: 'group_1_not_existing', device: 'bulb_color'})); await flushPromises(); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/remove', stringify({data: {}, status: 'error', error: "Group 'group_1_not_existing' does not exist"}), {retain: false, qos: 0}, @@ -674,11 +674,11 @@ describe('Extension: Groups', () => { it('Error when adding a non-existing device', async () => { mockLogger.error.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color_not_existing'})); await flushPromises(); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {}, status: 'error', error: "Device 'bulb_color_not_existing' does not exist"}), {retain: false, qos: 0}, @@ -687,14 +687,14 @@ describe('Extension: Groups', () => { it('Error when adding a non-existing endpoint', async () => { mockLogger.error.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message( 'zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color', endpoint: 'not_existing_endpoint'}), ); await flushPromises(); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {}, status: 'error', error: "Device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}), {retain: false, qos: 0}, @@ -703,11 +703,11 @@ describe('Extension: Groups', () => { it('Error when invalid payload', async () => { mockLogger.error.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', devicez: 'bulb_color'})); await flushPromises(); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -716,11 +716,11 @@ describe('Extension: Groups', () => { it('Error when add/remove with invalid payload', async () => { mockLogger.error.mockClear(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockMQTTEvents.message('zigbee2mqtt/bridge/request/group/members/add', stringify({groupz: 'group_1', device: 'bulb_color'})); await flushPromises(); - expect(mockMQTT.publishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object)); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bridge/response/group/members/add', stringify({data: {}, status: 'error', error: 'Invalid payload'}), {retain: false, qos: 0}, @@ -734,38 +734,38 @@ describe('Extension: Groups', () => { group.members.push(bulbColor.getEndpoint(1)!); group.members.push(bulbColorTemp.getEndpoint(1)!); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({color_temp: 50})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({color_mode: 'color_temp', color_temp: 50}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb_color', stringify({color_mode: 'color_temp', color_temp: 50}), { retain: false, qos: 0, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({color_mode: 'color_temp', color_temp: 50}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/group_1', stringify({color_mode: 'color_temp', color_temp: 50}), { retain: false, qos: 0, }); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 50}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 50}), { retain: true, qos: 0, }); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); await mockMQTTEvents.message('zigbee2mqtt/group_1/set', stringify({color: {x: 0.5, y: 0.3}})); await flushPromises(); - expect(mockMQTT.publishAsync).toHaveBeenCalledTimes(3); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledTimes(3); + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/bulb_color', stringify({color: {x: 0.5, y: 0.3}, color_mode: 'xy', color_temp: 548}), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'zigbee2mqtt/group_1', stringify({color: {x: 0.5, y: 0.3}, color_mode: 'xy', color_temp: 548}), {retain: false, qos: 0}, ); - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 548}), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({color_mode: 'color_temp', color_temp: 548}), { retain: true, qos: 0, }); diff --git a/test/extensions/homeassistant.test.ts b/test/extensions/homeassistant.test.ts index 1834f0d7..18d874f1 100644 --- a/test/extensions/homeassistant.test.ts +++ b/test/extensions/homeassistant.test.ts @@ -1,6 +1,6 @@ import * as data from '../mocks/data'; import {mockLogger} from '../mocks/logger'; -import {mockMQTT, events as mockMQTTEvents} from '../mocks/mqtt'; +import {events as mockMQTTEvents, mockMQTTPublishAsync, mockMQTTSubscribeAsync, mockMQTTUnsubscribeAsync} from '../mocks/mqtt'; import * as mockSleep from '../mocks/sleep'; import {flushPromises} from '../mocks/utils'; import {devices, groups, events as mockZHEvents} from '../mocks/zigbeeHerdsman'; @@ -13,7 +13,7 @@ import {Controller} from '../../lib/controller'; import HomeAssistant from '../../lib/extension/homeassistant'; import * as settings from '../../lib/util/settings'; -const mocksClear = [mockMQTT.publishAsync, mockLogger.debug, mockLogger.warning, mockLogger.error]; +const mocksClear = [mockMQTTPublishAsync, mockLogger.debug, mockLogger.warning, mockLogger.error]; describe('Extension: HomeAssistant', () => { let controller: Controller; @@ -30,7 +30,7 @@ describe('Extension: HomeAssistant', () => { extension = controller.extensions.find((e) => e.constructor.name === 'HomeAssistant'); if (runTimers) { - await jest.runOnlyPendingTimersAsync(); + await vi.runOnlyPendingTimersAsync(); } }; @@ -50,19 +50,19 @@ describe('Extension: HomeAssistant', () => { z2m_version = (await getZigbee2MQTTVersion()).version; version = `Zigbee2MQTT ${z2m_version}`; origin.sw = z2m_version; - jest.useFakeTimers(); + vi.useFakeTimers(); settings.set(['homeassistant'], {enabled: true}); data.writeDefaultConfiguration(); settings.reRead(); data.writeEmptyState(); - mockMQTT.publishAsync.mockClear(); + mockMQTTPublishAsync.mockClear(); mockSleep.mock(); - controller = new Controller(jest.fn(), jest.fn()); + controller = new Controller(vi.fn(), vi.fn()); await controller.start(); }); afterAll(async () => { - jest.useRealTimers(); + vi.useRealTimers(); mockSleep.restore(); }); @@ -152,7 +152,7 @@ describe('Extension: HomeAssistant', () => { origin: origin, }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/light/1221051039810110150109113116116_9/light/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/light/1221051039810110150109113116116_9/light/config', stringify(payload), { retain: true, qos: 1, }); @@ -178,7 +178,7 @@ describe('Extension: HomeAssistant', () => { value_template: '{{ value_json.state }}', }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith( + expect(mockMQTTPublishAsync).toHaveBeenCalledWith( 'homeassistant/switch/1221051039810110150109113116116_9/switch/config', stringify(payload), {retain: true, qos: 1}, @@ -204,7 +204,7 @@ describe('Extension: HomeAssistant', () => { enabled_by_default: true, }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/temperature/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/temperature/config', stringify(payload), { retain: true, qos: 1, }); @@ -229,7 +229,7 @@ describe('Extension: HomeAssistant', () => { availability: [{topic: 'zigbee2mqtt/bridge/state', value_template: '{{ value_json.state }}'}], }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/humidity/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/humidity/config', stringify(payload), { retain: true, qos: 1, }); @@ -254,7 +254,7 @@ describe('Extension: HomeAssistant', () => { availability: [{topic: 'zigbee2mqtt/bridge/state', value_template: '{{ value_json.state }}'}], }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/pressure/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/pressure/config', stringify(payload), { retain: true, qos: 1, }); @@ -280,7 +280,7 @@ describe('Extension: HomeAssistant', () => { availability: [{topic: 'zigbee2mqtt/bridge/state', value_template: '{{ value_json.state }}'}], }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/battery/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/battery/config', stringify(payload), { retain: true, qos: 1, }); @@ -307,7 +307,7 @@ describe('Extension: HomeAssistant', () => { availability: [{topic: 'zigbee2mqtt/bridge/state', value_template: '{{ value_json.state }}'}], }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/linkquality/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/sensor/0x0017880104e45522/linkquality/config', stringify(payload), { retain: true, qos: 1, }); @@ -332,7 +332,7 @@ describe('Extension: HomeAssistant', () => { value_template: '{{ value_json.state_left }}', }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/switch/0x0017880104e45542/switch_left/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/switch/0x0017880104e45542/switch_left/config', stringify(payload), { retain: true, qos: 1, }); @@ -357,7 +357,7 @@ describe('Extension: HomeAssistant', () => { value_template: '{{ value_json.state_right }}', }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/switch/0x0017880104e45542/switch_right/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/switch/0x0017880104e45542/switch_right/config', stringify(payload), { retain: true, qos: 1, }); @@ -387,7 +387,7 @@ describe('Extension: HomeAssistant', () => { origin: origin, }; - expect(mockMQTT.publishAsync).toHaveBeenCalledWith('homeassistant/light/0x000b57fffec6a5b2/light/config', stringify(payload), { + expect(mockMQTTPublishAsync).toHaveBeenCalledWith('homeassistant/light/0x000b57fffec6a5b2/light/config', stringify(payload), { retain: true, qos: 1, }); @@ -413,13 +413,13 @@ describe('Extension: HomeAssistant', () => { "{% set patterns = [\n{\"pattern\": '^(?P