@tx5dr/contracts 1.7.2 → 1.7.3

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 (57) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/schema/__tests__/audio.schema.test.js +4 -0
  6. package/dist/schema/__tests__/audio.schema.test.js.map +1 -1
  7. package/dist/schema/__tests__/operator.schema.test.d.ts +2 -0
  8. package/dist/schema/__tests__/operator.schema.test.d.ts.map +1 -0
  9. package/dist/schema/__tests__/operator.schema.test.js +23 -0
  10. package/dist/schema/__tests__/operator.schema.test.js.map +1 -0
  11. package/dist/schema/__tests__/process-monitor.schema.test.d.ts +2 -0
  12. package/dist/schema/__tests__/process-monitor.schema.test.d.ts.map +1 -0
  13. package/dist/schema/__tests__/process-monitor.schema.test.js +139 -0
  14. package/dist/schema/__tests__/process-monitor.schema.test.js.map +1 -0
  15. package/dist/schema/api-response.schema.d.ts +2 -2
  16. package/dist/schema/audio.schema.d.ts +369 -0
  17. package/dist/schema/audio.schema.d.ts.map +1 -1
  18. package/dist/schema/audio.schema.js +3 -0
  19. package/dist/schema/audio.schema.js.map +1 -1
  20. package/dist/schema/logbook.schema.d.ts +2 -2
  21. package/dist/schema/operator.schema.d.ts +10 -10
  22. package/dist/schema/operator.schema.d.ts.map +1 -1
  23. package/dist/schema/operator.schema.js +6 -1
  24. package/dist/schema/operator.schema.js.map +1 -1
  25. package/dist/schema/plugin.schema.d.ts +16 -16
  26. package/dist/schema/process-monitor.schema.d.ts +1373 -0
  27. package/dist/schema/process-monitor.schema.d.ts.map +1 -1
  28. package/dist/schema/process-monitor.schema.js +43 -0
  29. package/dist/schema/process-monitor.schema.js.map +1 -1
  30. package/dist/schema/radio-capability.schema.d.ts +6 -6
  31. package/dist/schema/radio.schema.d.ts +6 -6
  32. package/dist/schema/slot-info.schema.d.ts +136 -0
  33. package/dist/schema/slot-info.schema.d.ts.map +1 -1
  34. package/dist/schema/slot-info.schema.js +27 -2
  35. package/dist/schema/slot-info.schema.js.map +1 -1
  36. package/dist/schema/spectrum.schema.d.ts +24 -24
  37. package/dist/schema/voice-keyer.schema.d.ts +2 -2
  38. package/dist/schema/websocket.schema.d.ts +363 -139
  39. package/dist/schema/websocket.schema.d.ts.map +1 -1
  40. package/dist/schema/websocket.schema.js +4 -2
  41. package/dist/schema/websocket.schema.js.map +1 -1
  42. package/dist/utils/callsign.d.ts +2 -0
  43. package/dist/utils/callsign.d.ts.map +1 -0
  44. package/dist/utils/callsign.js +8 -0
  45. package/dist/utils/callsign.js.map +1 -0
  46. package/package.json +1 -1
  47. package/src/index.ts +1 -0
  48. package/src/schema/__tests__/audio.schema.test.ts +4 -0
  49. package/src/schema/__tests__/operator.schema.test.ts +29 -0
  50. package/src/schema/__tests__/process-monitor.schema.test.ts +146 -0
  51. package/src/schema/audio.schema.ts +3 -0
  52. package/src/schema/operator.schema.ts +7 -1
  53. package/src/schema/process-monitor.schema.ts +51 -0
  54. package/src/schema/slot-info.schema.ts +33 -2
  55. package/src/schema/websocket.schema.ts +16 -2
  56. package/src/utils/callsign.ts +9 -0
  57. package/test/decode-request-schema.test.ts +48 -0
@@ -28,6 +28,52 @@ export const EventLoopDelaySchema = z.object({
28
28
  p99: z.number(),
29
29
  });
30
30
 
31
+ export const DecodeWorkerCurrentJobSchema = z.object({
32
+ jobId: z.number(),
33
+ slotId: z.string(),
34
+ windowIdx: z.number(),
35
+ mode: z.string(),
36
+ startedAt: z.number(),
37
+ elapsedMs: z.number(),
38
+ requestAudioDurationMs: z.number().optional(),
39
+ });
40
+
41
+ export const DecodeWorkerTelemetryWorkerSchema = z.object({
42
+ workerId: z.number(),
43
+ pid: z.number().optional(),
44
+ ready: z.boolean(),
45
+ busy: z.boolean(),
46
+ nativeThreads: z.number().int().positive(),
47
+ uptimeSeconds: z.number(),
48
+ memory: ProcessMemorySchema,
49
+ cpu: ProcessCpuSchema,
50
+ currentJob: DecodeWorkerCurrentJobSchema.optional(),
51
+ lastSeenAt: z.number(),
52
+ });
53
+
54
+ export const DecodeWorkerTelemetrySummarySchema = z.object({
55
+ status: z.enum(['stopped', 'starting', 'ready', 'degraded', 'unavailable']).optional(),
56
+ workerCount: z.number().int().nonnegative(),
57
+ desiredWorkers: z.number().int().nonnegative().optional(),
58
+ readyCount: z.number().int().nonnegative(),
59
+ busyCount: z.number().int().nonnegative(),
60
+ totalRss: z.number().nonnegative(),
61
+ totalCpu: z.number().nonnegative(),
62
+ nativeThreadsPerWorker: z.number().int().positive(),
63
+ pendingJobs: z.number().int().nonnegative(),
64
+ activeJobs: z.number().int().nonnegative(),
65
+ lastError: z.string().optional(),
66
+ lastFailureAt: z.number().optional(),
67
+ restartAttempts: z.number().int().nonnegative().optional(),
68
+ workerEntry: z.string().optional(),
69
+ workerMode: z.enum(['development', 'production']).optional(),
70
+ });
71
+
72
+ export const DecodeWorkerTelemetrySnapshotSchema = z.object({
73
+ summary: DecodeWorkerTelemetrySummarySchema,
74
+ workers: z.array(DecodeWorkerTelemetryWorkerSchema),
75
+ });
76
+
31
77
  export const ProcessSnapshotSchema = z.object({
32
78
  timestamp: z.number(),
33
79
  uptimeSeconds: z.number(),
@@ -35,6 +81,7 @@ export const ProcessSnapshotSchema = z.object({
35
81
  cpu: ProcessCpuSchema,
36
82
  hostCpu: HostCpuSchema.optional(),
37
83
  eventLoop: EventLoopDelaySchema,
84
+ decodeWorkers: DecodeWorkerTelemetrySnapshotSchema.optional(),
38
85
  });
39
86
 
40
87
  export const ProcessSnapshotHistorySchema = z.object({
@@ -47,5 +94,9 @@ export type ProcessMemory = z.infer<typeof ProcessMemorySchema>;
47
94
  export type ProcessCpu = z.infer<typeof ProcessCpuSchema>;
48
95
  export type HostCpu = z.infer<typeof HostCpuSchema>;
49
96
  export type EventLoopDelay = z.infer<typeof EventLoopDelaySchema>;
97
+ export type DecodeWorkerCurrentJob = z.infer<typeof DecodeWorkerCurrentJobSchema>;
98
+ export type DecodeWorkerTelemetryWorker = z.infer<typeof DecodeWorkerTelemetryWorkerSchema>;
99
+ export type DecodeWorkerTelemetrySummary = z.infer<typeof DecodeWorkerTelemetrySummarySchema>;
100
+ export type DecodeWorkerTelemetrySnapshot = z.infer<typeof DecodeWorkerTelemetrySnapshotSchema>;
50
101
  export type ProcessSnapshot = z.infer<typeof ProcessSnapshotSchema>;
51
102
  export type ProcessSnapshotHistory = z.infer<typeof ProcessSnapshotHistorySchema>;
@@ -1,6 +1,20 @@
1
1
  import { z } from 'zod';
2
2
  import { DxccStatusSchema, SubdivisionConfidenceSchema } from './qso.schema.js';
3
3
 
4
+ /**
5
+ * Frequency context captured when a slot pack is created.
6
+ * Fields are optional so older persisted slot packs remain valid.
7
+ */
8
+ export const SlotPackFrequencyContextSchema = z.object({
9
+ frequency: z.number().optional(),
10
+ mode: z.string().optional(),
11
+ band: z.string().optional(),
12
+ radioMode: z.string().optional(),
13
+ description: z.string().optional(),
14
+ });
15
+
16
+ export type SlotPackFrequencyContext = z.infer<typeof SlotPackFrequencyContextSchema>;
17
+
4
18
  /**
5
19
  * 时隙周期(偶数奇数)
6
20
  */
@@ -83,6 +97,19 @@ export const FrameMessageSchema = z.object({
83
97
 
84
98
  export type FrameMessage = z.infer<typeof FrameMessageSchema>;
85
99
 
100
+ export const DecodeApContextSchema = z.object({
101
+ operatorId: z.string(),
102
+ myCall: z.string(),
103
+ myGrid: z.string().optional(),
104
+ dxCall: z.string(),
105
+ dxGrid: z.string().optional(),
106
+ frequencyHz: z.number(),
107
+ qsoProgress: z.number(),
108
+ currentSlot: z.string(),
109
+ });
110
+
111
+ export type DecodeApContext = z.infer<typeof DecodeApContextSchema>;
112
+
86
113
  /**
87
114
  * 解码请求,用于SlotScheduler内部
88
115
  */
@@ -100,7 +127,9 @@ export const DecodeRequestSchema = z.object({
100
127
  /** 请求时间戳 */
101
128
  timestamp: z.number().default(() => Date.now()),
102
129
  /** 窗口时间偏移(毫秒) */
103
- windowOffsetMs: z.number().default(0)
130
+ windowOffsetMs: z.number().default(0),
131
+ /** 可选的单操作员 AP decode 上下文 */
132
+ apContext: DecodeApContextSchema.optional()
104
133
  });
105
134
 
106
135
  export type DecodeRequest = z.infer<typeof DecodeRequestSchema>;
@@ -160,7 +189,9 @@ export const SlotPackSchema = z.object({
160
189
  timestamp: z.number(),
161
190
  frameCount: z.number(),
162
191
  processingTimeMs: z.number()
163
- })).default([])
192
+ })).default([]),
193
+ /** Dial/band context active when this slot was created. */
194
+ frequencyContext: SlotPackFrequencyContextSchema.optional()
164
195
  });
165
196
 
166
197
  export type SlotPack = z.infer<typeof SlotPackSchema>;
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { FT8DecodeSchema } from './ft8.schema.js';
3
- import { SlotPackSchema, SlotInfoSchema } from './slot-info.schema.js';
3
+ import { SlotPackSchema, SlotInfoSchema, SlotPackFrequencyContextSchema } from './slot-info.schema.js';
4
4
  import {
5
5
  PluginDataPayloadSchema,
6
6
  PluginLogEntrySchema,
@@ -779,7 +779,9 @@ export const WSTransmissionLogMessageSchema = WSBaseMessageSchema.extend({
779
779
  time: z.string(),
780
780
  message: z.string(),
781
781
  frequency: z.number(),
782
- slotStartMs: z.number()
782
+ slotStartMs: z.number(),
783
+ replaceExisting: z.boolean().optional(),
784
+ frequencyContext: SlotPackFrequencyContextSchema.optional(),
783
785
  }),
784
786
  });
785
787
 
@@ -1443,6 +1445,7 @@ export interface DigitalRadioEngineEvents {
1443
1445
  frequency: number;
1444
1446
  slotStartMs: number;
1445
1447
  replaceExisting?: boolean;
1448
+ frequencyContext?: z.infer<typeof SlotPackFrequencyContextSchema>;
1446
1449
  }) => void;
1447
1450
 
1448
1451
  // 操作员事件
@@ -1544,6 +1547,17 @@ export interface DigitalRadioEngineEvents {
1544
1547
  }) => void;
1545
1548
  pluginPanelMeta: (data: import('./plugin.schema.js').PluginPanelMetaPayload) => void;
1546
1549
  pluginPanelContributionsChanged: (data: import('./plugin.schema.js').PluginUIPanelContributionGroup) => void;
1550
+ pluginRemoteReplyToDecode: (data: { operatorId: string; callsign: string; modifiers?: number }) => void;
1551
+ pluginRemoteClearDecodes: (data: { operatorId: string; window?: number }) => void;
1552
+ pluginRemoteFreeText: (data: { operatorId: string; text?: string; send: boolean }) => void;
1553
+ pluginRemoteLocation: (data: { operatorId: string; location: string }) => void;
1554
+ pluginRemoteHighlightCallsign: (data: {
1555
+ operatorId: string;
1556
+ callsign: string;
1557
+ background?: string | null;
1558
+ foreground?: string | null;
1559
+ lastOnly?: boolean;
1560
+ }) => void;
1547
1561
 
1548
1562
  // 内部子系统间通信事件(不通过 WebSocket 广播,用于 engine emitter 内部编排)
1549
1563
  encodeStart: (slotInfo: z.infer<typeof SlotInfoSchema>) => void;
@@ -0,0 +1,9 @@
1
+ const CALLSIGN_WHITESPACE_REGEX = /\s+/g;
2
+
3
+ export function sanitizeCallsignInput(callsign?: string | null): string {
4
+ if (!callsign) {
5
+ return '';
6
+ }
7
+
8
+ return callsign.toUpperCase().replace(CALLSIGN_WHITESPACE_REGEX, '');
9
+ }
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { DecodeRequestSchema } from '../src/schema/slot-info.schema.js';
3
+
4
+ function makePcm(): ArrayBuffer {
5
+ return new Float32Array(16).buffer;
6
+ }
7
+
8
+ describe('DecodeRequestSchema', () => {
9
+ it('accepts legacy decode requests without AP context', () => {
10
+ const parsed = DecodeRequestSchema.parse({
11
+ slotId: 'FT8-1-15000',
12
+ mode: 'FT8',
13
+ windowIdx: 0,
14
+ pcm: makePcm(),
15
+ sampleRate: 12000,
16
+ windowOffsetMs: 0,
17
+ });
18
+
19
+ expect(parsed.apContext).toBeUndefined();
20
+ });
21
+
22
+ it('accepts a single AP decode context', () => {
23
+ const parsed = DecodeRequestSchema.parse({
24
+ slotId: 'FT8-1-15000',
25
+ mode: 'FT8',
26
+ windowIdx: 1,
27
+ pcm: makePcm(),
28
+ sampleRate: 12000,
29
+ windowOffsetMs: 0,
30
+ apContext: {
31
+ operatorId: 'op1',
32
+ myCall: 'BG4IAJ',
33
+ myGrid: 'OM96',
34
+ dxCall: 'JA1AAA',
35
+ dxGrid: 'PM95',
36
+ frequencyHz: 1500,
37
+ qsoProgress: 4,
38
+ currentSlot: 'TX4',
39
+ },
40
+ });
41
+
42
+ expect(parsed.apContext).toEqual(expect.objectContaining({
43
+ operatorId: 'op1',
44
+ qsoProgress: 4,
45
+ currentSlot: 'TX4',
46
+ }));
47
+ });
48
+ });