@springfield/ham-radio-utils 4.9.0 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -78,10 +78,21 @@
78
78
  "properties": {
79
79
  "baudRate": {
80
80
  "type": "integer",
81
- "description": "Serial baud rate",
81
+ "description": "Default serial baud rate used to open the programming port. Must be included in baudRates when that list is present.",
82
82
  "minimum": 1200,
83
83
  "maximum": 115200
84
84
  },
85
+ "baudRates": {
86
+ "type": "array",
87
+ "description": "Baud rates the radio programming port accepts. Omit when only baudRate is valid. The HamBench import and write dialogs show a selector when more than one value is listed.",
88
+ "minItems": 1,
89
+ "uniqueItems": true,
90
+ "items": {
91
+ "type": "integer",
92
+ "minimum": 1200,
93
+ "maximum": 115200
94
+ }
95
+ },
85
96
  "dataBits": {
86
97
  "type": "integer",
87
98
  "description": "Number of data bits",
@@ -1 +1 @@
1
- {"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAOD,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAM;IAEjB,cAMC;IAED,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAEvD;IAED,iBAAiB,CAAC,SAAS,EAAE,OAAO,GAAG,gBAAgB,CAEtD;IAED,OAAO,CAAC,eAAe;CAqBxB"}
1
+ {"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAOD,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAM;IAEjB,cAMC;IAED,qBAAqB,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAiBvD;IAED,iBAAiB,CAAC,SAAS,EAAE,OAAO,GAAG,gBAAgB,CAEtD;IAED,OAAO,CAAC,eAAe;CAsBxB"}
@@ -12,7 +12,18 @@ export class SchemaValidator {
12
12
  addFormats(this.ajv);
13
13
  }
14
14
  validateRadioProtocol(config) {
15
- return this.validateAgainst(radioProtocolSchema, config);
15
+ const schemaResult = this.validateAgainst(radioProtocolSchema, config);
16
+ if (!schemaResult.valid) {
17
+ return schemaResult;
18
+ }
19
+ const baudRateErrors = baudRateMembershipErrors(config);
20
+ if (baudRateErrors.length === 0) {
21
+ return schemaResult;
22
+ }
23
+ return {
24
+ valid: false,
25
+ errors: baudRateErrors,
26
+ };
16
27
  }
17
28
  validateMemoryMap(memoryMap) {
18
29
  return this.validateAgainst(radioMemoryMapSchema, memoryMap);
@@ -36,4 +47,26 @@ export class SchemaValidator {
36
47
  };
37
48
  }
38
49
  }
50
+ function isRecord(value) {
51
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
52
+ }
53
+ /**
54
+ * `baudRate` is the default open speed. When a radio lists `baudRates`, that
55
+ * default must be one of the accepted values so the UI can preselect it.
56
+ */
57
+ function baudRateMembershipErrors(config) {
58
+ if (!isRecord(config) || !isRecord(config.serialConfig)) {
59
+ return [];
60
+ }
61
+ const serialConfig = config.serialConfig;
62
+ const baudRate = serialConfig.baudRate;
63
+ const baudRates = serialConfig.baudRates;
64
+ if (!Array.isArray(baudRates) || typeof baudRate !== 'number') {
65
+ return [];
66
+ }
67
+ if (baudRates.includes(baudRate)) {
68
+ return [];
69
+ }
70
+ return ['/serialConfig/baudRate: must be included in baudRates'];
71
+ }
39
72
  //# sourceMappingURL=schema-validator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAC1B,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,mBAAmB,MAAM,uCAAuC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC9F,OAAO,oBAAoB,MAAM,yCAAyC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAYjG,MAAM,OAAO,eAAe;IAClB,GAAG,CAAM;IAEjB;QACE,IAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC;YACjB,SAAS,EAAE,IAAI;YACf,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACF,UAA2C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IAED,qBAAqB,CAAC,MAAe;QACnC,OAAO,IAAI,CAAC,eAAe,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC;IAED,iBAAiB,CAAC,SAAkB;QAClC,OAAO,IAAI,CAAC,eAAe,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;IAC/D,CAAC;IAEO,eAAe,CAAC,MAAc,EAAE,IAAa;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAE7B,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACzB,CAAC;QAED,IAAI,MAAM,GAAa,EAAE,CAAC;QAC1B,IAAI,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAe,EAAE,EAAE;gBAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,IAAI,MAAM,CAAC;gBAC1C,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;YACrC,CAAC,CAAC,CAAC;QACL,CAAC;QAED,OAAO;YACL,MAAM;YACN,KAAK,EAAE,KAAK;SACb,CAAC;IACJ,CAAC;CACF","sourcesContent":["import { Ajv } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport radioProtocolSchema from '../schemas/radio-protocol-schema.json' with { type: 'json' };\nimport radioMemoryMapSchema from '../schemas/radio-memory-map-schema.json' with { type: 'json' };\n\nexport interface ValidationResult {\n valid: boolean;\n errors?: string[];\n}\n\ninterface AjvError {\n instancePath?: string;\n message?: string;\n}\n\nexport class SchemaValidator {\n private ajv: Ajv;\n\n constructor() {\n this.ajv = new Ajv({\n allErrors: true,\n verbose: true,\n });\n (addFormats as unknown as (ajv: Ajv) => Ajv)(this.ajv);\n }\n\n validateRadioProtocol(config: unknown): ValidationResult {\n return this.validateAgainst(radioProtocolSchema, config);\n }\n\n validateMemoryMap(memoryMap: unknown): ValidationResult {\n return this.validateAgainst(radioMemoryMapSchema, memoryMap);\n }\n\n private validateAgainst(schema: object, data: unknown): ValidationResult {\n const validate = this.ajv.compile(schema);\n const valid = validate(data);\n\n if (valid) {\n return { valid: true };\n }\n\n let errors: string[] = [];\n if (validate.errors && validate.errors.length > 0) {\n errors = validate.errors.map((error: AjvError) => {\n const path = error.instancePath || 'root';\n return `${path}: ${error.message}`;\n });\n }\n\n return {\n errors,\n valid: false,\n };\n }\n}\n"]}
1
+ {"version":3,"file":"schema-validator.js","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ajv } from 'ajv';\nimport addFormats from 'ajv-formats';\nimport radioProtocolSchema from '../schemas/radio-protocol-schema.json' with { type: 'json' };\nimport radioMemoryMapSchema from '../schemas/radio-memory-map-schema.json' with { type: 'json' };\n\nexport interface ValidationResult {\n valid: boolean;\n errors?: string[];\n}\n\ninterface AjvError {\n instancePath?: string;\n message?: string;\n}\n\nexport class SchemaValidator {\n private ajv: Ajv;\n\n constructor() {\n this.ajv = new Ajv({\n allErrors: true,\n verbose: true,\n });\n (addFormats as unknown as (ajv: Ajv) => Ajv)(this.ajv);\n }\n\n validateRadioProtocol(config: unknown): ValidationResult {\n const schemaResult = this.validateAgainst(radioProtocolSchema, config);\n\n if (!schemaResult.valid) {\n return schemaResult;\n }\n\n const baudRateErrors = baudRateMembershipErrors(config);\n\n if (baudRateErrors.length === 0) {\n return schemaResult;\n }\n\n return {\n valid: false,\n errors: baudRateErrors,\n };\n }\n\n validateMemoryMap(memoryMap: unknown): ValidationResult {\n return this.validateAgainst(radioMemoryMapSchema, memoryMap);\n }\n\n private validateAgainst(schema: object, data: unknown): ValidationResult {\n const validate = this.ajv.compile(schema);\n const valid = validate(data);\n\n if (valid) {\n return { valid: true };\n }\n\n let errors: string[] = [];\n\n if (validate.errors && validate.errors.length > 0) {\n errors = validate.errors.map((error: AjvError) => {\n const path = error.instancePath || 'root';\n return `${path}: ${error.message}`;\n });\n }\n\n return {\n errors,\n valid: false,\n };\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * `baudRate` is the default open speed. When a radio lists `baudRates`, that\n * default must be one of the accepted values so the UI can preselect it.\n */\nfunction baudRateMembershipErrors(config: unknown): string[] {\n if (!isRecord(config) || !isRecord(config.serialConfig)) {\n return [];\n }\n\n const serialConfig = config.serialConfig;\n const baudRate = serialConfig.baudRate;\n const baudRates = serialConfig.baudRates;\n\n if (!Array.isArray(baudRates) || typeof baudRate !== 'number') {\n return [];\n }\n\n if (baudRates.includes(baudRate)) {\n return [];\n }\n\n return ['/serialConfig/baudRate: must be included in baudRates'];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springfield/ham-radio-utils",
3
- "version": "4.9.0",
3
+ "version": "4.10.0",
4
4
  "author": "Bryan Hunt",
5
5
  "license": "MIT",
6
6
  "readme": "README.md",
@@ -78,10 +78,21 @@
78
78
  "properties": {
79
79
  "baudRate": {
80
80
  "type": "integer",
81
- "description": "Serial baud rate",
81
+ "description": "Default serial baud rate used to open the programming port. Must be included in baudRates when that list is present.",
82
82
  "minimum": 1200,
83
83
  "maximum": 115200
84
84
  },
85
+ "baudRates": {
86
+ "type": "array",
87
+ "description": "Baud rates the radio programming port accepts. Omit when only baudRate is valid. The HamBench import and write dialogs show a selector when more than one value is listed.",
88
+ "minItems": 1,
89
+ "uniqueItems": true,
90
+ "items": {
91
+ "type": "integer",
92
+ "minimum": 1200,
93
+ "maximum": 115200
94
+ }
95
+ },
85
96
  "dataBits": {
86
97
  "type": "integer",
87
98
  "description": "Number of data bits",
@@ -25,7 +25,22 @@ export class SchemaValidator {
25
25
  }
26
26
 
27
27
  validateRadioProtocol(config: unknown): ValidationResult {
28
- return this.validateAgainst(radioProtocolSchema, config);
28
+ const schemaResult = this.validateAgainst(radioProtocolSchema, config);
29
+
30
+ if (!schemaResult.valid) {
31
+ return schemaResult;
32
+ }
33
+
34
+ const baudRateErrors = baudRateMembershipErrors(config);
35
+
36
+ if (baudRateErrors.length === 0) {
37
+ return schemaResult;
38
+ }
39
+
40
+ return {
41
+ valid: false,
42
+ errors: baudRateErrors,
43
+ };
29
44
  }
30
45
 
31
46
  validateMemoryMap(memoryMap: unknown): ValidationResult {
@@ -41,6 +56,7 @@ export class SchemaValidator {
41
56
  }
42
57
 
43
58
  let errors: string[] = [];
59
+
44
60
  if (validate.errors && validate.errors.length > 0) {
45
61
  errors = validate.errors.map((error: AjvError) => {
46
62
  const path = error.instancePath || 'root';
@@ -54,3 +70,31 @@ export class SchemaValidator {
54
70
  };
55
71
  }
56
72
  }
73
+
74
+ function isRecord(value: unknown): value is Record<string, unknown> {
75
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
76
+ }
77
+
78
+ /**
79
+ * `baudRate` is the default open speed. When a radio lists `baudRates`, that
80
+ * default must be one of the accepted values so the UI can preselect it.
81
+ */
82
+ function baudRateMembershipErrors(config: unknown): string[] {
83
+ if (!isRecord(config) || !isRecord(config.serialConfig)) {
84
+ return [];
85
+ }
86
+
87
+ const serialConfig = config.serialConfig;
88
+ const baudRate = serialConfig.baudRate;
89
+ const baudRates = serialConfig.baudRates;
90
+
91
+ if (!Array.isArray(baudRates) || typeof baudRate !== 'number') {
92
+ return [];
93
+ }
94
+
95
+ if (baudRates.includes(baudRate)) {
96
+ return [];
97
+ }
98
+
99
+ return ['/serialConfig/baudRate: must be included in baudRates'];
100
+ }