From 9ab1f160ce809af9609c8169859df987eb7f043f Mon Sep 17 00:00:00 2001 From: philon- Date: Mon, 26 May 2025 21:48:50 +0200 Subject: [PATCH] Zod WIP --- package.json | 2 +- src/validation/channel.ts | 73 +++++---------- src/validation/config/bluetooth.ts | 24 ++--- src/validation/config/device.ts | 64 +++++-------- src/validation/config/display.ts | 69 +++++++------- src/validation/config/lora.ts | 92 ++++++------------- src/validation/config/network.ts | 14 +-- src/validation/config/position.ts | 62 ++++--------- src/validation/config/power.ts | 49 +++------- src/validation/config/security.ts | 50 +++------- .../moduleConfig/ambientLighting.ts | 37 +++----- src/validation/moduleConfig/audio.ts | 38 +++----- src/validation/moduleConfig/cannedMessage.ts | 65 +++++-------- .../moduleConfig/detectionSensor.ts | 49 ++++------ .../moduleConfig/externalNotification.ts | 77 +++++----------- src/validation/moduleConfig/mqtt.ts | 81 +++++----------- src/validation/moduleConfig/neighborInfo.ts | 19 ++-- src/validation/moduleConfig/paxcounter.ts | 25 ++--- src/validation/moduleConfig/rangeTest.ts | 21 ++--- src/validation/moduleConfig/serial.ts | 49 ++++------ src/validation/moduleConfig/storeForward.ts | 37 +++----- src/validation/moduleConfig/telemetry.ts | 55 ++++------- src/validation/rasterSource.ts | 30 +++--- src/validation/validate.ts | 4 +- 24 files changed, 372 insertions(+), 714 deletions(-) diff --git a/package.json b/package.json index ccaa07a0..ffb2da2f 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "react-qrcode-logo": "^3.0.0", "rfc4648": "^1.5.4", "vite-plugin-node-polyfills": "^0.23.0", - "zod": "^3.24.3", + "zod": "^3.25.0", "zustand": "5.0.4" }, "devDependencies": { diff --git a/src/validation/channel.ts b/src/validation/channel.ts index 4f4d43bb..e73e6db4 100644 --- a/src/validation/channel.ts +++ b/src/validation/channel.ts @@ -1,51 +1,26 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { - IsBoolean, - IsEnum, - IsInt, - IsNumber, - IsString, - Length, -} from "class-validator"; -export class ChannelValidation - implements Omit { - @IsNumber() - index: number; - - settings: Channel_SettingsValidation; - - @IsEnum(Protobuf.Channel.Channel_Role) - role: Protobuf.Channel.Channel_Role; -} - -export class Channel_SettingsValidation - implements Omit { - @IsNumber() - channelNum: number; - - @IsString() - psk: string; - - @Length(0, 11) - name: string; - - @IsInt() - id: number; - - @IsBoolean() - uplinkEnabled: boolean; - - @IsBoolean() - downlinkEnabled: boolean; - - @IsBoolean() - positionEnabled: boolean; - - @IsBoolean() - preciseLocation: boolean; - - @IsInt() - positionPrecision: number; -} +const RoleEnum = z.enum( + Protobuf.Channel.Channel_Role, +); + +export const Channel_SettingsValidationSchema = z.object({ + channelNum: z.number(), + psk: z.string(), + name: z.string().min(0).max(11), + id: z.int(), + uplinkEnabled: z.boolean(), + downlinkEnabled: z.boolean(), + positionEnabled: z.boolean(), + preciseLocation: z.boolean(), + positionPrecision: z.boolean(), +}); + +export const ChannelValidationSchema = z.object({ + index: z.number(), + settings: Channel_SettingsValidationSchema, + role: RoleEnum, +}); + +export type ChannelValidation = z.infer; diff --git a/src/validation/config/bluetooth.ts b/src/validation/config/bluetooth.ts index 67492240..13881a0e 100644 --- a/src/validation/config/bluetooth.ts +++ b/src/validation/config/bluetooth.ts @@ -1,18 +1,14 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsEnum, IsInt } from "class-validator"; -export class BluetoothValidation implements - Omit< - Protobuf.Config.Config_BluetoothConfig, - keyof Message | "deviceLoggingEnabled" - > { - @IsBoolean() - enabled: boolean; +const PairingModeEnum = z.enum( + Protobuf.Config.Config_BluetoothConfig_PairingMode, +); - @IsEnum(Protobuf.Config.Config_BluetoothConfig_PairingMode) - mode: Protobuf.Config.Config_BluetoothConfig_PairingMode; +export const BluetoothValidationSchema = z.object({ + enabled: z.boolean(), + mode: PairingModeEnum, + fixedPin: z.int(), +}); - @IsInt() - fixedPin: number; -} +export type BluetoothValidation = z.infer; diff --git a/src/validation/config/device.ts b/src/validation/config/device.ts index 0858e6a7..66610370 100644 --- a/src/validation/config/device.ts +++ b/src/validation/config/device.ts @@ -1,42 +1,26 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsEnum, IsInt, IsString } from "class-validator"; -export class DeviceValidation - implements Omit { - @IsEnum(Protobuf.Config.Config_DeviceConfig_Role) - role: Protobuf.Config.Config_DeviceConfig_Role; - - @IsBoolean() - serialEnabled: boolean; - - @IsBoolean() - debugLogEnabled: boolean; - - @IsInt() - buttonGpio: number; - - @IsInt() - buzzerGpio: number; - - @IsEnum(Protobuf.Config.Config_DeviceConfig_RebroadcastMode) - rebroadcastMode: Protobuf.Config.Config_DeviceConfig_RebroadcastMode; - - @IsInt() - nodeInfoBroadcastSecs: number; - - @IsBoolean() - doubleTapAsButtonPress: boolean; - - @IsBoolean() - isManaged: boolean; - - @IsBoolean() - disableTripleClick: boolean; - - @IsBoolean() - ledHeartbeatDisabled: boolean; - - @IsString() - tzdef: string; -} +const RoleEnum = z.enum( + Protobuf.Config.Config_DeviceConfig_Role, +); +const RebroadcastModeEnum = z.enum( + Protobuf.Config.Config_DeviceConfig_RebroadcastMode, +); + +export const DeviceValidationSchema = z.object({ + role: RoleEnum, + serialEnabled: z.boolean(), + debugLogEnabled: z.boolean(), + buttonGpio: z.int(), + buzzerGpio: z.int(), + rebroadcastMode: RebroadcastModeEnum, + nodeInfoBroadcastSecs: z.int(), + doubleTapAsButtonPress: z.boolean(), + isManaged: z.boolean(), + disableTripleClick: z.boolean(), + ledHeartbeatDisabled: z.boolean(), + tzdef: z.string(), +}); + +export type DeviceValidation = z.infer; diff --git a/src/validation/config/display.ts b/src/validation/config/display.ts index 6f38164b..927f30de 100644 --- a/src/validation/config/display.ts +++ b/src/validation/config/display.ts @@ -1,39 +1,34 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsEnum, IsInt } from "class-validator"; -export class DisplayValidation - implements Omit { - @IsInt() - screenOnSecs: number; - - @IsEnum(Protobuf.Config.Config_DisplayConfig_GpsCoordinateFormat) - gpsFormat: Protobuf.Config.Config_DisplayConfig_GpsCoordinateFormat; - - @IsInt() - autoScreenCarouselSecs: number; - - @IsBoolean() - compassNorthTop: boolean; - - @IsBoolean() - flipScreen: boolean; - - @IsEnum(Protobuf.Config.Config_DisplayConfig_DisplayUnits) - units: Protobuf.Config.Config_DisplayConfig_DisplayUnits; - - @IsEnum(Protobuf.Config.Config_DisplayConfig_OledType) - oled: Protobuf.Config.Config_DisplayConfig_OledType; - - @IsEnum(Protobuf.Config.Config_DisplayConfig_DisplayMode) - displaymode: Protobuf.Config.Config_DisplayConfig_DisplayMode; - - @IsBoolean() - headingBold: boolean; - - @IsBoolean() - wakeOnTapOrMotion: boolean; - - @IsEnum(Protobuf.Config.Config_DisplayConfig_CompassOrientation) - compassOrientation: Protobuf.Config.Config_DisplayConfig_CompassOrientation; -} +const GpsCoordinateEnum = z.enum( + Protobuf.Config.Config_DisplayConfig_GpsCoordinateFormat, +); +const DisplayUnitsEnum = z.enum( + Protobuf.Config.Config_DisplayConfig_DisplayUnits, +); +const OledTypeEnum = z.enum( + Protobuf.Config.Config_DisplayConfig_OledType, +); +const DisplayModeEnum = z.enum( + Protobuf.Config.Config_DisplayConfig_DisplayMode, +); +const CompassOrientationEnum = z.enum( + Protobuf.Config.Config_DisplayConfig_CompassOrientation, +); + +export const DisplayValidationSchema = z.object({ + screenOnSecs: z.int(), + gpsFormat: GpsCoordinateEnum, + autoScreenCarouselSecs: z.int(), + compassNorthTop: z.boolean(), + flipScreen: z.boolean(), + units: DisplayUnitsEnum, + oled: OledTypeEnum, + displaymode: DisplayModeEnum, + headingBold: z.boolean(), + wakeOnTapOrMotion: z.boolean(), + compassOrientation: CompassOrientationEnum, +}); + +export type DisplayValidation = z.infer; diff --git a/src/validation/config/lora.ts b/src/validation/config/lora.ts index ad70b586..d8844aca 100644 --- a/src/validation/config/lora.ts +++ b/src/validation/config/lora.ts @@ -1,65 +1,31 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsArray, IsBoolean, IsEnum, IsInt, Max, Min } from "class-validator"; -export class LoRaValidation - implements - Omit { - @IsBoolean() - usePreset: boolean; - - @IsEnum(Protobuf.Config.Config_LoRaConfig_ModemPreset) - modemPreset: Protobuf.Config.Config_LoRaConfig_ModemPreset; - - @IsInt() - bandwidth: number; - - @IsInt() - // @Min(7) - @Max(12) - spreadFactor: number; - - @IsInt() - @Min(0) - @Max(10) - codingRate: number; - - @IsInt() - frequencyOffset: number; - - @IsEnum(Protobuf.Config.Config_LoRaConfig_RegionCode) - region: Protobuf.Config.Config_LoRaConfig_RegionCode; - - @IsInt() - @Min(1) - @Max(7) - hopLimit: number; - - @IsBoolean() - txEnabled: boolean; - - @IsInt() - @Min(0) - txPower: number; - - @IsInt() - channelNum: number; - - @IsBoolean() - overrideDutyCycle: boolean; - - @IsBoolean() - sx126xRxBoostedGain: boolean; - - @IsInt() - overrideFrequency: number; - - @IsArray() - ignoreIncoming: number[]; - - @IsBoolean() - ignoreMqtt: boolean; - - @IsBoolean() - configOkToMqtt: boolean; -} +const ModemPresetEnum = z.enum( + Protobuf.Config.Config_LoRaConfig_ModemPreset, +); +const RegionCodeEnum = z.enum( + Protobuf.Config.Config_LoRaConfig_RegionCode, +); + +export const LoRaValidationSchema = z.object({ + usePreset: z.boolean(), + modemPreset: ModemPresetEnum, + bandwidth: z.int(), + spreadFactor: z.int().max(12), + codingRate: z.int().min(0).max(10), + frequencyOffset: z.int(), + region: RegionCodeEnum, + hopLimit: z.int().min(0).max(7), + txEnabled: z.boolean(), + txPower: z.int().min(0), + channelNum: z.int(), + overrideDutyCycle: z.boolean(), + sx126xRxBoostedGain: z.boolean(), + overrideFrequency: z.int(), + ignoreIncoming: z.number().array(), + ignoreMqtt: z.boolean(), + configOkToMqtt: z.boolean(), +}); + +export type LoRaValidation = z.infer; diff --git a/src/validation/config/network.ts b/src/validation/config/network.ts index e58fb94d..2139b622 100644 --- a/src/validation/config/network.ts +++ b/src/validation/config/network.ts @@ -1,18 +1,18 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -const AddressModeEnum = z.nativeEnum( +const AddressModeEnum = z.enum( Protobuf.Config.Config_NetworkConfig_AddressMode, ); -const ProtocolFlagsEnum = z.nativeEnum( +const ProtocolFlagsEnum = z.enum( Protobuf.Config.Config_NetworkConfig_ProtocolFlags, ); export const NetworkValidationIpV4ConfigSchema = z.object({ - ip: z.string().ip(), - gateway: z.string().ip(), - subnet: z.string().ip(), - dns: z.string().ip(), + ip: z.ipv4(), + gateway: z.ipv4(), + subnet: z.ipv4(), + dns: z.ipv4(), }); export const NetworkValidationSchema = z.object({ diff --git a/src/validation/config/position.ts b/src/validation/config/position.ts index 84e67603..63837ae9 100644 --- a/src/validation/config/position.ts +++ b/src/validation/config/position.ts @@ -1,44 +1,22 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsEnum, IsInt } from "class-validator"; -const DeprecatedPositionValidationFields = ["gpsEnabled", "gpsAttemptTime"]; - -export class PositionValidation implements - Omit< - Protobuf.Config.Config_PositionConfig, - keyof Message | (typeof DeprecatedPositionValidationFields)[number] - > { - @IsInt() - positionBroadcastSecs: number; - - @IsBoolean() - positionBroadcastSmartEnabled: boolean; - - @IsBoolean() - fixedPosition: boolean; - - @IsInt() - gpsUpdateInterval: number; - - @IsInt() - positionFlags: number; - - @IsInt() - rxGpio: number; - - @IsInt() - txGpio: number; - - @IsInt() - broadcastSmartMinimumDistance: number; - - @IsInt() - broadcastSmartMinimumIntervalSecs: number; - - @IsInt() - gpsEnGpio: number; - - @IsEnum(Protobuf.Config.Config_PositionConfig_GpsMode) - gpsMode: Protobuf.Config.Config_PositionConfig_GpsMode; -} +const GpsModeEnum = z.enum( + Protobuf.Config.Config_PositionConfig_GpsMode, +); + +export const PositionValidationSchema = z.object({ + positionBroadcastSecs: z.int(), + positionBroadcastSmartEnabled: z.boolean(), + fixedPosition: z.boolean(), + gpsUpdateInterval: z.int(), + positionFlags: z.int(), + rxGpio: z.int(), + txGpio: z.int(), + broadcastSmartMinimumDistance: z.int(), + broadcastSmartMinimumIntervalSecs: z.int(), + gpsEnGpio: z.int(), + gpsMode: GpsModeEnum, +}); + +export type PositionValidation = z.infer; diff --git a/src/validation/config/power.ts b/src/validation/config/power.ts index a83f5f15..d28c6896 100644 --- a/src/validation/config/power.ts +++ b/src/validation/config/power.ts @@ -1,35 +1,14 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt, IsNumber, Max, Min } from "class-validator"; - -export class PowerValidation implements - Omit< - Protobuf.Config.Config_PowerConfig, - keyof Message | "powermonEnables" - > { - @IsBoolean() - isPowerSaving: boolean; - - @IsInt() - onBatteryShutdownAfterSecs: number; - - @IsNumber() - @Min(2) - @Max(4) - adcMultiplierOverride: number; - - @IsInt() - waitBluetoothSecs: number; - - @IsInt() - sdsSecs: number; - - @IsInt() - lsSecs: number; - - @IsInt() - minWakeSecs: number; - - @IsInt() - deviceBatteryInaAddress: number; -} +import { z } from "zod/v4"; + +export const PowerValidationSchema = z.object({ + isPowerSaving: z.boolean(), + onBatteryShutdownAfterSecs: z.int(), + adcMultiplierOverride: z.number().min(2).max(4), + waitBluetoothSecs: z.int(), + sdsSecs: z.int(), + lsSecs: z.int(), + minWakeSecs: z.int(), + deviceBatteryInaAddress: z.int(), +}); + +export type PowerValidation = z.infer; diff --git a/src/validation/config/security.ts b/src/validation/config/security.ts index 5d6ed059..a0a7c8c4 100644 --- a/src/validation/config/security.ts +++ b/src/validation/config/security.ts @@ -1,36 +1,14 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsString } from "class-validator"; - -export class SecurityValidation implements - Omit< - Protobuf.Config.Config_SecurityConfig, - | keyof Message - | "adminKey" - | "privateKey" - | "publicKey" - > { - @IsBoolean() - adminChannelEnabled: boolean; - - @IsString() - adminKey: [string, string, string]; - - @IsBoolean() - bluetoothLoggingEnabled: boolean; - - @IsBoolean() - debugLogApiEnabled: boolean; - - @IsBoolean() - isManaged: boolean; - - @IsString() - privateKey: string; - - @IsString() - publicKey: string; - - @IsBoolean() - serialEnabled: boolean; -} +import { z } from "zod/v4"; + +export const SecurityValidationSchema = z.object({ + adminChannelEnabled: z.boolean(), + adminKey: z.string().array().length(3), + bluetoothLoggingEnabled: z.boolean(), + debugLogApiEnabled: z.boolean(), + isManaged: z.boolean(), + privateKey: z.string(), + publicKey: z.string(), + serialEnabled: z.boolean(), +}); + +export type SecurityValidation = z.infer; diff --git a/src/validation/moduleConfig/ambientLighting.ts b/src/validation/moduleConfig/ambientLighting.ts index 93124502..b67e01b6 100644 --- a/src/validation/moduleConfig/ambientLighting.ts +++ b/src/validation/moduleConfig/ambientLighting.ts @@ -1,24 +1,13 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt } from "class-validator"; - -export class AmbientLightingValidation implements - Omit< - Protobuf.ModuleConfig.ModuleConfig_AmbientLightingConfig, - keyof Message - > { - @IsBoolean() - ledState: boolean; - - @IsInt() - current: number; - - @IsInt() - red: number; - - @IsInt() - green: number; - - @IsInt() - blue: number; -} +import { z } from "zod/v4"; + +export const AmbientLightingValidationSchema = z.object({ + ledState: z.boolean(), + current: z.int(), + red: z.int(), + green: z.int(), + blue: z.int(), +}); + +export type AmbientLightingValidation = z.infer< + typeof AmbientLightingValidationSchema +>; diff --git a/src/validation/moduleConfig/audio.ts b/src/validation/moduleConfig/audio.ts index b70e3dae..2ca3419d 100644 --- a/src/validation/moduleConfig/audio.ts +++ b/src/validation/moduleConfig/audio.ts @@ -1,28 +1,18 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsEnum, IsInt } from "class-validator"; -export class AudioValidation - implements - Omit { - @IsBoolean() - codec2Enabled: boolean; +const Audio_BaudEnum = z.enum( + Protobuf.ModuleConfig.ModuleConfig_AudioConfig_Audio_Baud, +); - @IsInt() - pttPin: number; +export const AudioValidationSchema = z.object({ + codec2Enabled: z.boolean(), + pttPin: z.int(), + bitrate: Audio_BaudEnum, + i2sWs: z.int(), + i2sSd: z.int(), + i2sDin: z.int(), + i2sSck: z.int(), +}); - @IsEnum(Protobuf.ModuleConfig.ModuleConfig_AudioConfig_Audio_Baud) - bitrate: Protobuf.ModuleConfig.ModuleConfig_AudioConfig_Audio_Baud; - - @IsInt() - i2sWs: number; - - @IsInt() - i2sSd: number; - - @IsInt() - i2sDin: number; - - @IsInt() - i2sSck: number; -} +export type AudioValidation = z.infer; diff --git a/src/validation/moduleConfig/cannedMessage.ts b/src/validation/moduleConfig/cannedMessage.ts index 2b70eaf5..6c3dc5e9 100644 --- a/src/validation/moduleConfig/cannedMessage.ts +++ b/src/validation/moduleConfig/cannedMessage.ts @@ -1,45 +1,24 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsEnum, IsInt, Length } from "class-validator"; -export class CannedMessageValidation implements - Omit< - Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig, - keyof Message - > { - @IsBoolean() - rotary1Enabled: boolean; - - @IsInt() - inputbrokerPinA: number; - - @IsInt() - inputbrokerPinB: number; - - @IsInt() - inputbrokerPinPress: number; - - @IsEnum(Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig_InputEventChar) - inputbrokerEventCw: - Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig_InputEventChar; - - @IsEnum(Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig_InputEventChar) - inputbrokerEventCcw: - Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig_InputEventChar; - - @IsEnum(Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig_InputEventChar) - inputbrokerEventPress: - Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig_InputEventChar; - - @IsBoolean() - updown1Enabled: boolean; - - @IsBoolean() - enabled: boolean; - - @Length(2, 30) - allowInputSource: string; - - @IsBoolean() - sendBell: boolean; -} +const InputEventCharEnum = z.enum( + Protobuf.ModuleConfig.ModuleConfig_CannedMessageConfig_InputEventChar, +); + +export const CannedMessageValidationSchema = z.object({ + rotary1Enabled: z.boolean(), + inputbrokerPinA: z.int(), + inputbrokerPinB: z.int(), + inputbrokerPinPress: z.int(), + inputbrokerEventCw: InputEventCharEnum, + inputbrokerEventCcw: InputEventCharEnum, + inputbrokerEventPress: InputEventCharEnum, + updown1Enabled: z.boolean(), + enabled: z.boolean(), + allowInputSource: z.string().min(2).max(30), + sendBell: z.boolean(), +}); + +export type CannedMessageValidation = z.infer< + typeof CannedMessageValidationSchema +>; diff --git a/src/validation/moduleConfig/detectionSensor.ts b/src/validation/moduleConfig/detectionSensor.ts index b812df17..e1099732 100644 --- a/src/validation/moduleConfig/detectionSensor.ts +++ b/src/validation/moduleConfig/detectionSensor.ts @@ -1,33 +1,16 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt, Length } from "class-validator"; - -export class DetectionSensorValidation implements - Omit< - Protobuf.ModuleConfig.ModuleConfig_DetectionSensorConfig, - keyof Message - > { - @IsBoolean() - enabled: boolean; - - @IsInt() - minimumBroadcastSecs: number; - - @IsInt() - stateBroadcastSecs: number; - - @IsBoolean() - sendBell: boolean; - - @Length(0, 20) - name: string; - - @IsInt() - monitorPin: number; - - @IsBoolean() - detectionTriggeredHigh: boolean; - - @IsBoolean() - usePullup: boolean; -} +import { z } from "zod/v4"; + +export const DetectionSensorValidationSchema = z.object({ + enabled: z.boolean(), + minimumBroadcastSecs: z.int(), + stateBroadcastSecs: z.int(), + sendBell: z.boolean(), + name: z.string().min(0).max(20), + monitorPin: z.int(), + detectionTriggeredHigh: z.boolean(), + usePullup: z.boolean(), +}); + +export type DetectionSensorValidation = z.infer< + typeof DetectionSensorValidationSchema +>; diff --git a/src/validation/moduleConfig/externalNotification.ts b/src/validation/moduleConfig/externalNotification.ts index 5b68ffa3..01d461a2 100644 --- a/src/validation/moduleConfig/externalNotification.ts +++ b/src/validation/moduleConfig/externalNotification.ts @@ -1,54 +1,23 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt } from "class-validator"; - -export class ExternalNotificationValidation implements - Omit< - Protobuf.ModuleConfig.ModuleConfig_ExternalNotificationConfig, - keyof Message - > { - @IsBoolean() - enabled: boolean; - - @IsInt() - outputMs: number; - - @IsInt() - output: number; - - @IsInt() - outputVibra: number; - - @IsInt() - outputBuzzer: number; - - @IsBoolean() - active: boolean; - - @IsBoolean() - alertMessage: boolean; - - @IsBoolean() - alertMessageVibra: boolean; - - @IsBoolean() - alertMessageBuzzer: boolean; - - @IsBoolean() - alertBell: boolean; - - @IsBoolean() - alertBellVibra: boolean; - - @IsBoolean() - alertBellBuzzer: boolean; - - @IsBoolean() - usePwm: boolean; - - @IsInt() - nagTimeout: number; - - @IsBoolean() - useI2sAsBuzzer: boolean; -} +import { z } from "zod/v4"; + +export const DetectionSensorValidationSchema = z.object({ + enabled: z.boolean(), + outputMs: z.int(), + output: z.int(), + outputVibra: z.int(), + outputBuzzer: z.int(), + active: z.boolean(), + alertMessage: z.boolean(), + alertMessageVibra: z.boolean(), + alertMessageBuzzer: z.boolean(), + alertBell: z.boolean(), + alertBellVibra: z.boolean(), + alertBellBuzzer: z.boolean(), + usePwm: z.boolean(), + nagTimeout: z.int(), + useI2sAsBuzzer: z.boolean(), +}); + +export type DetectionSensorValidation = z.infer< + typeof DetectionSensorValidationSchema +>; diff --git a/src/validation/moduleConfig/mqtt.ts b/src/validation/moduleConfig/mqtt.ts index 44312c8e..638f076f 100644 --- a/src/validation/moduleConfig/mqtt.ts +++ b/src/validation/moduleConfig/mqtt.ts @@ -1,59 +1,22 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { - IsBoolean, - IsNumber, - IsOptional, - IsString, - Length, -} from "class-validator"; - -export class MqttValidation implements - Omit< - Protobuf.ModuleConfig.ModuleConfig_MQTTConfig, - keyof Message | "mapReportSettings" - > { - @IsBoolean() - enabled: boolean; - - @Length(0, 30) - address: string; - - @Length(0, 30) - username: string; - - @Length(0, 30) - password: string; - - @IsBoolean() - encryptionEnabled: boolean; - - @IsBoolean() - jsonEnabled: boolean; - - @IsBoolean() - tlsEnabled: boolean; - - @IsString() - root: string; - - @IsBoolean() - proxyToClientEnabled: boolean; - - @IsBoolean() - mapReportingEnabled: boolean; - - mapReportSettings: MqttValidationMapReportSettings; -} - -export class MqttValidationMapReportSettings - implements - Omit { - @IsNumber() - @IsOptional() - publishIntervalSecs: number; - - @IsNumber() - @IsOptional() - positionPrecision: number; -} +import { z } from "zod/v4"; + +export const MqttValidationMapReportSettingsSchema = z.object({ + publishIntervalSecs: z.number().optional(), + positionPrecision: z.number().optional(), +}); + +export const MqttValidationSchema = z.object({ + enabled: z.boolean(), + address: z.string().min(0).max(30), + username: z.string().min(0).max(30), + password: z.string().min(0).max(30), + encryptionEnabled: z.boolean(), + jsonEnabled: z.boolean(), + tlsEnabled: z.boolean(), + root: z.string(), + proxyToClientEnabled: z.boolean(), + mapReportingEnabled: z.boolean(), + mapReportSettings: MqttValidationMapReportSettingsSchema, +}); + +export type MqttValidation = z.infer; diff --git a/src/validation/moduleConfig/neighborInfo.ts b/src/validation/moduleConfig/neighborInfo.ts index 513b8d7c..04285e0a 100644 --- a/src/validation/moduleConfig/neighborInfo.ts +++ b/src/validation/moduleConfig/neighborInfo.ts @@ -1,13 +1,10 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt } from "class-validator"; +import { z } from "zod/v4"; -export class NeighborInfoValidation - implements - Omit { - @IsBoolean() - enabled: boolean; +export const NeighborInfoValidationSchema = z.object({ + enabled: z.boolean(), + updateInterval: z.int(), +}); - @IsInt() - updateInterval: number; -} +export type NeighborInfoValidation = z.infer< + typeof NeighborInfoValidationSchema +>; diff --git a/src/validation/moduleConfig/paxcounter.ts b/src/validation/moduleConfig/paxcounter.ts index 6b3eeb9b..99afda3e 100644 --- a/src/validation/moduleConfig/paxcounter.ts +++ b/src/validation/moduleConfig/paxcounter.ts @@ -1,19 +1,10 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt } from "class-validator"; +import { z } from "zod/v4"; -export class PaxcounterValidation - implements - Omit { - @IsBoolean() - enabled: boolean; +export const PaxcounterValidationSchema = z.object({ + enabled: z.boolean(), + paxcounterUpdateInterval: z.int(), + bleThreshold: z.int(), + wifiThreshold: z.int(), +}); - @IsInt() - paxcounterUpdateInterval: number; - - @IsInt() - bleThreshold: number; - - @IsInt() - wifiThreshold: number; -} +export type PaxcounterValidation = z.infer; diff --git a/src/validation/moduleConfig/rangeTest.ts b/src/validation/moduleConfig/rangeTest.ts index 5debf256..a56992ef 100644 --- a/src/validation/moduleConfig/rangeTest.ts +++ b/src/validation/moduleConfig/rangeTest.ts @@ -1,16 +1,9 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt } from "class-validator"; +import { z } from "zod/v4"; -export class RangeTestValidation - implements - Omit { - @IsBoolean() - enabled: boolean; +export const RangeTestValidationSchema = z.object({ + enabled: z.boolean(), + sender: z.int(), + save: z.boolean(), +}); - @IsInt() - sender: number; - - @IsBoolean() - save: boolean; -} +export type RangeTestValidation = z.infer; diff --git a/src/validation/moduleConfig/serial.ts b/src/validation/moduleConfig/serial.ts index c814a71b..58cf0e0c 100644 --- a/src/validation/moduleConfig/serial.ts +++ b/src/validation/moduleConfig/serial.ts @@ -1,31 +1,22 @@ -import type { Message } from "@bufbuild/protobuf"; +import { z } from "zod/v4"; import { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsEnum, IsInt } from "class-validator"; -export class SerialValidation - implements - Omit { - @IsBoolean() - enabled: boolean; - - @IsBoolean() - echo: boolean; - - @IsInt() - rxd: number; - - @IsInt() - txd: number; - - @IsEnum(Protobuf.ModuleConfig.ModuleConfig_SerialConfig_Serial_Baud) - baud: Protobuf.ModuleConfig.ModuleConfig_SerialConfig_Serial_Baud; - - @IsInt() - timeout: number; - - @IsEnum(Protobuf.ModuleConfig.ModuleConfig_SerialConfig_Serial_Mode) - mode: Protobuf.ModuleConfig.ModuleConfig_SerialConfig_Serial_Mode; - - @IsBoolean() - overrideConsoleSerialPort: boolean; -} +const Serial_BaudEnum = z.enum( + Protobuf.ModuleConfig.ModuleConfig_SerialConfig_Serial_Baud, +); +const Serial_ModeEnum = z.enum( + Protobuf.ModuleConfig.ModuleConfig_SerialConfig_Serial_Mode, +); + +export const SerialValidationSchema = z.object({ + enabled: z.boolean(), + echo: z.boolean(), + rxd: z.int(), + txd: z.int(), + baud: Serial_BaudEnum, + timeout: z.int(), + mode: Serial_ModeEnum, + overrideConsoleSerialPort: z.boolean(), +}); + +export type SerialValidation = z.infer; diff --git a/src/validation/moduleConfig/storeForward.ts b/src/validation/moduleConfig/storeForward.ts index e539dd4b..6555de2c 100644 --- a/src/validation/moduleConfig/storeForward.ts +++ b/src/validation/moduleConfig/storeForward.ts @@ -1,24 +1,13 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt } from "class-validator"; - -export class StoreForwardValidation implements - Omit< - Protobuf.ModuleConfig.ModuleConfig_StoreForwardConfig, - keyof Message | "isServer" - > { - @IsBoolean() - enabled: boolean; - - @IsBoolean() - heartbeat: boolean; - - @IsInt() - records: number; - - @IsInt() - historyReturnMax: number; - - @IsInt() - historyReturnWindow: number; -} +import { z } from "zod/v4"; + +export const StoreForwardValidationSchema = z.object({ + enabled: z.boolean(), + heartbeat: z.boolean(), + records: z.int(), + historyReturnMax: z.int(), + historyReturnWindow: z.int(), +}); + +export type StoreForwardValidation = z.infer< + typeof StoreForwardValidationSchema +>; diff --git a/src/validation/moduleConfig/telemetry.ts b/src/validation/moduleConfig/telemetry.ts index 921fb04a..0d4cd7eb 100644 --- a/src/validation/moduleConfig/telemetry.ts +++ b/src/validation/moduleConfig/telemetry.ts @@ -1,37 +1,18 @@ -import type { Message } from "@bufbuild/protobuf"; -import type { Protobuf } from "@meshtastic/core"; -import { IsBoolean, IsInt } from "class-validator"; - -export class TelemetryValidation - implements - Omit { - @IsInt() - deviceUpdateInterval: number; - - @IsInt() - environmentUpdateInterval: number; - - @IsBoolean() - environmentMeasurementEnabled: boolean; - - @IsBoolean() - environmentScreenEnabled: boolean; - - @IsBoolean() - environmentDisplayFahrenheit: boolean; - - @IsBoolean() - airQualityEnabled: boolean; - - @IsInt() - airQualityInterval: number; - - @IsBoolean() - powerMeasurementEnabled: boolean; - - @IsInt() - powerUpdateInterval: number; - - @IsBoolean() - powerScreenEnabled: boolean; -} +import { z } from "zod/v4"; + +export const StoreForwardValidationSchema = z.object({ + deviceUpdateInterval: z.int(), + environmentUpdateInterval: z.int(), + environmentMeasurementEnabled: z.boolean(), + environmentScreenEnabled: z.boolean(), + environmentDisplayFahrenheit: z.boolean(), + airQualityEnabled: z.boolean(), + airQualityInterval: z.int(), + powerMeasurementEnabled: z.boolean(), + powerUpdateInterval: z.int(), + powerScreenEnabled: z.boolean(), +}); + +export type StoreForwardValidation = z.infer< + typeof StoreForwardValidationSchema +>; diff --git a/src/validation/rasterSource.ts b/src/validation/rasterSource.ts index 965206d4..940676d2 100644 --- a/src/validation/rasterSource.ts +++ b/src/validation/rasterSource.ts @@ -1,22 +1,14 @@ -import { IsArray, IsBoolean, IsNumber, IsString, IsUrl } from "class-validator"; +import { z } from "zod/v4"; -import type { RasterSource } from "@core/stores/appStore.ts"; +export const MapValidation_RasterSourcesSchema = z.object({ + enabled: z.boolean(), + title: z.string(), + tiles: z.url(), + tileSize: z.number(), +}); -export class MapValidation { - @IsArray() - rasterSources: MapValidation_RasterSources[]; -} +export const MapValidationSchema = z.object({ + rasterSources: MapValidation_RasterSourcesSchema.array(), +}); -export class MapValidation_RasterSources implements RasterSource { - @IsBoolean() - enabled: boolean; - - @IsString() - title: string; - - @IsUrl() - tiles: string; - - @IsNumber() - tileSize: number; -} +export type MapValidation = z.infer; diff --git a/src/validation/validate.ts b/src/validation/validate.ts index 7130add5..f147e0c6 100644 --- a/src/validation/validate.ts +++ b/src/validation/validate.ts @@ -1,7 +1,7 @@ -import { ZodError, ZodSchema } from "zod"; +import { ZodError, ZodType } from "zod/v4"; export function validateSchema( - schema: ZodSchema, + schema: ZodType, data: unknown, ): { success: true; data: T } | { success: false; errors: ZodError["issues"] } { const result = schema.safeParse(data);