@springfield/ham-radio-utils 4.1.2 → 4.2.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.
@@ -0,0 +1,151 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Radio Memory-Map DSL Schema",
4
+ "description": "Schema for defining radio EEPROM settings layout as JSON (Chirp MEM_FORMAT as data)",
5
+ "type": "object",
6
+ "required": ["structs"],
7
+ "properties": {
8
+ "version": {
9
+ "type": "string",
10
+ "description": "Optional map version"
11
+ },
12
+ "description": {
13
+ "type": "string"
14
+ },
15
+ "structs": {
16
+ "type": "array",
17
+ "minItems": 1,
18
+ "items": {
19
+ "$ref": "#/definitions/struct"
20
+ }
21
+ }
22
+ },
23
+ "definitions": {
24
+ "seek": {
25
+ "oneOf": [
26
+ { "type": "integer", "minimum": 0 },
27
+ { "type": "string", "pattern": "^(0[xX][0-9a-fA-F]+|[0-9]+)$" }
28
+ ]
29
+ },
30
+ "ui": {
31
+ "type": "object",
32
+ "required": ["group", "label", "widget"],
33
+ "properties": {
34
+ "group": { "type": "string" },
35
+ "label": { "type": "string" },
36
+ "widget": {
37
+ "type": "string",
38
+ "enum": ["integer", "select", "switch", "text", "number"]
39
+ },
40
+ "description": { "type": "string" },
41
+ "writable": { "type": "boolean" }
42
+ },
43
+ "additionalProperties": false
44
+ },
45
+ "value": {
46
+ "oneOf": [
47
+ {
48
+ "type": "object",
49
+ "required": ["kind"],
50
+ "properties": {
51
+ "kind": { "const": "integer" },
52
+ "min": { "type": "number" },
53
+ "max": { "type": "number" }
54
+ },
55
+ "additionalProperties": false
56
+ },
57
+ {
58
+ "type": "object",
59
+ "required": ["kind"],
60
+ "properties": {
61
+ "kind": { "const": "boolean" }
62
+ },
63
+ "additionalProperties": false
64
+ },
65
+ {
66
+ "type": "object",
67
+ "required": ["kind", "values"],
68
+ "properties": {
69
+ "kind": { "const": "enum" },
70
+ "values": {
71
+ "type": "array",
72
+ "items": { "type": "string" },
73
+ "minItems": 1
74
+ }
75
+ },
76
+ "additionalProperties": false
77
+ },
78
+ {
79
+ "type": "object",
80
+ "required": ["kind", "length"],
81
+ "properties": {
82
+ "kind": { "const": "ascii" },
83
+ "length": { "type": "integer", "minimum": 1 }
84
+ },
85
+ "additionalProperties": false
86
+ },
87
+ {
88
+ "type": "object",
89
+ "required": ["kind", "length"],
90
+ "properties": {
91
+ "kind": { "const": "digits" },
92
+ "length": { "type": "integer", "minimum": 1 },
93
+ "scale": { "type": "number" }
94
+ },
95
+ "additionalProperties": false
96
+ },
97
+ {
98
+ "type": "object",
99
+ "required": ["kind", "length"],
100
+ "properties": {
101
+ "kind": { "const": "dtmf" },
102
+ "length": { "type": "integer", "minimum": 1 },
103
+ "charset": { "type": "string" }
104
+ },
105
+ "additionalProperties": false
106
+ },
107
+ {
108
+ "type": "object",
109
+ "required": ["kind", "length"],
110
+ "properties": {
111
+ "kind": { "const": "bbcd" },
112
+ "length": { "type": "integer", "minimum": 1 }
113
+ },
114
+ "additionalProperties": false
115
+ }
116
+ ]
117
+ },
118
+ "field": {
119
+ "type": "object",
120
+ "required": ["id", "type"],
121
+ "properties": {
122
+ "id": { "type": "string", "minLength": 1 },
123
+ "type": {
124
+ "type": "string",
125
+ "enum": ["u8", "u16", "bits", "char"]
126
+ },
127
+ "width": { "type": "integer", "minimum": 1, "maximum": 8 },
128
+ "reserved": { "type": "boolean" },
129
+ "value": { "$ref": "#/definitions/value" },
130
+ "ui": { "$ref": "#/definitions/ui" }
131
+ },
132
+ "additionalProperties": false
133
+ },
134
+ "struct": {
135
+ "type": "object",
136
+ "required": ["id", "seek", "fields"],
137
+ "properties": {
138
+ "id": { "type": "string", "minLength": 1 },
139
+ "seek": { "$ref": "#/definitions/seek" },
140
+ "fields": {
141
+ "type": "array",
142
+ "minItems": 1,
143
+ "items": { "$ref": "#/definitions/field" }
144
+ },
145
+ "count": { "type": "integer", "minimum": 1 },
146
+ "stride": { "type": "integer", "minimum": 1 }
147
+ },
148
+ "additionalProperties": false
149
+ }
150
+ }
151
+ }
@@ -6,5 +6,7 @@ export declare class SchemaValidator {
6
6
  private ajv;
7
7
  constructor();
8
8
  validateRadioProtocol(config: unknown): ValidationResult;
9
+ validateMemoryMap(memoryMap: unknown): ValidationResult;
10
+ private validateAgainst;
9
11
  }
10
12
  //# sourceMappingURL=schema-validator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema-validator.d.ts","sourceRoot":"","sources":["../../src/utils/schema-validator.ts"],"names":[],"mappings":"AAIA,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,CAoBvD;CACF"}
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,6 +1,7 @@
1
1
  import { Ajv } from 'ajv';
2
2
  import addFormats from 'ajv-formats';
3
3
  import radioProtocolSchema from '../schemas/radio-protocol-schema.json' with { type: 'json' };
4
+ import radioMemoryMapSchema from '../schemas/radio-memory-map-schema.json' with { type: 'json' };
4
5
  export class SchemaValidator {
5
6
  ajv;
6
7
  constructor() {
@@ -11,8 +12,14 @@ export class SchemaValidator {
11
12
  addFormats(this.ajv);
12
13
  }
13
14
  validateRadioProtocol(config) {
14
- const validate = this.ajv.compile(radioProtocolSchema);
15
- const valid = validate(config);
15
+ return this.validateAgainst(radioProtocolSchema, config);
16
+ }
17
+ validateMemoryMap(memoryMap) {
18
+ return this.validateAgainst(radioMemoryMapSchema, memoryMap);
19
+ }
20
+ validateAgainst(schema, data) {
21
+ const validate = this.ajv.compile(schema);
22
+ const valid = validate(data);
16
23
  if (valid) {
17
24
  return { valid: true };
18
25
  }
@@ -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;AAY9F,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,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;QAE/B,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' };\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 validate = this.ajv.compile(radioProtocolSchema);\n const valid = validate(config);\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":"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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springfield/ham-radio-utils",
3
- "version": "4.1.2",
3
+ "version": "4.2.0",
4
4
  "author": "Bryan Hunt",
5
5
  "license": "MIT",
6
6
  "readme": "README.md",
@@ -30,7 +30,7 @@
30
30
  "test:unit": "tsx --test test/unit/**/*.test.ts"
31
31
  },
32
32
  "dependencies": {
33
- "@springfield/ham-radio-api": "^17.0.0",
33
+ "@springfield/ham-radio-api": "^17.1.0",
34
34
  "ajv": "^8.20.0",
35
35
  "ajv-formats": "^3.0.1",
36
36
  "fishery": "^2.4.0",
package/src/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export * from './memory/segmented-memory.js';
2
+ export * from './memory/memory-map-codec.js';
3
+ export * from './memory/memory-map-ui.js';
2
4
 
3
5
  export * from './test-utils/radio-channel-factory.js';
4
6
  export * from './test-utils/radio-programmed-channel-factory.js';
@@ -15,3 +17,4 @@ export * from './utils/ui-logger-factory.js';
15
17
 
16
18
  // Export the radio protocol schema
17
19
  export { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };
20
+ export { default as radioMemoryMapSchema } from './schemas/radio-memory-map-schema.json' with { type: 'json' };