@tx5dr/contracts 1.7.12 → 2.5.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.
Files changed (255) hide show
  1. package/dist/index.d.ts +7 -1
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +6 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts +2 -0
  6. package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts.map +1 -0
  7. package/dist/schema/__tests__/android-operator-audio.schema.test.js +27 -0
  8. package/dist/schema/__tests__/android-operator-audio.schema.test.js.map +1 -0
  9. package/dist/schema/__tests__/assisted-queue.schema.test.d.ts +2 -0
  10. package/dist/schema/__tests__/assisted-queue.schema.test.d.ts.map +1 -0
  11. package/dist/schema/__tests__/assisted-queue.schema.test.js +114 -0
  12. package/dist/schema/__tests__/assisted-queue.schema.test.js.map +1 -0
  13. package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts +2 -0
  14. package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts.map +1 -0
  15. package/dist/schema/__tests__/audio-sidecar.schema.test.js +42 -0
  16. package/dist/schema/__tests__/audio-sidecar.schema.test.js.map +1 -0
  17. package/dist/schema/__tests__/audio.schema.test.js +92 -1
  18. package/dist/schema/__tests__/audio.schema.test.js.map +1 -1
  19. package/dist/schema/__tests__/diagnostics.schema.test.d.ts +2 -0
  20. package/dist/schema/__tests__/diagnostics.schema.test.d.ts.map +1 -0
  21. package/dist/schema/__tests__/diagnostics.schema.test.js +31 -0
  22. package/dist/schema/__tests__/diagnostics.schema.test.js.map +1 -0
  23. package/dist/schema/__tests__/logbook-health.schema.test.d.ts +2 -0
  24. package/dist/schema/__tests__/logbook-health.schema.test.d.ts.map +1 -0
  25. package/dist/schema/__tests__/logbook-health.schema.test.js +104 -0
  26. package/dist/schema/__tests__/logbook-health.schema.test.js.map +1 -0
  27. package/dist/schema/__tests__/meter.schema.test.d.ts +2 -0
  28. package/dist/schema/__tests__/meter.schema.test.d.ts.map +1 -0
  29. package/dist/schema/__tests__/meter.schema.test.js +27 -0
  30. package/dist/schema/__tests__/meter.schema.test.js.map +1 -0
  31. package/dist/schema/__tests__/observability.schema.test.d.ts +2 -0
  32. package/dist/schema/__tests__/observability.schema.test.d.ts.map +1 -0
  33. package/dist/schema/__tests__/observability.schema.test.js +12 -0
  34. package/dist/schema/__tests__/observability.schema.test.js.map +1 -0
  35. package/dist/schema/__tests__/operator-context-report.schema.test.d.ts +2 -0
  36. package/dist/schema/__tests__/operator-context-report.schema.test.d.ts.map +1 -0
  37. package/dist/schema/__tests__/operator-context-report.schema.test.js +70 -0
  38. package/dist/schema/__tests__/operator-context-report.schema.test.js.map +1 -0
  39. package/dist/schema/__tests__/operator.schema.test.js +9 -3
  40. package/dist/schema/__tests__/operator.schema.test.js.map +1 -1
  41. package/dist/schema/__tests__/plugin-market.schema.test.js +149 -3
  42. package/dist/schema/__tests__/plugin-market.schema.test.js.map +1 -1
  43. package/dist/schema/__tests__/qso.schema.test.d.ts +2 -0
  44. package/dist/schema/__tests__/qso.schema.test.d.ts.map +1 -0
  45. package/dist/schema/__tests__/qso.schema.test.js +70 -0
  46. package/dist/schema/__tests__/qso.schema.test.js.map +1 -0
  47. package/dist/schema/__tests__/radio-power-support.test.js +15 -0
  48. package/dist/schema/__tests__/radio-power-support.test.js.map +1 -1
  49. package/dist/schema/__tests__/radio-profile.schema.test.d.ts +2 -0
  50. package/dist/schema/__tests__/radio-profile.schema.test.d.ts.map +1 -0
  51. package/dist/schema/__tests__/radio-profile.schema.test.js +36 -0
  52. package/dist/schema/__tests__/radio-profile.schema.test.js.map +1 -0
  53. package/dist/schema/__tests__/radio.schema.test.js +92 -1
  54. package/dist/schema/__tests__/radio.schema.test.js.map +1 -1
  55. package/dist/schema/__tests__/realtime-settings.schema.test.js +2 -1
  56. package/dist/schema/__tests__/realtime-settings.schema.test.js.map +1 -1
  57. package/dist/schema/__tests__/remote-access.schema.test.d.ts +2 -0
  58. package/dist/schema/__tests__/remote-access.schema.test.d.ts.map +1 -0
  59. package/dist/schema/__tests__/remote-access.schema.test.js +18 -0
  60. package/dist/schema/__tests__/remote-access.schema.test.js.map +1 -0
  61. package/dist/schema/__tests__/spectrum.schema.test.d.ts +2 -0
  62. package/dist/schema/__tests__/spectrum.schema.test.d.ts.map +1 -0
  63. package/dist/schema/__tests__/spectrum.schema.test.js +107 -0
  64. package/dist/schema/__tests__/spectrum.schema.test.js.map +1 -0
  65. package/dist/schema/__tests__/strategy-transmission.schema.test.d.ts +2 -0
  66. package/dist/schema/__tests__/strategy-transmission.schema.test.d.ts.map +1 -0
  67. package/dist/schema/__tests__/strategy-transmission.schema.test.js +95 -0
  68. package/dist/schema/__tests__/strategy-transmission.schema.test.js.map +1 -0
  69. package/dist/schema/android-operator-audio.schema.d.ts +174 -0
  70. package/dist/schema/android-operator-audio.schema.d.ts.map +1 -0
  71. package/dist/schema/android-operator-audio.schema.js +38 -0
  72. package/dist/schema/android-operator-audio.schema.js.map +1 -0
  73. package/dist/schema/api-response.schema.d.ts +503 -44
  74. package/dist/schema/api-response.schema.d.ts.map +1 -1
  75. package/dist/schema/api-response.schema.js +9 -0
  76. package/dist/schema/api-response.schema.js.map +1 -1
  77. package/dist/schema/audio-sidecar.schema.d.ts +75 -0
  78. package/dist/schema/audio-sidecar.schema.d.ts.map +1 -1
  79. package/dist/schema/audio-sidecar.schema.js +32 -0
  80. package/dist/schema/audio-sidecar.schema.js.map +1 -1
  81. package/dist/schema/audio.schema.d.ts +4749 -3
  82. package/dist/schema/audio.schema.d.ts.map +1 -1
  83. package/dist/schema/audio.schema.js +101 -2
  84. package/dist/schema/audio.schema.js.map +1 -1
  85. package/dist/schema/auth.schema.d.ts +173 -14
  86. package/dist/schema/auth.schema.d.ts.map +1 -1
  87. package/dist/schema/auth.schema.js +65 -1
  88. package/dist/schema/auth.schema.js.map +1 -1
  89. package/dist/schema/cpu-profile.schema.d.ts +2 -2
  90. package/dist/schema/cw-decoder.schema.d.ts +74 -74
  91. package/dist/schema/cw-keyer.schema.d.ts +20 -14
  92. package/dist/schema/cw-keyer.schema.d.ts.map +1 -1
  93. package/dist/schema/cw-keyer.schema.js +3 -0
  94. package/dist/schema/cw-keyer.schema.js.map +1 -1
  95. package/dist/schema/desktop-https.schema.d.ts +6 -6
  96. package/dist/schema/device-ui.schema.d.ts +16 -16
  97. package/dist/schema/diagnostics.schema.d.ts +163 -0
  98. package/dist/schema/diagnostics.schema.d.ts.map +1 -0
  99. package/dist/schema/diagnostics.schema.js +46 -0
  100. package/dist/schema/diagnostics.schema.js.map +1 -0
  101. package/dist/schema/ft8.schema.d.ts +12 -2
  102. package/dist/schema/ft8.schema.d.ts.map +1 -1
  103. package/dist/schema/ft8.schema.js +8 -0
  104. package/dist/schema/ft8.schema.js.map +1 -1
  105. package/dist/schema/image-radio.schema.d.ts +2597 -0
  106. package/dist/schema/image-radio.schema.d.ts.map +1 -0
  107. package/dist/schema/image-radio.schema.js +283 -0
  108. package/dist/schema/image-radio.schema.js.map +1 -0
  109. package/dist/schema/logbook.schema.d.ts +2128 -18
  110. package/dist/schema/logbook.schema.d.ts.map +1 -1
  111. package/dist/schema/logbook.schema.js +159 -1
  112. package/dist/schema/logbook.schema.js.map +1 -1
  113. package/dist/schema/mode.schema.d.ts +4 -6
  114. package/dist/schema/mode.schema.d.ts.map +1 -1
  115. package/dist/schema/mode.schema.js +18 -6
  116. package/dist/schema/mode.schema.js.map +1 -1
  117. package/dist/schema/observability.schema.d.ts +66 -0
  118. package/dist/schema/observability.schema.d.ts.map +1 -0
  119. package/dist/schema/observability.schema.js +20 -0
  120. package/dist/schema/observability.schema.js.map +1 -0
  121. package/dist/schema/openwebrx.schema.d.ts +2 -2
  122. package/dist/schema/operator.schema.d.ts +2682 -6
  123. package/dist/schema/operator.schema.d.ts.map +1 -1
  124. package/dist/schema/operator.schema.js +10 -20
  125. package/dist/schema/operator.schema.js.map +1 -1
  126. package/dist/schema/plugin.schema.d.ts +1977 -492
  127. package/dist/schema/plugin.schema.d.ts.map +1 -1
  128. package/dist/schema/plugin.schema.js +165 -14
  129. package/dist/schema/plugin.schema.js.map +1 -1
  130. package/dist/schema/process-monitor.schema.d.ts +132 -132
  131. package/dist/schema/pskreporter.schema.d.ts +1 -3
  132. package/dist/schema/pskreporter.schema.d.ts.map +1 -1
  133. package/dist/schema/pskreporter.schema.js +1 -3
  134. package/dist/schema/pskreporter.schema.js.map +1 -1
  135. package/dist/schema/qso.schema.d.ts +246 -1
  136. package/dist/schema/qso.schema.d.ts.map +1 -1
  137. package/dist/schema/qso.schema.js +68 -0
  138. package/dist/schema/qso.schema.js.map +1 -1
  139. package/dist/schema/radio-capability.schema.d.ts +31 -40
  140. package/dist/schema/radio-capability.schema.d.ts.map +1 -1
  141. package/dist/schema/radio-capability.schema.js +8 -15
  142. package/dist/schema/radio-capability.schema.js.map +1 -1
  143. package/dist/schema/radio-power-support.d.ts.map +1 -1
  144. package/dist/schema/radio-power-support.js +3 -0
  145. package/dist/schema/radio-power-support.js.map +1 -1
  146. package/dist/schema/radio-power.schema.d.ts +102 -13
  147. package/dist/schema/radio-power.schema.d.ts.map +1 -1
  148. package/dist/schema/radio-power.schema.js +15 -8
  149. package/dist/schema/radio-power.schema.js.map +1 -1
  150. package/dist/schema/radio-profile.schema.d.ts +1491 -97
  151. package/dist/schema/radio-profile.schema.d.ts.map +1 -1
  152. package/dist/schema/radio-profile.schema.js +7 -0
  153. package/dist/schema/radio-profile.schema.js.map +1 -1
  154. package/dist/schema/radio.schema.d.ts +442 -48
  155. package/dist/schema/radio.schema.d.ts.map +1 -1
  156. package/dist/schema/radio.schema.js +75 -7
  157. package/dist/schema/radio.schema.js.map +1 -1
  158. package/dist/schema/realtime.schema.d.ts +60 -52
  159. package/dist/schema/realtime.schema.d.ts.map +1 -1
  160. package/dist/schema/realtime.schema.js +9 -1
  161. package/dist/schema/realtime.schema.js.map +1 -1
  162. package/dist/schema/rigctld.schema.d.ts +6 -6
  163. package/dist/schema/slot-info.schema.d.ts +28 -9
  164. package/dist/schema/slot-info.schema.d.ts.map +1 -1
  165. package/dist/schema/slot-info.schema.js +17 -10
  166. package/dist/schema/slot-info.schema.js.map +1 -1
  167. package/dist/schema/spectrum-config.schema.d.ts +638 -0
  168. package/dist/schema/spectrum-config.schema.d.ts.map +1 -0
  169. package/dist/schema/spectrum-config.schema.js +129 -0
  170. package/dist/schema/spectrum-config.schema.js.map +1 -0
  171. package/dist/schema/spectrum.schema.d.ts +1323 -30
  172. package/dist/schema/spectrum.schema.d.ts.map +1 -1
  173. package/dist/schema/spectrum.schema.js +72 -0
  174. package/dist/schema/spectrum.schema.js.map +1 -1
  175. package/dist/schema/station-info.schema.d.ts +5 -0
  176. package/dist/schema/station-info.schema.d.ts.map +1 -1
  177. package/dist/schema/station-info.schema.js +5 -0
  178. package/dist/schema/station-info.schema.js.map +1 -1
  179. package/dist/schema/system.schema.d.ts +56 -6
  180. package/dist/schema/system.schema.d.ts.map +1 -1
  181. package/dist/schema/system.schema.js +22 -0
  182. package/dist/schema/system.schema.js.map +1 -1
  183. package/dist/schema/transmission.schema.d.ts +8 -7
  184. package/dist/schema/transmission.schema.d.ts.map +1 -1
  185. package/dist/schema/transmission.schema.js +2 -7
  186. package/dist/schema/transmission.schema.js.map +1 -1
  187. package/dist/schema/tx-audio-input.schema.d.ts +8 -0
  188. package/dist/schema/tx-audio-input.schema.d.ts.map +1 -0
  189. package/dist/schema/tx-audio-input.schema.js +19 -0
  190. package/dist/schema/tx-audio-input.schema.js.map +1 -0
  191. package/dist/schema/voice-keyer.schema.d.ts +16 -16
  192. package/dist/schema/voice.schema.d.ts +1 -1
  193. package/dist/schema/voice.schema.d.ts.map +1 -1
  194. package/dist/schema/voice.schema.js +1 -1
  195. package/dist/schema/voice.schema.js.map +1 -1
  196. package/dist/schema/websocket.schema.d.ts +35811 -7829
  197. package/dist/schema/websocket.schema.d.ts.map +1 -1
  198. package/dist/schema/websocket.schema.js +453 -9
  199. package/dist/schema/websocket.schema.js.map +1 -1
  200. package/package.json +6 -1
  201. package/src/index.ts +7 -0
  202. package/src/schema/__tests__/android-operator-audio.schema.test.ts +31 -0
  203. package/src/schema/__tests__/assisted-queue.schema.test.ts +122 -0
  204. package/src/schema/__tests__/audio-sidecar.schema.test.ts +48 -0
  205. package/src/schema/__tests__/audio.schema.test.ts +102 -0
  206. package/src/schema/__tests__/diagnostics.schema.test.ts +32 -0
  207. package/src/schema/__tests__/logbook-health.schema.test.ts +117 -0
  208. package/src/schema/__tests__/meter.schema.test.ts +28 -0
  209. package/src/schema/__tests__/observability.schema.test.ts +15 -0
  210. package/src/schema/__tests__/operator-context-report.schema.test.ts +77 -0
  211. package/src/schema/__tests__/operator.schema.test.ts +11 -3
  212. package/src/schema/__tests__/plugin-market.schema.test.ts +165 -2
  213. package/src/schema/__tests__/qso.schema.test.ts +91 -0
  214. package/src/schema/__tests__/radio-power-support.test.ts +17 -0
  215. package/src/schema/__tests__/radio-profile.schema.test.ts +44 -0
  216. package/src/schema/__tests__/radio.schema.test.ts +108 -1
  217. package/src/schema/__tests__/realtime-settings.schema.test.ts +2 -1
  218. package/src/schema/__tests__/remote-access.schema.test.ts +22 -0
  219. package/src/schema/__tests__/spectrum.schema.test.ts +119 -0
  220. package/src/schema/__tests__/strategy-transmission.schema.test.ts +106 -0
  221. package/src/schema/android-operator-audio.schema.ts +49 -0
  222. package/src/schema/api-response.schema.ts +11 -0
  223. package/src/schema/audio-sidecar.schema.ts +35 -0
  224. package/src/schema/audio.schema.ts +115 -2
  225. package/src/schema/auth.schema.ts +79 -1
  226. package/src/schema/cw-keyer.schema.ts +5 -0
  227. package/src/schema/diagnostics.schema.ts +55 -0
  228. package/src/schema/ft8.schema.ts +8 -0
  229. package/src/schema/image-radio.schema.ts +342 -0
  230. package/src/schema/logbook.schema.ts +205 -1
  231. package/src/schema/mode.schema.ts +18 -6
  232. package/src/schema/observability.schema.ts +26 -0
  233. package/src/schema/operator.schema.ts +11 -20
  234. package/src/schema/plugin.schema.ts +186 -15
  235. package/src/schema/pskreporter.schema.ts +1 -3
  236. package/src/schema/qso.schema.ts +78 -0
  237. package/src/schema/radio-capability.schema.ts +8 -15
  238. package/src/schema/radio-power-support.ts +3 -0
  239. package/src/schema/radio-power.schema.ts +15 -8
  240. package/src/schema/radio-profile.schema.ts +7 -0
  241. package/src/schema/radio.schema.ts +90 -7
  242. package/src/schema/realtime.schema.ts +9 -1
  243. package/src/schema/slot-info.schema.ts +18 -11
  244. package/src/schema/spectrum-config.schema.ts +156 -0
  245. package/src/schema/spectrum.schema.ts +89 -0
  246. package/src/schema/station-info.schema.ts +5 -0
  247. package/src/schema/system.schema.ts +29 -0
  248. package/src/schema/transmission.schema.ts +2 -7
  249. package/src/schema/tx-audio-input.schema.ts +22 -0
  250. package/src/schema/voice.schema.ts +1 -1
  251. package/src/schema/websocket.schema.ts +601 -13
  252. package/test/image-radio-schema.test.ts +107 -0
  253. package/test/plugin-slot-schema.test.ts +31 -0
  254. package/test/radio-capability-schema.test.ts +44 -0
  255. package/CLAUDE.md +0 -41
@@ -0,0 +1,107 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import {
4
+ EngineModeSchema,
5
+ ImageRadioCapabilitySchema,
6
+ ImageHistoryRecordSchema,
7
+ ImageComposerBackgroundSchema,
8
+ ImagePaperBoundarySchema,
9
+ ImageFaxCalibrationSchema,
10
+ FaxCalibrationSetCommandSchema,
11
+ ImageReceiveProfileSchema,
12
+ ImageTemplateSchema,
13
+ SstvTxStartCommandSchema,
14
+ } from '../src/index.js';
15
+
16
+ describe('image radio contracts', () => {
17
+ it('adds image as an engine mode', () => {
18
+ expect(EngineModeSchema.parse('image')).toBe('image');
19
+ });
20
+
21
+ it('keeps fax receive-only at the schema boundary', () => {
22
+ const base = { available: true, sstv: { rx: true, tx: true }, fax: { rx: true, tx: false } };
23
+ expect(ImageRadioCapabilitySchema.parse(base).fax.tx).toBe(false);
24
+ expect(() => ImageRadioCapabilitySchema.parse({ ...base, fax: { rx: true, tx: true } })).toThrow();
25
+ });
26
+
27
+ it('separates automatic SSTV from fixed immediate receive profiles', () => {
28
+ expect(ImageReceiveProfileSchema.parse({ family: 'sstv', strategy: 'auto' })).toEqual({ family: 'sstv', strategy: 'auto' });
29
+ expect(ImageReceiveProfileSchema.parse({ family: 'sstv', strategy: 'manual', mode: 'robot36' }).mode).toBe('robot36');
30
+ expect(() => ImageReceiveProfileSchema.parse({ family: 'sstv', strategy: 'manual' })).toThrow();
31
+ expect(ImageReceiveProfileSchema.parse({ family: 'fax', strategy: 'auto' })).toEqual({ family: 'fax', strategy: 'auto' });
32
+ expect(ImageReceiveProfileSchema.parse({
33
+ family: 'fax', strategy: 'manual', ioc: 'ioc576', lpm: 120,
34
+ modulation: 'am', centerHz: 1900, deviationHz: 400,
35
+ }).family).toBe('fax');
36
+ });
37
+
38
+ it('bounds templates and validates idempotent TX requests', () => {
39
+ const layer = { id: 'line', text: '{MYCALL}', x: 0, y: 0, width: 1, height: 0.2, fontSize: 0.1, color: '#ffffff', align: 'center' };
40
+ const parsedTemplate = ImageTemplateSchema.parse({ id: 'legacy', name: 'Legacy', layers: [layer], createdAt: 1, updatedAt: 1 });
41
+ expect(parsedTemplate.layers[0]).toMatchObject({ align: 'center', rotation: 0, strokeWidth: 0.12 });
42
+ expect(ImageTemplateSchema.parse({ ...parsedTemplate, layers: [{ ...parsedTemplate.layers[0], rotation: 45 }] }).layers[0].rotation).toBe(45);
43
+ expect(ImageTemplateSchema.parse({ ...parsedTemplate, layers: [{ ...parsedTemplate.layers[0], x: -0.5, width: 2 }] }).layers[0]).toMatchObject({ x: -0.5, width: 2 });
44
+ expect(() => ImageTemplateSchema.parse({ ...parsedTemplate, layers: [{ ...parsedTemplate.layers[0], rotation: 181 }] })).toThrow();
45
+ expect(() => ImageTemplateSchema.parse({ ...parsedTemplate, layers: [{ ...parsedTemplate.layers[0], strokeWidth: 0.51 }] })).toThrow();
46
+ expect(() => ImageTemplateSchema.parse({ ...parsedTemplate, layers: [{ ...parsedTemplate.layers[0], x: -2.1 }] })).toThrow();
47
+ expect(() => ImageTemplateSchema.parse({ id: 't', name: 'T', layers: Array.from({ length: 17 }, (_, index) => ({ ...layer, id: String(index) })), createdAt: 1, updatedAt: 1 })).toThrow();
48
+ expect(SstvTxStartCommandSchema.parse({ requestId: 'request-1', operatorId: 'op', artifactId: 'a', mode: 'robot36', expectedFrequency: 14_230_000 })).toMatchObject({
49
+ requestId: 'request-1', envelope: { enhancedPreamble: true, stationIdMode: 'fsk' },
50
+ });
51
+ expect(SstvTxStartCommandSchema.parse({ requestId: 'request-2', operatorId: 'op', artifactId: 'a', mode: 'robot36', expectedFrequency: 14_230_000, interruptActiveCapture: true }).interruptActiveCapture).toBe(true);
52
+ expect(SstvTxStartCommandSchema.parse({
53
+ requestId: 'request-3', operatorId: 'op', artifactId: 'a', mode: 'robot36', expectedFrequency: 14_230_000,
54
+ envelope: { enhancedPreamble: true, stationIdMode: 'cw' },
55
+ }).envelope).toEqual({ enhancedPreamble: true, stationIdMode: 'cw' });
56
+ });
57
+
58
+ it('separates received captures from real transmit history', () => {
59
+ expect(ImageHistoryRecordSchema.parse({
60
+ id: 'rx', artifactId: 'image', family: 'sstv', direction: 'rx', occurredAt: 1,
61
+ saveReason: 'manual', complete: false, truncated: false,
62
+ }).direction).toBe('rx');
63
+ expect(ImageHistoryRecordSchema.parse({
64
+ id: 'tx', artifactId: 'image', family: 'sstv', direction: 'tx', operatorId: 'op',
65
+ sessionId: 'session', occurredAt: 1, startedAt: 1, outcome: 'completed',
66
+ }).direction).toBe('tx');
67
+ expect(() => ImageHistoryRecordSchema.parse({
68
+ id: 'tx', artifactId: 'image', family: 'sstv', direction: 'tx',
69
+ sessionId: 'session', occurredAt: 1, startedAt: 1, outcome: 'completed',
70
+ })).toThrow();
71
+ });
72
+
73
+ it('distinguishes local transmit paper segments from received content', () => {
74
+ const boundary = ImagePaperBoundarySchema.parse({
75
+ boundaryId: 'tx:start', lineIndex: 12, kind: 'localTxStart', trusted: false,
76
+ codecMode: 'robot36', width: 320, pixelFormat: 'rgb8', timestamp: 1,
77
+ source: 'localTx', txSessionId: 'tx-1',
78
+ });
79
+ expect(boundary).toMatchObject({ kind: 'localTxStart', source: 'localTx', txSessionId: 'tx-1' });
80
+ });
81
+
82
+ it('validates per-operator composer background metadata', () => {
83
+ expect(ImageComposerBackgroundSchema.parse({
84
+ operatorId: 'op', width: 1024, height: 512, updatedAt: 1, imageUrl: '/background.png',
85
+ }).operatorId).toBe('op');
86
+ expect(() => ImageComposerBackgroundSchema.parse({
87
+ operatorId: '', width: 1024, height: 512, updatedAt: 1, imageUrl: '/background.png',
88
+ })).toThrow();
89
+ });
90
+
91
+ it('bounds FAX calibration commands and control points', () => {
92
+ const calibration = ImageFaxCalibrationSchema.parse({
93
+ boundaryId: 'fax:1', revision: 2, autoEnabled: true,
94
+ autoPoints: [{ revision: 1, referenceLine: 10, phasePixels: 2, clockPpm: 25, confidence: 0.8, source: 'phasing', status: 'locked' }],
95
+ manualPhasePixels: 0, manualClockPpm: 0, updatedAt: 1,
96
+ });
97
+ expect(calibration.autoPoints[0].clockPpm).toBe(25);
98
+ expect(FaxCalibrationSetCommandSchema.parse({
99
+ requestId: 'r', operatorId: 'op', sessionId: 's', boundaryId: 'fax:1', expectedRevision: 2,
100
+ autoEnabled: true, phasePixels: 10, clockPpm: 5000,
101
+ }).clockPpm).toBe(5000);
102
+ expect(() => FaxCalibrationSetCommandSchema.parse({
103
+ requestId: 'r', operatorId: 'op', sessionId: 's', boundaryId: 'fax:1', expectedRevision: 2,
104
+ autoEnabled: true, phasePixels: 10, clockPpm: 5001,
105
+ })).toThrow();
106
+ });
107
+ });
@@ -5,6 +5,7 @@ describe('PluginPanelSlotSchema', () => {
5
5
  it('accepts every supported plugin panel slot', () => {
6
6
  expect(PluginPanelSlotSchema.parse('operator')).toBe('operator');
7
7
  expect(PluginPanelSlotSchema.parse('automation')).toBe('automation');
8
+ expect(PluginPanelSlotSchema.parse('operator-action')).toBe('operator-action');
8
9
  expect(PluginPanelSlotSchema.parse('main-right')).toBe('main-right');
9
10
  expect(PluginPanelSlotSchema.parse('voice-left-top')).toBe('voice-left-top');
10
11
  expect(PluginPanelSlotSchema.parse('voice-right-top')).toBe('voice-right-top');
@@ -13,6 +14,27 @@ describe('PluginPanelSlotSchema', () => {
13
14
  expect(PluginPanelSlotSchema.parse('radio-control-toolbar')).toBe('radio-control-toolbar');
14
15
  });
15
16
 
17
+ it('requires operator action entries to open an iframe page', () => {
18
+ expect(PluginPanelDescriptorSchema.parse({
19
+ id: 'contest-log',
20
+ title: 'Contest log',
21
+ component: 'iframe',
22
+ pageId: 'contest-log',
23
+ slot: 'operator-action',
24
+ openMode: 'page',
25
+ icon: 'file-lines',
26
+ })).toMatchObject({ slot: 'operator-action', openMode: 'page' });
27
+
28
+ expect(() => PluginPanelDescriptorSchema.parse({
29
+ id: 'embedded-action',
30
+ title: 'Embedded',
31
+ component: 'iframe',
32
+ pageId: 'embedded',
33
+ slot: 'operator-action',
34
+ openMode: 'modal',
35
+ })).toThrow('operator-action panels must use page openMode');
36
+ });
37
+
16
38
  it('validates iframe panels declared in the new host slots', () => {
17
39
  expect(() => PluginPanelDescriptorSchema.parse({
18
40
  id: 'main-pane',
@@ -56,6 +78,15 @@ describe('PluginPanelSlotSchema', () => {
56
78
  uiSize: 'lg',
57
79
  });
58
80
 
81
+ expect(PluginPanelDescriptorSchema.parse({
82
+ id: 'standalone-log',
83
+ title: 'Log',
84
+ component: 'iframe',
85
+ pageId: 'log',
86
+ slot: 'radio-control-toolbar',
87
+ openMode: 'page',
88
+ })).toMatchObject({ openMode: 'page' });
89
+
59
90
  expect(() => PluginPanelDescriptorSchema.parse({
60
91
  id: 'bad-toolbar',
61
92
  title: 'Bad',
@@ -0,0 +1,44 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ CAPABILITY_IDS,
4
+ CapabilityDescriptorSchema,
5
+ CapabilityStateSchema,
6
+ type CapabilityId,
7
+ } from '../src/schema/radio-capability.schema';
8
+
9
+ describe('CAPABILITY_IDS', () => {
10
+ it('includes radio_mode for dynamic voice radio mode discovery', () => {
11
+ const id: CapabilityId = 'radio_mode';
12
+
13
+ expect(CAPABILITY_IDS).toContain(id);
14
+ });
15
+
16
+ it('accepts radio_mode as an enum capability descriptor and state', () => {
17
+ expect(CapabilityDescriptorSchema.parse({
18
+ id: 'radio_mode',
19
+ category: 'operation',
20
+ valueType: 'enum',
21
+ options: [{ value: 'USB' }, { value: 'WFM' }],
22
+ readable: true,
23
+ writable: false,
24
+ updateMode: 'polling',
25
+ labelI18nKey: 'radio:capability.radio_mode.label',
26
+ hasSurfaceControl: false,
27
+ })).toMatchObject({
28
+ id: 'radio_mode',
29
+ valueType: 'enum',
30
+ options: [{ value: 'USB' }, { value: 'WFM' }],
31
+ });
32
+
33
+ expect(CapabilityStateSchema.parse({
34
+ id: 'radio_mode',
35
+ supported: true,
36
+ value: 'WFM',
37
+ updatedAt: 1,
38
+ })).toMatchObject({
39
+ id: 'radio_mode',
40
+ supported: true,
41
+ value: 'WFM',
42
+ });
43
+ });
44
+ });
package/CLAUDE.md DELETED
@@ -1,41 +0,0 @@
1
- # CLAUDE.md - Contracts
2
-
3
- Zod Schema 和 TypeScript 类型定义包,为整个 TX-5DR 系统提供数据契约。
4
-
5
- ## 核心文件
6
- - **websocket.schema.ts**: WebSocket 消息类型
7
- - **radio/audio.schema.ts**: 电台和音频控制
8
- - **ft8/operator.schema.ts**: FT8 消息和操作员
9
- - **logbook/qso.schema.ts**: 日志本和联络记录
10
- - **其他**: slot-info, mode, cycle, hello
11
-
12
- ## 开发规范
13
-
14
- ### 新增 Schema
15
- 1. `src/schema/` 创建 `.schema.ts` 文件
16
- 2. 定义 Zod Schema:
17
- ```typescript
18
- export const DataSchema = z.object({
19
- id: z.string(),
20
- value: z.number().optional(),
21
- });
22
- export type Data = z.infer<typeof DataSchema>;
23
- ```
24
- 3. `src/index.ts` 导出
25
- 4. `yarn build` 生成类型
26
-
27
- ### WebSocket 消息
28
- ```typescript
29
- export const WSMessageType = {
30
- COMMAND_NAME: 'command_name', // 客户端→服务端
31
- EVENT_NAME: 'event_name', // 服务端→客户端
32
- } as const;
33
- ```
34
-
35
- ### 设计原则
36
- - 严格验证,新字段用 `.optional()` 保证兼容性
37
- - 复用现有 Schema,避免重复定义
38
- - 使用语义化的命名
39
-
40
- ## 命令
41
- `yarn build` - 构建类型定义