From c8de7fd32ce52bd3089eaa3c65c24be07ba7fef1 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 12 Dec 2024 12:15:26 +0100 Subject: [PATCH 1/2] refactor: untar config updates in memory, remove execa from prod deps --- packages/core/src/index.ts | 2 +- packages/core/src/index_browser.ts | 2 +- packages/core/src/index_safe.ts | 2 +- packages/core/src/util/compression.ts | 6 +- packages/zwave-js/package.json | 2 +- .../zwave-js/src/lib/driver/UpdateConfig.ts | 93 ++++++------------- yarn.lock | 9 +- 7 files changed, 43 insertions(+), 73 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 04e735becd0a..17d751ea1325 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -17,7 +17,7 @@ export * from "./test/assertZWaveError.js"; export type * from "./traits/index.js"; export * from "./util/_Types.js"; export * from "./util/compareVersions.js"; -export { deflateSync } from "./util/compression.js"; +export { deflateSync, gunzipSync } from "./util/compression.js"; export * from "./util/config.js"; export * from "./util/crc.js"; export * from "./util/date.js"; diff --git a/packages/core/src/index_browser.ts b/packages/core/src/index_browser.ts index d29e8324cf29..e1c43b053449 100644 --- a/packages/core/src/index_browser.ts +++ b/packages/core/src/index_browser.ts @@ -16,7 +16,7 @@ export * from "./registries/Scales.js"; export * from "./registries/Sensors.js"; export type * from "./traits/index.js"; export type * from "./util/_Types.js"; -export { deflateSync } from "./util/compression.js"; +export { deflateSync, gunzipSync } from "./util/compression.js"; export * from "./util/config.js"; export * from "./util/crc.js"; export * from "./util/graph.js"; diff --git a/packages/core/src/index_safe.ts b/packages/core/src/index_safe.ts index 683b9a763d88..917dc42e1d4a 100644 --- a/packages/core/src/index_safe.ts +++ b/packages/core/src/index_safe.ts @@ -15,7 +15,7 @@ export * from "./registries/Scales.js"; export * from "./registries/Sensors.js"; export type * from "./traits/index.js"; export * from "./util/_Types.js"; -export { deflateSync } from "./util/compression.js"; +export { deflateSync, gunzipSync } from "./util/compression.js"; export * from "./util/config.js"; export * from "./util/crc.js"; export * from "./util/graph.js"; diff --git a/packages/core/src/util/compression.ts b/packages/core/src/util/compression.ts index ed1c365ff447..9e4699181bf3 100644 --- a/packages/core/src/util/compression.ts +++ b/packages/core/src/util/compression.ts @@ -1,5 +1,9 @@ -import { deflateSync as defflateSync } from "fflate"; +import { deflateSync as defflateSync, gunzipSync as fgunzipSync } from "fflate"; export function deflateSync(data: Uint8Array): Uint8Array { return defflateSync(data); } + +export function gunzipSync(data: Uint8Array): Uint8Array { + return fgunzipSync(data); +} diff --git a/packages/zwave-js/package.json b/packages/zwave-js/package.json index 8673a0216cd2..0e0acb5c0728 100644 --- a/packages/zwave-js/package.json +++ b/packages/zwave-js/package.json @@ -99,6 +99,7 @@ }, "dependencies": { "@alcalzone/jsonl-db": "^3.1.1", + "@andrewbranch/untar.js": "^1.0.3", "@homebridge/ciao": "^1.3.1", "@zwave-js/cc": "workspace:*", "@zwave-js/config": "workspace:*", @@ -110,7 +111,6 @@ "@zwave-js/testing": "workspace:*", "alcalzone-shared": "^5.0.0", "ansi-colors": "^4.1.3", - "execa": "^5.1.1", "ky": "^1.7.2", "mdns-server": "^1.0.11", "p-queue": "^8.0.1", diff --git a/packages/zwave-js/src/lib/driver/UpdateConfig.ts b/packages/zwave-js/src/lib/driver/UpdateConfig.ts index 3c1ce1fb8dd5..ed60be07e147 100644 --- a/packages/zwave-js/src/lib/driver/UpdateConfig.ts +++ b/packages/zwave-js/src/lib/driver/UpdateConfig.ts @@ -1,13 +1,9 @@ +import { untar } from "@andrewbranch/untar.js"; import { ZWaveError, ZWaveErrorCodes } from "@zwave-js/core"; -import { - copyFilesRecursive, - getErrorMessage, - noop, - writeTextFile, -} from "@zwave-js/shared"; +import { gunzipSync } from "@zwave-js/core"; +import { getErrorMessage, writeTextFile } from "@zwave-js/shared"; import { type CopyFile, - type FileHandle, type MakeTempDirectory, type ManageDirectory, type OpenFile, @@ -15,7 +11,6 @@ import { type WriteFile, } from "@zwave-js/shared/bindings"; import { isObject } from "alcalzone-shared/typeguards"; -import execa from "execa"; import path from "pathe"; import semverInc from "semver/functions/inc.js"; import semverValid from "semver/functions/valid.js"; @@ -105,34 +100,11 @@ export async function installConfigUpdate( ); } - // Download tarball to a temporary directory - let tmpDir: string; + let tarballData: Uint8Array; try { - tmpDir = await fs.makeTempDir("zjs-config-update-"); - } catch (e) { - throw new ZWaveError( - `Config update failed: Could not create temporary directory. Reason: ${ - getErrorMessage( - e, - ) - }`, - ZWaveErrorCodes.Config_Update_InstallFailed, + tarballData = new Uint8Array( + await ky.get(url).arrayBuffer(), ); - } - - // Download the package tarball into the temporary directory - const tarFilename = path.join(tmpDir, "zjs-config-update.tgz"); - let fileHandle: FileHandle | undefined; - try { - fileHandle = await fs.open(tarFilename, { - read: false, - write: true, - create: true, - truncate: true, - }); - - const response = await ky.get(url); - await response.body?.pipeTo(fileHandle.writable); } catch (e) { throw new ZWaveError( `Config update failed: Could not download tarball. Reason: ${ @@ -142,41 +114,31 @@ export async function installConfigUpdate( }`, ZWaveErrorCodes.Config_Update_InstallFailed, ); - } finally { - await fileHandle?.close(); - } - - // This should not be necessary in Docker. Leaving it here anyways in case - // we want to use this method on Windows at some point - function normalizeToUnixStyle(path: string): string { - path = path.replaceAll(":", ""); - path = path.replaceAll("\\", "/"); - if (!path.startsWith("/")) path = `/${path}`; - return path; } - const extractedDir = path.join(tmpDir, "extracted"); try { - // Extract the tarball in the temporary folder - await fs.deleteDir(extractedDir); - await fs.ensureDir(extractedDir); - await execa("tar", [ - "--strip-components=1", - "-xzf", - normalizeToUnixStyle(tarFilename), - "-C", - normalizeToUnixStyle(extractedDir), - ]); - - // then overwrite the files in the external config directory + // Extract json files from the tarball's config directory + // and overwrite the external config directory with them + const tarFiles = untar(gunzipSync(tarballData)); await fs.deleteDir(external.configDir); await fs.ensureDir(external.configDir); - await copyFilesRecursive( - fs, - path.join(extractedDir, "config"), - external.configDir, - (src) => src.endsWith(".json"), - ); + + const prefix = "package/config/"; + for (const file of tarFiles) { + if ( + !file.filename.startsWith(prefix) + || !file.filename.endsWith(".json") + ) { + continue; + } + const filename = file.filename.slice(prefix.length); + const targetFileName = path.join(external.configDir, filename); + const targetDirName = path.dirname(targetFileName); + + await fs.ensureDir(targetDirName); + await fs.writeFile(targetFileName, file.fileData); + } + const externalVersionFilename = path.join( external.configDir, "version", @@ -188,7 +150,4 @@ export async function installConfigUpdate( ZWaveErrorCodes.Config_Update_InstallFailed, ); } - - // Clean up the temp dir and ignore errors - await fs.deleteDir(tmpDir).catch(noop); } diff --git a/yarn.lock b/yarn.lock index d07bef48074a..cdc755a78439 100644 --- a/yarn.lock +++ b/yarn.lock @@ -234,6 +234,13 @@ __metadata: languageName: node linkType: hard +"@andrewbranch/untar.js@npm:^1.0.3": + version: 1.0.3 + resolution: "@andrewbranch/untar.js@npm:1.0.3" + checksum: 10/a32de53839fc61af90a394cf93d4368aacd167c9c80f0b3ba0c268460942a6ce2bfe257b6d3f03986b9dcb7368f10b9dc7f66c2f94254d2662da8278454e7d12 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0": version: 7.14.5 resolution: "@babel/code-frame@npm:7.14.5" @@ -9901,6 +9908,7 @@ __metadata: dependencies: "@alcalzone/esm2cjs": "npm:^1.4.1" "@alcalzone/jsonl-db": "npm:^3.1.1" + "@andrewbranch/untar.js": "npm:^1.0.3" "@homebridge/ciao": "npm:^1.3.1" "@microsoft/api-extractor": "npm:^7.48.0" "@types/node": "npm:^18.19.63" @@ -9922,7 +9930,6 @@ __metadata: alcalzone-shared: "npm:^5.0.0" ansi-colors: "npm:^4.1.3" del-cli: "npm:^6.0.0" - execa: "npm:^5.1.1" ky: "npm:^1.7.2" mdns-server: "npm:^1.0.11" mockdate: "npm:^3.0.5" From 6f13c3e64491545b58fa0d5afb6755e4a1d7e596 Mon Sep 17 00:00:00 2001 From: Dominic Griesel Date: Thu, 12 Dec 2024 13:09:34 +0100 Subject: [PATCH 2/2] chore: cleanup unused stuff --- packages/config/package.json | 2 - packages/serial/src/index.ts | 2 - packages/serial/src/index_mock.ts | 2 - .../serial/src/mock/SerialPortBindingMock.ts | 406 ------------------ packages/serial/src/mock/SerialPortMock.ts | 33 -- .../serial/src/mock/_MockSerialPort.ts.txt | 129 ------ packages/zwave-js/package.json | 4 - yarn.lock | 117 ----- 8 files changed, 695 deletions(-) delete mode 100644 packages/serial/src/mock/SerialPortBindingMock.ts delete mode 100644 packages/serial/src/mock/SerialPortMock.ts delete mode 100644 packages/serial/src/mock/_MockSerialPort.ts.txt diff --git a/packages/config/package.json b/packages/config/package.json index 40ac20e87a04..e2e6a5c0df94 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -74,7 +74,6 @@ "@types/js-levenshtein": "^1.1.3", "@types/json-logic-js": "^2.0.7", "@types/node": "^18.19.63", - "@types/proxyquire": "^1.3.31", "@types/semver": "^7.5.8", "@types/sinon": "^17.0.3", "@types/xml2js": "^0.4.14", @@ -86,7 +85,6 @@ "js-levenshtein": "^1.1.6", "ky": "^1.7.2", "peggy": "^3.0.2", - "proxyquire": "^2.1.3", "sinon": "^19.0.2", "ts-pegjs": "patch:ts-pegjs@npm%3A4.2.1#~/.yarn/patches/ts-pegjs-npm-4.2.1-0f567a1059.patch", "tsx": "^4.19.2", diff --git a/packages/serial/src/index.ts b/packages/serial/src/index.ts index 75f85dbbd231..6899eb054b83 100644 --- a/packages/serial/src/index.ts +++ b/packages/serial/src/index.ts @@ -13,8 +13,6 @@ export * from "./parsers/ZnifferSerialFrame.js"; export * from "./plumbing/Faucet.js"; export type * from "./serialport/Bindings.js"; export * from "./serialport/LegacyBindingWrapper.js"; -export * from "./serialport/NodeSerialPort.js"; -export * from "./serialport/NodeSocket.js"; export * from "./serialport/ZWaveSerialPortImplementation.js"; export * from "./serialport/ZWaveSerialStream.js"; export * from "./serialport/ZWaveSocketOptions.js"; diff --git a/packages/serial/src/index_mock.ts b/packages/serial/src/index_mock.ts index 59e501aeb6cc..60f70ead21eb 100644 --- a/packages/serial/src/index_mock.ts +++ b/packages/serial/src/index_mock.ts @@ -1,3 +1 @@ export * from "./mock/MockPort.js"; -export * from "./mock/SerialPortBindingMock.js"; -export * from "./mock/SerialPortMock.js"; diff --git a/packages/serial/src/mock/SerialPortBindingMock.ts b/packages/serial/src/mock/SerialPortBindingMock.ts deleted file mode 100644 index 12a2a742b216..000000000000 --- a/packages/serial/src/mock/SerialPortBindingMock.ts +++ /dev/null @@ -1,406 +0,0 @@ -/* eslint-disable no-restricted-globals -- The serialport typings require a Node.js Buffer */ -/* eslint-disable @typescript-eslint/require-await */ -// Clone of https://github.com/serialport/binding-mock with support for emitting events on the written side - -import type { - BindingInterface, - BindingPortInterface, - OpenOptions, - PortInfo, - PortStatus, - SetOptions, - UpdateOptions, -} from "@serialport/bindings-interface"; -import { Bytes, TypedEventTarget, isUint8Array } from "@zwave-js/shared"; - -export interface MockPortInternal { - data: Uint8Array; - // echo: boolean; - // record: boolean; - info: PortInfo; - maxReadSize: number; - readyData?: Uint8Array; - openOpt?: OpenOptions; - instance?: MockPortBinding; -} - -export interface CreatePortOptions { - echo?: boolean; - record?: boolean; - readyData?: Uint8Array; - maxReadSize?: number; - manufacturer?: string; - vendorId?: string; - productId?: string; -} - -let ports: { - [key: string]: MockPortInternal; -} = {}; -let serialNumber = 0; - -function resolveNextTick() { - return new Promise((resolve) => process.nextTick(() => resolve())); -} - -export class CanceledError extends Error { - canceled: true; - constructor(message: string) { - super(message); - this.canceled = true; - } -} - -export interface MockBindingInterface - extends BindingInterface -{ - reset(): void; - createPort(path: string, opt?: CreatePortOptions): void; - getInstance(path: string): MockPortBinding | undefined; -} - -export const MockBinding: MockBindingInterface = { - reset() { - ports = {}; - serialNumber = 0; - }, - - // Create a mock port - createPort(path: string, options: CreatePortOptions = {}) { - serialNumber++; - const optWithDefaults = { - echo: false, - record: false, - manufacturer: "The J5 Robotics Company", - vendorId: undefined, - productId: undefined, - maxReadSize: 1024, - ...options, - }; - - ports[path] = { - data: new Uint8Array(), - // echo: optWithDefaults.echo, - // record: optWithDefaults.record, - readyData: optWithDefaults.readyData, - maxReadSize: optWithDefaults.maxReadSize, - info: { - path, - manufacturer: optWithDefaults.manufacturer, - serialNumber: `${serialNumber}`, - pnpId: undefined, - locationId: undefined, - vendorId: optWithDefaults.vendorId, - productId: optWithDefaults.productId, - }, - }; - }, - - async list() { - return Object.values(ports).map((port) => port.info); - }, - - async open(options) { - if (!options || typeof options !== "object" || Array.isArray(options)) { - throw new TypeError("\"options\" is not an object"); - } - - if (!options.path) { - throw new TypeError("\"path\" is not a valid port"); - } - - if (!options.baudRate) { - throw new TypeError("\"baudRate\" is not a valid baudRate"); - } - - const openOptions: Required = { - dataBits: 8, - lock: true, - stopBits: 1, - parity: "none", - rtscts: false, - xon: false, - xoff: false, - xany: false, - hupcl: true, - ...options, - }; - const { path } = openOptions; - - const port = ports[path]; - await resolveNextTick(); - if (!port) { - throw new Error( - `Port does not exist - please call MockBinding.createPort('${path}') first`, - ); - } - - if (port.openOpt?.lock) { - throw new Error("Port is locked cannot open"); - } - - port.openOpt = { ...openOptions }; - - port.instance = new MockPortBinding(port, openOptions); - return port.instance; - }, - - getInstance(path: string): MockPortBinding | undefined { - return ports[path]?.instance; - }, -}; - -interface MockPortBindingEvents { - write: (data: Uint8Array) => void; - close: () => void; -} - -/** - * Mock bindings for pretend serialport access - */ -export class MockPortBinding extends TypedEventTarget - implements BindingPortInterface -{ - readonly openOptions: Required; - readonly port: MockPortInternal; - private pendingRead: null | ((err: null | Error) => void); - lastWrite: null | Uint8Array; - recording: Uint8Array; - writeOperation: null | Promise; - isOpen: boolean; - serialNumber?: string; - - constructor(port: MockPortInternal, openOptions: Required) { - super(); - - this.port = port; - this.openOptions = openOptions; - this.pendingRead = null; - this.isOpen = true; - this.lastWrite = null; - this.recording = new Uint8Array(); - this.writeOperation = null; // in flight promise or null - this.serialNumber = port.info.serialNumber; - - if (port.readyData) { - const data = port.readyData; - process.nextTick(() => { - if (this.isOpen) { - this.emitData(data); - } - }); - } - } - - // Emit data on a mock port - emitData(data: Uint8Array | string): void { - if (!this.isOpen || !this.port) { - throw new Error("Port must be open to pretend to receive data"); - } - const bufferData = isUint8Array(data) ? data : Bytes.from(data); - this.port.data = Bytes.concat([this.port.data, bufferData]); - if (this.pendingRead) { - process.nextTick(this.pendingRead); - this.pendingRead = null; - } - } - - async close(): Promise { - if (!this.isOpen) { - throw new Error("Port is not open"); - } - - const port = this.port; - if (!port) { - throw new Error("already closed"); - } - - port.openOpt = undefined; - // reset data on close - port.data = new Uint8Array(); - this.serialNumber = undefined; - this.isOpen = false; - if (this.pendingRead) { - this.pendingRead(new CanceledError("port is closed")); - } - - this.emit("close"); - } - - async read( - buffer: Buffer, - offset: number, - length: number, - ): Promise<{ - buffer: Buffer; - bytesRead: number; - }> { - if (!Buffer.isBuffer(buffer)) { - throw new TypeError("\"buffer\" is not a Buffer"); - } - - if (typeof offset !== "number" || Number.isNaN(offset)) { - throw new TypeError( - `"offset" is not an integer got "${ - Number.isNaN(offset) ? "NaN" : typeof offset - }"`, - ); - } - - if (typeof length !== "number" || Number.isNaN(length)) { - throw new TypeError( - `"length" is not an integer got "${ - Number.isNaN(length) ? "NaN" : typeof length - }"`, - ); - } - - if (buffer.length < offset + length) { - throw new Error("buffer is too small"); - } - - if (!this.isOpen) { - throw new Error("Port is not open"); - } - - await resolveNextTick(); - if (!this.isOpen || !this.port) { - throw new CanceledError("Read canceled"); - } - if (this.port.data.length <= 0) { - return new Promise((resolve, reject) => { - this.pendingRead = async (err) => { - if (err) { - return reject(err); - } - try { - const readResult = await this.read( - buffer, - offset, - length, - ); - resolve(readResult); - } catch (e) { - reject(e as Error); - } - }; - }); - } - - const lengthToRead = this.port.maxReadSize > length - ? length - : this.port.maxReadSize; - - const data = this.port.data.subarray(0, lengthToRead); - buffer.set(data, offset); - this.port.data = this.port.data.subarray(lengthToRead); - return { bytesRead: data.length, buffer }; - } - - async write(buffer: Uint8Array): Promise { - if (!isUint8Array(buffer)) { - throw new TypeError("\"buffer\" is not a Uint8Array"); - } - - if (!this.isOpen || !this.port) { - throw new Error("Port is not open"); - } - - if (this.writeOperation) { - throw new Error( - "Overlapping writes are not supported and should be queued by the serialport object", - ); - } - this.writeOperation = (async () => { - await resolveNextTick(); - if (!this.isOpen || !this.port) { - return; - // throw new Error("Write canceled"); - } - const data = (this.lastWrite = Bytes.from(buffer)); // copy - this.emit("write", data); - - // if (this.port.record) { - // this.recording = Buffer.concat([this.recording, data]); - // } - // if (this.port.echo) { - // process.nextTick(() => { - // if (this.isOpen) { - // this.emitData(data); - // } - // }); - // } - this.writeOperation = null; - })(); - return this.writeOperation; - } - - async update(options: UpdateOptions): Promise { - if (typeof options !== "object") { - throw TypeError("\"options\" is not an object"); - } - - if (typeof options.baudRate !== "number") { - throw new TypeError("\"options.baudRate\" is not a number"); - } - - if (!this.isOpen || !this.port) { - throw new Error("Port is not open"); - } - await resolveNextTick(); - if (this.port.openOpt) { - this.port.openOpt.baudRate = options.baudRate; - } - } - - async set(options: SetOptions): Promise { - if (typeof options !== "object") { - throw new TypeError("\"options\" is not an object"); - } - if (!this.isOpen) { - throw new Error("Port is not open"); - } - await resolveNextTick(); - } - - async get(): Promise { - if (!this.isOpen) { - throw new Error("Port is not open"); - } - await resolveNextTick(); - return { - cts: true, - dsr: false, - dcd: false, - }; - } - - async getBaudRate(): Promise<{ baudRate: number }> { - if (!this.isOpen || !this.port) { - throw new Error("Port is not open"); - } - await resolveNextTick(); - if (!this.port.openOpt?.baudRate) { - throw new Error("Internal Error"); - } - return { - baudRate: this.port.openOpt.baudRate, - }; - } - - async flush(): Promise { - if (!this.isOpen || !this.port) { - throw new Error("Port is not open"); - } - await resolveNextTick(); - this.port.data = new Uint8Array(); - } - - async drain(): Promise { - if (!this.isOpen) { - throw new Error("Port is not open"); - } - await this.writeOperation; - await resolveNextTick(); - } -} diff --git a/packages/serial/src/mock/SerialPortMock.ts b/packages/serial/src/mock/SerialPortMock.ts deleted file mode 100644 index b2144b78f3ad..000000000000 --- a/packages/serial/src/mock/SerialPortMock.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Clone of https://github.com/serialport/node-serialport/blob/4e8a3c4a9f46a09d39374eb67a59ff10eb09a5cd/packages/serialport/lib/serialport-mock.ts -// with support for emitting events on the written side - -import { - type ErrorCallback, - type OpenOptions, - SerialPortStream, -} from "@serialport/stream"; -import { - MockBinding, - type MockBindingInterface, -} from "./SerialPortBindingMock.js"; - -export type SerialPortMockOpenOptions = Omit< - OpenOptions, - "binding" ->; - -export class SerialPortMock extends SerialPortStream { - // es tic list = MockBinding.list; - static readonly binding = MockBinding; - - constructor( - options: SerialPortMockOpenOptions, - openCallback?: ErrorCallback, - ) { - const opts: OpenOptions = { - binding: MockBinding, - ...options, - }; - super(opts, openCallback); - } -} diff --git a/packages/serial/src/mock/_MockSerialPort.ts.txt b/packages/serial/src/mock/_MockSerialPort.ts.txt deleted file mode 100644 index 370272b8dd92..000000000000 --- a/packages/serial/src/mock/_MockSerialPort.ts.txt +++ /dev/null @@ -1,129 +0,0 @@ -// TODO: Get rid of this entire thing and use the serialport-based mocking instead. - -import { ZWaveLogContainer } from "@zwave-js/core"; -import { Mixin } from "@zwave-js/shared"; -import { EventEmitter } from "node:events"; -import { PassThrough } from "node:stream"; -import sinon from "sinon"; -import { ZWaveSerialPort } from "../serialport/ZWaveSerialPort.js"; -import type { ZWaveSerialPortEventCallbacks } from "../serialport/ZWaveSerialPortBase.js"; -import { - MockBinding as SerialPortMockBinding, - type MockPortBinding as SerialPortMockPortBinding, -} from "./SerialPortBindingMock.js"; -import { SerialPortMock } from "./SerialPortMock.js"; - -const instances = new Map(); - -@Mixin([EventEmitter]) -class MockBinding extends PassThrough {} - -interface MockSerialPortEventCallbacks extends ZWaveSerialPortEventCallbacks { - write: (data: Uint8Array) => void; -} - -type MockSerialPortEvents = Extract; - -export interface MockSerialPort { - on( - event: TEvent, - callback: MockSerialPortEventCallbacks[TEvent], - ): this; - addListener( - event: TEvent, - callback: MockSerialPortEventCallbacks[TEvent], - ): this; - once( - event: TEvent, - callback: MockSerialPortEventCallbacks[TEvent], - ): this; - off( - event: TEvent, - callback: MockSerialPortEventCallbacks[TEvent], - ): this; - removeListener( - event: TEvent, - callback: MockSerialPortEventCallbacks[TEvent], - ): this; - removeAllListeners(event?: MockSerialPortEvents): this; - - emit( - event: TEvent, - ...args: Parameters - ): boolean; -} - -export class MockSerialPort extends ZWaveSerialPort { - constructor(port: string, loggers: ZWaveLogContainer) { - super(port, loggers, MockBinding as any); - instances.set(port, this); - } - - public static getInstance(port: string): MockSerialPort | undefined { - return instances.get(port); - } - - private __isOpen: boolean = false; - public get isOpen(): boolean { - return this.__isOpen; - } - - public open(): Promise { - return this.openStub().then(() => { - this.__isOpen = true; - }); - } - public readonly openStub = sinon.stub().resolves(); - - public close(): Promise { - return this.closeStub().then(() => { - this.__isOpen = false; - }); - } - public readonly closeStub = sinon.stub().resolves(); - - public receiveData(data: Uint8Array): void { - this.serial.emit("data", data); - } - - public raiseError(err: Error): void { - this.emit("error", err); - } - - public writeAsync(data: Uint8Array): Promise { - this._lastWrite = data; - this.emit("write", data); - return this.writeStub(data); - } - public readonly writeStub = sinon.stub(); - - private _lastWrite: string | number[] | Uint8Array | undefined; - public get lastWrite(): string | number[] | Uint8Array | undefined { - return this._lastWrite; - } -} - -export async function createAndOpenMockedZWaveSerialPort( - path: string, -): Promise<{ - port: ZWaveSerialPort; - binding: SerialPortMockPortBinding; -}> { - SerialPortMockBinding.reset(); - SerialPortMockBinding.createPort(path, { - record: true, - readyData: new Uint8Array(), - }); - - const port = new ZWaveSerialPort( - path, - new ZWaveLogContainer({ - enabled: false, - }), - // @ts-expect-error We're using an internal signature here - SerialPortMock, - ); - await port.open(); - const binding = (port["serial"] as SerialPortMock).port!; - return { port, binding }; -} diff --git a/packages/zwave-js/package.json b/packages/zwave-js/package.json index 0e0acb5c0728..c88cf031a5d2 100644 --- a/packages/zwave-js/package.json +++ b/packages/zwave-js/package.json @@ -115,7 +115,6 @@ "mdns-server": "^1.0.11", "p-queue": "^8.0.1", "pathe": "^1.1.2", - "proper-lockfile": "^4.1.2", "reflect-metadata": "^0.2.2", "semver": "^7.6.3", "serialport": "^12.0.0", @@ -126,8 +125,6 @@ "@alcalzone/esm2cjs": "^1.4.1", "@microsoft/api-extractor": "^7.48.0", "@types/node": "^18.19.63", - "@types/proper-lockfile": "^4.1.4", - "@types/proxyquire": "^1.3.31", "@types/semver": "^7.5.8", "@types/sinon": "^17.0.3", "@types/source-map-support": "^0.5.10", @@ -135,7 +132,6 @@ "@zwave-js/transformers": "workspace:*", "del-cli": "^6.0.0", "mockdate": "^3.0.5", - "proxyquire": "^2.1.3", "sinon": "^19.0.2", "tsx": "^4.19.2", "typescript": "5.6.2", diff --git a/yarn.lock b/yarn.lock index cdc755a78439..5fbc89007edf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2166,29 +2166,6 @@ __metadata: languageName: node linkType: hard -"@types/proper-lockfile@npm:^4.1.4": - version: 4.1.4 - resolution: "@types/proper-lockfile@npm:4.1.4" - dependencies: - "@types/retry": "npm:*" - checksum: 10/b0d1b8e84a563b2c5f869f7ff7542b1d83dec03d1c9d980847cbb189865f44b4a854673cdde59767e41bcb8c31932e613ac43822d358a6f8eede6b79ccfceb1d - languageName: node - linkType: hard - -"@types/proxyquire@npm:^1.3.31": - version: 1.3.31 - resolution: "@types/proxyquire@npm:1.3.31" - checksum: 10/945024495fc991f6152686795ac6f2f2d0f571834e67fa41c1e84877eeb1a321a24ab8ff67fc5152d9227f5b39f6254213dced15deaf80ed7443059c2257e072 - languageName: node - linkType: hard - -"@types/retry@npm:*": - version: 0.12.0 - resolution: "@types/retry@npm:0.12.0" - checksum: 10/bbd0b88f4b3eba7b7acfc55ed09c65ef6f2e1bcb4ec9b4dca82c66566934351534317d294a770a7cc6c0468d5573c5350abab6e37c65f8ef254443e1b028e44d - languageName: node - linkType: hard - "@types/semver@npm:^7.5.8": version: 7.5.8 resolution: "@types/semver@npm:7.5.8" @@ -2549,7 +2526,6 @@ __metadata: "@types/js-levenshtein": "npm:^1.1.3" "@types/json-logic-js": "npm:^2.0.7" "@types/node": "npm:^18.19.63" - "@types/proxyquire": "npm:^1.3.31" "@types/semver": "npm:^7.5.8" "@types/sinon": "npm:^17.0.3" "@types/xml2js": "npm:^0.4.14" @@ -2568,7 +2544,6 @@ __metadata: ky: "npm:^1.7.2" pathe: "npm:^1.1.2" peggy: "npm:^3.0.2" - proxyquire: "npm:^2.1.3" semver: "npm:^7.6.3" sinon: "npm:^19.0.2" ts-pegjs: "patch:ts-pegjs@npm%3A4.2.1#~/.yarn/patches/ts-pegjs-npm-4.2.1-0f567a1059.patch" @@ -5236,16 +5211,6 @@ __metadata: languageName: node linkType: hard -"fill-keys@npm:^1.0.2": - version: 1.0.2 - resolution: "fill-keys@npm:1.0.2" - dependencies: - is-object: "npm:~1.0.1" - merge-descriptors: "npm:~1.0.0" - checksum: 10/6e78e314dc6852060086a8edc754d6a5b8efea5389ba8ec48367c1d0fa14272f9fad2f5c1ced1dcbd73306ffbfa3aa8cc64add884150ccf729f2a45ebc22d2eb - languageName: node - linkType: hard - "fill-range@npm:^7.1.1": version: 7.1.1 resolution: "fill-range@npm:7.1.1" @@ -6096,15 +6061,6 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.9.0": - version: 2.10.0 - resolution: "is-core-module@npm:2.10.0" - dependencies: - has: "npm:^1.0.3" - checksum: 10/2bbaf37f60c3ac6a45ea020cda8df202d98145923a8d501b00810edd206c567328d09ffc279d84862a88a3bf9631116280cdc5d60dd59059554b6cc432310a88 - languageName: node - linkType: hard - "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -6197,13 +6153,6 @@ __metadata: languageName: node linkType: hard -"is-object@npm:~1.0.1": - version: 1.0.2 - resolution: "is-object@npm:1.0.2" - checksum: 10/db53971751c50277f0ed31d065d93038d23cb9785090ab5c8070a903cf5bab16cdb18f05b8855599ad87ec19eb4c85afa05980bcda77dd4a8482120b6348c73c - languageName: node - linkType: hard - "is-path-cwd@npm:^3.0.0": version: 3.0.0 resolution: "is-path-cwd@npm:3.0.0" @@ -6837,13 +6786,6 @@ __metadata: languageName: node linkType: hard -"merge-descriptors@npm:~1.0.0": - version: 1.0.1 - resolution: "merge-descriptors@npm:1.0.1" - checksum: 10/5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26 - languageName: node - linkType: hard - "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -7082,13 +7024,6 @@ __metadata: languageName: node linkType: hard -"module-not-found-error@npm:^1.0.1": - version: 1.0.1 - resolution: "module-not-found-error@npm:1.0.1" - checksum: 10/ebd65339d4d5980dd55cd32dbf112ec02b8e33f30866312b94caeee4783322259f18cf2270e9d2e600df3bd1876c35612b87f5c2525c21885fb1f83e85a9b9b0 - languageName: node - linkType: hard - "moment@npm:^2.29.1": version: 2.29.4 resolution: "moment@npm:2.29.4" @@ -7794,17 +7729,6 @@ __metadata: languageName: node linkType: hard -"proper-lockfile@npm:^4.1.2": - version: 4.1.2 - resolution: "proper-lockfile@npm:4.1.2" - dependencies: - graceful-fs: "npm:^4.2.4" - retry: "npm:^0.12.0" - signal-exit: "npm:^3.0.2" - checksum: 10/000a4875f543f591872b36ca94531af8a6463ddb0174f41c0b004d19e231d7445268b422ff1ea595e43d238655c702250cd3d27f408e7b9d97b56f1533ba26bf - languageName: node - linkType: hard - "proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" @@ -7812,17 +7736,6 @@ __metadata: languageName: node linkType: hard -"proxyquire@npm:^2.1.3": - version: 2.1.3 - resolution: "proxyquire@npm:2.1.3" - dependencies: - fill-keys: "npm:^1.0.2" - module-not-found-error: "npm:^1.0.1" - resolve: "npm:^1.11.1" - checksum: 10/c124bdb6f732d680c59c2d4cf0ff66ebae7b3c76af0c1bb05d5c9b982ba05db22c1df6ad4cb486d1d8a5f6b1fe0534cbea864917e82dacaf947cfa84c4c30463 - languageName: node - linkType: hard - "punycode@npm:^2.1.0": version: 2.1.1 resolution: "punycode@npm:2.1.1" @@ -8021,19 +7934,6 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.11.1": - version: 1.22.1 - resolution: "resolve@npm:1.22.1" - dependencies: - is-core-module: "npm:^2.9.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10/4adcfac33f0baf6fc46d6c3a11acfad5c9345eab8bb7280d65672dc40a9694ddab6d18be2feebccf6cfc581bedd7ebfa792f6bc86db1903a41d328c23161bd23 - languageName: node - linkType: hard - "resolve@npm:^1.22.2": version: 1.22.2 resolution: "resolve@npm:1.22.2" @@ -8083,19 +7983,6 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.11.1#optional!builtin": - version: 1.22.1 - resolution: "resolve@patch:resolve@npm%3A1.22.1#optional!builtin::version=1.22.1&hash=c3c19d" - dependencies: - is-core-module: "npm:^2.9.0" - path-parse: "npm:^1.0.7" - supports-preserve-symlinks-flag: "npm:^1.0.0" - bin: - resolve: bin/resolve - checksum: 10/551dd500765cce767c583747f5f21ceb51d437f539b01aee96d6ec39eb2c68a8ff5d646b083d690fe428a81329856bc1bbdb094379b8df4b3f10e7e1f6aa3839 - languageName: node - linkType: hard - "resolve@patch:resolve@npm%3A^1.22.2#optional!builtin": version: 1.22.2 resolution: "resolve@patch:resolve@npm%3A1.22.2#optional!builtin::version=1.22.2&hash=c3c19d" @@ -9912,8 +9799,6 @@ __metadata: "@homebridge/ciao": "npm:^1.3.1" "@microsoft/api-extractor": "npm:^7.48.0" "@types/node": "npm:^18.19.63" - "@types/proper-lockfile": "npm:^4.1.4" - "@types/proxyquire": "npm:^1.3.31" "@types/semver": "npm:^7.5.8" "@types/sinon": "npm:^17.0.3" "@types/source-map-support": "npm:^0.5.10" @@ -9935,8 +9820,6 @@ __metadata: mockdate: "npm:^3.0.5" p-queue: "npm:^8.0.1" pathe: "npm:^1.1.2" - proper-lockfile: "npm:^4.1.2" - proxyquire: "npm:^2.1.3" reflect-metadata: "npm:^0.2.2" semver: "npm:^7.6.3" serialport: "npm:^12.0.0"