@tx5dr/plugin-api 1.7.11 → 2.0.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.
- package/README.md +274 -17
- package/dist/__tests__/adif.test.js +40 -0
- package/dist/__tests__/adif.test.js.map +1 -1
- package/dist/__tests__/capability-context.test.d.ts +2 -0
- package/dist/__tests__/capability-context.test.d.ts.map +1 -0
- package/dist/__tests__/capability-context.test.js +99 -0
- package/dist/__tests__/capability-context.test.js.map +1 -0
- package/dist/__tests__/testing-utils.test.js +235 -14
- package/dist/__tests__/testing-utils.test.js.map +1 -1
- package/dist/capabilities.d.ts +37 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +48 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/context.d.ts +95 -56
- package/dist/context.d.ts.map +1 -1
- package/dist/definition.d.ts +73 -20
- package/dist/definition.d.ts.map +1 -1
- package/dist/definition.js +8 -1
- package/dist/definition.js.map +1 -1
- package/dist/helpers.d.ts +340 -129
- package/dist/helpers.d.ts.map +1 -1
- package/dist/hooks.d.ts +54 -34
- package/dist/hooks.d.ts.map +1 -1
- package/dist/host-dependencies.d.ts +101 -0
- package/dist/host-dependencies.d.ts.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +164 -9
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +9 -1
- package/dist/runtime.js.map +1 -1
- package/dist/settings.d.ts +31 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/sync.d.ts +98 -6
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +6 -0
- package/dist/sync.js.map +1 -1
- package/dist/testing/index.d.ts +70 -12
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +307 -104
- package/dist/testing/index.js.map +1 -1
- package/dist/utils/adif.d.ts.map +1 -1
- package/dist/utils/adif.js +6 -2
- package/dist/utils/adif.js.map +1 -1
- package/dist/utils/qso-text-fields.d.ts +7 -1
- package/dist/utils/qso-text-fields.d.ts.map +1 -1
- package/dist/utils/qso-text-fields.js +74 -6
- package/dist/utils/qso-text-fields.js.map +1 -1
- package/package.json +4 -4
package/dist/helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ParsedFT8Message, SlotInfo, SlotPack,
|
|
1
|
+
import type { ParsedFT8Message, SlotInfo, SlotPack, FrameMessage, ModeDescriptor, EngineMode, PermissionGrant, PluginPanelDescriptor, CapabilityList, CapabilityState, RadioPowerResponse, RadioPowerStateEvent, RadioPowerSupportInfo, RadioPowerTarget } from '@tx5dr/contracts';
|
|
2
2
|
import type { StrategyRuntimeSnapshot } from './runtime.js';
|
|
3
3
|
/**
|
|
4
4
|
* Simple persistent key-value store exposed to plugins.
|
|
@@ -10,11 +10,17 @@ export interface KVStore {
|
|
|
10
10
|
/**
|
|
11
11
|
* Reads a stored value.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* Stored values are returned by value, so mutating the result does not update
|
|
14
|
+
* persistence until {@link set} is called. When the key is missing, the
|
|
15
|
+
* caller-owned `defaultValue` is returned unchanged.
|
|
14
16
|
*/
|
|
15
17
|
get<T = unknown>(key: string, defaultValue?: T): T;
|
|
16
18
|
/**
|
|
17
|
-
* Persists a
|
|
19
|
+
* Persists a JSON-compatible snapshot under the given key.
|
|
20
|
+
*
|
|
21
|
+
* `undefined` follows JSON object semantics and removes the key. Cycles,
|
|
22
|
+
* BigInt values, functions and Host capabilities are rejected with
|
|
23
|
+
* `PLUGIN_DATA_NOT_SERIALIZABLE`.
|
|
18
24
|
*/
|
|
19
25
|
set(key: string, value: unknown): void;
|
|
20
26
|
/**
|
|
@@ -22,7 +28,7 @@ export interface KVStore {
|
|
|
22
28
|
*/
|
|
23
29
|
delete(key: string): void;
|
|
24
30
|
/**
|
|
25
|
-
* Returns
|
|
31
|
+
* Returns an independent snapshot of all stored entries in this scope.
|
|
26
32
|
*/
|
|
27
33
|
getAll(): Record<string, unknown>;
|
|
28
34
|
/**
|
|
@@ -70,42 +76,159 @@ export interface PluginTimers {
|
|
|
70
76
|
* Remote UDP endpoint metadata for datagrams received by plugin-owned sockets.
|
|
71
77
|
*/
|
|
72
78
|
export interface PluginUdpRemoteInfo {
|
|
79
|
+
/** Source IP address reported by the UDP socket. */
|
|
73
80
|
address: string;
|
|
81
|
+
/** Source UDP port. */
|
|
74
82
|
port: number;
|
|
83
|
+
/** Address family reported by Node.js, typically `IPv4` or `IPv6`. */
|
|
75
84
|
family: string;
|
|
85
|
+
/** Datagram size in bytes. */
|
|
76
86
|
size: number;
|
|
77
87
|
}
|
|
88
|
+
/** Local endpoint used when binding a plugin-owned UDP socket. */
|
|
78
89
|
export interface PluginUdpBindOptions {
|
|
90
|
+
/** Local interface/address. Omit to use the Host default. */
|
|
79
91
|
host?: string;
|
|
92
|
+
/** Local port. Omit or use `0` to let the operating system choose one. */
|
|
80
93
|
port?: number;
|
|
81
94
|
}
|
|
95
|
+
/** Options applied when the Host creates a plugin-owned UDP socket. */
|
|
82
96
|
export interface PluginUdpSocketOptions {
|
|
97
|
+
/** IP family. Defaults to `udp4`. */
|
|
83
98
|
type?: 'udp4' | 'udp6';
|
|
99
|
+
/** Whether multiple sockets may reuse the local address. */
|
|
84
100
|
reuseAddr?: boolean;
|
|
101
|
+
/** Whether the socket may send IPv4 broadcast datagrams. */
|
|
85
102
|
broadcast?: boolean;
|
|
103
|
+
/** Multicast time-to-live applied to outbound multicast packets. */
|
|
86
104
|
multicastTtl?: number;
|
|
87
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Host-owned UDP socket capability.
|
|
108
|
+
*
|
|
109
|
+
* The handle may be stored by the plugin, but its methods are invocation
|
|
110
|
+
* guarded. Close it during unload when possible; Host cleanup also closes all
|
|
111
|
+
* sockets owned by the plugin instance.
|
|
112
|
+
*/
|
|
88
113
|
export interface PluginUdpSocket {
|
|
114
|
+
/** Binds the socket and resolves when it is ready to receive datagrams. */
|
|
89
115
|
bind(options?: PluginUdpBindOptions): Promise<void>;
|
|
116
|
+
/** Sends one datagram to the exact remote host and port. */
|
|
90
117
|
send(data: Uint8Array | string, port: number, host: string): Promise<void>;
|
|
118
|
+
/** Registers the callback used for received datagrams. */
|
|
91
119
|
onMessage(handler: (data: Uint8Array, remote: PluginUdpRemoteInfo) => void | Promise<void>): void;
|
|
120
|
+
/** Registers the callback used for socket-level errors. */
|
|
92
121
|
onError(handler: (error: Error) => void): void;
|
|
122
|
+
/** Closes the socket. Calling it again is safe. */
|
|
93
123
|
close(): Promise<void>;
|
|
94
124
|
}
|
|
125
|
+
/** Factory and bulk-cleanup surface for UDP sockets owned by one plugin instance. */
|
|
95
126
|
export interface PluginUdpControl {
|
|
127
|
+
/** Creates an unbound socket with the requested options. */
|
|
96
128
|
createSocket(options?: PluginUdpSocketOptions): PluginUdpSocket;
|
|
129
|
+
/** Closes every UDP socket created through this control. */
|
|
97
130
|
closeAll(): Promise<void>;
|
|
98
131
|
}
|
|
132
|
+
/** Network capability exposed when the plugin declares `network`. */
|
|
99
133
|
export interface PluginNetworkControl {
|
|
134
|
+
/** UDP socket factory. HTTP requests use the sibling `ctx.fetch` capability. */
|
|
100
135
|
readonly udp: PluginUdpControl;
|
|
101
136
|
}
|
|
102
137
|
/**
|
|
103
|
-
*
|
|
138
|
+
* A message delivered through the plugin-to-plugin event bus.
|
|
104
139
|
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
140
|
+
* Every message carries metadata about its publisher so subscribers can
|
|
141
|
+
* apply routing or filtering logic based on the source plugin.
|
|
142
|
+
*/
|
|
143
|
+
export interface PluginEventBusMessage {
|
|
144
|
+
/** The topic this message was published to. */
|
|
145
|
+
topic: string;
|
|
146
|
+
/**
|
|
147
|
+
* Structured-clone-compatible payload. The host does not interpret its
|
|
148
|
+
* business schema, but delivers an independent value to each subscriber.
|
|
149
|
+
*/
|
|
150
|
+
payload: unknown;
|
|
151
|
+
/** Epoch milliseconds when the host dispatched the message. */
|
|
152
|
+
timestamp: number;
|
|
153
|
+
/** Identity of the plugin instance that published this message. */
|
|
154
|
+
publisher: {
|
|
155
|
+
/** Name of the publishing plugin (from its `PluginDefinition.name`). */
|
|
156
|
+
pluginName: string;
|
|
157
|
+
/** Whether the publisher is a global or per-operator instance. */
|
|
158
|
+
instanceScope: 'operator' | 'global';
|
|
159
|
+
/** Operator ID when the publisher is an operator-scoped instance. */
|
|
160
|
+
operatorId?: string;
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Permission-gated pub/sub bus for in-process plugin-to-plugin communication.
|
|
165
|
+
*
|
|
166
|
+
* Topics are plain strings shared across all plugin instances within the same
|
|
167
|
+
* host process. Handlers are started synchronously in subscription order.
|
|
168
|
+
* Async handlers run independently; their errors are captured and logged by
|
|
169
|
+
* the host rather than propagated to the publisher.
|
|
170
|
+
*
|
|
171
|
+
* **Lifecycle**: the host automatically removes all subscriptions owned by a
|
|
172
|
+
* plugin instance when it unloads. Individual subscriptions can be cancelled
|
|
173
|
+
* earlier by calling the function returned from {@link subscribe}.
|
|
174
|
+
*
|
|
175
|
+
* **Topic naming**: use dot-separated, plugin-prefixed names to avoid
|
|
176
|
+
* collisions — for example `my-plugin.status.changed` or
|
|
177
|
+
* `callsign-filter.match.found`.
|
|
178
|
+
*/
|
|
179
|
+
export interface PluginEventBus {
|
|
180
|
+
/**
|
|
181
|
+
* Publishes a message to all current subscribers of the given topic.
|
|
182
|
+
*
|
|
183
|
+
* This is a fire-and-forget operation. The host guarantees that subscriber
|
|
184
|
+
* exceptions never propagate back to the caller. The call itself throws
|
|
185
|
+
* synchronously when the payload is not structured-clone compatible or
|
|
186
|
+
* contains a Host capability.
|
|
187
|
+
*
|
|
188
|
+
* @param topic - Exact topic string to publish to.
|
|
189
|
+
* @param payload - Optional structured-clone-compatible data. Keep payloads reasonably small.
|
|
190
|
+
*/
|
|
191
|
+
publish(topic: string, payload?: unknown): void;
|
|
192
|
+
/**
|
|
193
|
+
* Subscribes to messages on the given topic.
|
|
194
|
+
*
|
|
195
|
+
* The same handler function instance will only be added once per topic.
|
|
196
|
+
* Different closures with identical logic are treated as distinct subscribers.
|
|
197
|
+
*
|
|
198
|
+
* @param topic - Exact topic string to listen on.
|
|
199
|
+
* @param handler - Callback invoked for each matching message. May return a
|
|
200
|
+
* `Promise`; the host catches rejections and logs them.
|
|
201
|
+
* @returns An unsubscribe function. Calling it more than once is a no-op.
|
|
202
|
+
*/
|
|
203
|
+
subscribe(topic: string, handler: (message: PluginEventBusMessage) => void | Promise<void>): () => void;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Read-only summary of another operator in the same Host.
|
|
107
207
|
*/
|
|
108
|
-
export interface
|
|
208
|
+
export interface OtherOperatorSnapshot {
|
|
209
|
+
/** Unique operator identifier used by the host. */
|
|
210
|
+
readonly id: string;
|
|
211
|
+
/** Configured callsign of the operator/station. */
|
|
212
|
+
readonly callsign: string;
|
|
213
|
+
/** Configured grid locator of the operator/station. */
|
|
214
|
+
readonly grid: string;
|
|
215
|
+
/** Current transmit audio offset in Hz within the passband. */
|
|
216
|
+
readonly audioFrequencyHz: number;
|
|
217
|
+
/** Active digital mode descriptor, for example FT8 or FT4. */
|
|
218
|
+
readonly mode: ModeDescriptor;
|
|
219
|
+
/** Whether this operator is currently transmitting or otherwise armed. */
|
|
220
|
+
readonly isTransmitting: boolean;
|
|
221
|
+
/** Current transmit cycle selection where `0` is even and `1` is odd. */
|
|
222
|
+
readonly transmitCycles: number[];
|
|
223
|
+
/** Current automation runtime snapshot when available. */
|
|
224
|
+
readonly automation?: StrategyRuntimeSnapshot | null;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Read-only state and query surface for the current operator-scoped plugin
|
|
228
|
+
* instance. Mutations are submitted through `ctx.operatorCommands` when the
|
|
229
|
+
* plugin declares `operator:transmit-control`.
|
|
230
|
+
*/
|
|
231
|
+
export interface OperatorSnapshot {
|
|
109
232
|
/** Unique operator identifier used by the host. */
|
|
110
233
|
readonly id: string;
|
|
111
234
|
/** Whether this operator is currently transmitting or otherwise armed. */
|
|
@@ -122,39 +245,8 @@ export interface OperatorControl {
|
|
|
122
245
|
readonly transmitCycles: number[];
|
|
123
246
|
/** Current automation runtime snapshot visible to the operator UI. */
|
|
124
247
|
readonly automation: StrategyRuntimeSnapshot | null;
|
|
125
|
-
/**
|
|
126
|
-
|
|
127
|
-
/** Disables transmission/automation for the current operator. */
|
|
128
|
-
stopTransmitting(): void;
|
|
129
|
-
/**
|
|
130
|
-
* Requests that the operator call the specified target station.
|
|
131
|
-
*
|
|
132
|
-
* Passing `lastMessage` helps the host preserve the triggering context.
|
|
133
|
-
*/
|
|
134
|
-
call(callsign: string, lastMessage?: {
|
|
135
|
-
message: FrameMessage;
|
|
136
|
-
slotInfo: SlotInfo;
|
|
137
|
-
}): void;
|
|
138
|
-
/**
|
|
139
|
-
* Requests host-managed reply behavior for a decoded message.
|
|
140
|
-
*
|
|
141
|
-
* This is equivalent to an operator selecting a decode in the RX view while
|
|
142
|
-
* keeping the API independent from any specific UDP/control protocol.
|
|
143
|
-
*/
|
|
144
|
-
replyToDecode(decode: {
|
|
145
|
-
callsign: string;
|
|
146
|
-
lastMessage: {
|
|
147
|
-
message: FrameMessage;
|
|
148
|
-
slotInfo: SlotInfo;
|
|
149
|
-
};
|
|
150
|
-
modifiers?: number;
|
|
151
|
-
}): void;
|
|
152
|
-
/**
|
|
153
|
-
* Updates the operator's transmit cycle preference.
|
|
154
|
-
*
|
|
155
|
-
* Pass a single value or an array to support alternating or multi-cycle modes.
|
|
156
|
-
*/
|
|
157
|
-
setTransmitCycles(cycles: number | number[]): void;
|
|
248
|
+
/** Returns read-only snapshots for operators other than the current instance. */
|
|
249
|
+
getOtherOperators(): OtherOperatorSnapshot[];
|
|
158
250
|
/**
|
|
159
251
|
* Checks whether this operator has previously worked the given callsign.
|
|
160
252
|
*/
|
|
@@ -166,40 +258,81 @@ export interface OperatorControl {
|
|
|
166
258
|
* working the target callsign.
|
|
167
259
|
*/
|
|
168
260
|
isTargetBeingWorkedByOthers(targetCallsign: string): boolean;
|
|
169
|
-
/** Clears host-managed decoded-message views when available. */
|
|
170
|
-
clearDecodes(window?: number): void;
|
|
171
|
-
/** Stops current transmission/automation. */
|
|
172
|
-
haltTransmission(options?: {
|
|
173
|
-
autoOnly?: boolean;
|
|
174
|
-
}): void;
|
|
175
|
-
/** Stores the current free-text message without necessarily transmitting it. */
|
|
176
|
-
setFreeText(text: string): void;
|
|
177
|
-
/** Requests transmission of free text. If text is provided it is stored first. */
|
|
178
|
-
sendFreeText(text?: string): void;
|
|
179
|
-
/** Applies a temporary session grid/location override when the host supports it. */
|
|
180
|
-
setTemporaryLocation(location: string): void;
|
|
181
|
-
/** Requests callsign highlighting in host decode views when available. */
|
|
182
|
-
highlightCallsign(rule: {
|
|
183
|
-
callsign: string;
|
|
184
|
-
background?: string | null;
|
|
185
|
-
foreground?: string | null;
|
|
186
|
-
lastOnly?: boolean;
|
|
187
|
-
}): void;
|
|
188
|
-
/**
|
|
189
|
-
* Records a completed QSO through the host logbook pipeline.
|
|
190
|
-
*/
|
|
191
|
-
recordQSO(record: QSORecord): void;
|
|
192
|
-
/**
|
|
193
|
-
* Pushes updated slot text content to the frontend operator view.
|
|
194
|
-
*/
|
|
195
|
-
notifySlotsUpdated(slots: OperatorSlots): void;
|
|
196
|
-
/**
|
|
197
|
-
* Pushes a strategy state change notification to the frontend operator view.
|
|
198
|
-
*/
|
|
199
|
-
notifyStateChanged(state: string): void;
|
|
200
261
|
}
|
|
201
262
|
/**
|
|
202
|
-
*
|
|
263
|
+
* Declarative operator mutations accepted by the host transmission framework.
|
|
264
|
+
*
|
|
265
|
+
* The command set deliberately contains no PTT, audio, mixer, encoder, raw
|
|
266
|
+
* transmit or emergency-stop primitive. Plugins can request product actions;
|
|
267
|
+
* only the host coordinators may translate them into a physical RF lifecycle.
|
|
268
|
+
*/
|
|
269
|
+
export type PluginOperatorCommand = {
|
|
270
|
+
type: 'start-automation';
|
|
271
|
+
} | {
|
|
272
|
+
type: 'stop-automation';
|
|
273
|
+
} | {
|
|
274
|
+
type: 'request-call';
|
|
275
|
+
callsign: string;
|
|
276
|
+
lastMessage?: {
|
|
277
|
+
message: FrameMessage;
|
|
278
|
+
slotInfo: SlotInfo;
|
|
279
|
+
};
|
|
280
|
+
} | {
|
|
281
|
+
type: 'reply-to-decode';
|
|
282
|
+
callsign: string;
|
|
283
|
+
lastMessage: {
|
|
284
|
+
message: FrameMessage;
|
|
285
|
+
slotInfo: SlotInfo;
|
|
286
|
+
};
|
|
287
|
+
modifiers?: number;
|
|
288
|
+
} | {
|
|
289
|
+
type: 'set-transmit-cycles';
|
|
290
|
+
cycles: number | number[];
|
|
291
|
+
} | {
|
|
292
|
+
type: 'remove-contribution';
|
|
293
|
+
} | {
|
|
294
|
+
type: 'clear-decodes';
|
|
295
|
+
window?: number;
|
|
296
|
+
} | {
|
|
297
|
+
type: 'set-free-text';
|
|
298
|
+
text: string;
|
|
299
|
+
} | {
|
|
300
|
+
type: 'send-free-text';
|
|
301
|
+
text?: string;
|
|
302
|
+
} | {
|
|
303
|
+
type: 'set-temporary-location';
|
|
304
|
+
location: string;
|
|
305
|
+
} | {
|
|
306
|
+
type: 'highlight-callsign';
|
|
307
|
+
callsign: string;
|
|
308
|
+
background?: string | null;
|
|
309
|
+
foreground?: string | null;
|
|
310
|
+
lastOnly?: boolean;
|
|
311
|
+
};
|
|
312
|
+
/** Settlement returned after the Host accepts an operator command. */
|
|
313
|
+
export interface PluginOperatorCommandResult {
|
|
314
|
+
/** Host command epoch allocated before any asynchronous work begins. */
|
|
315
|
+
epoch: number;
|
|
316
|
+
/** `superseded` means a newer host command revoked this request. */
|
|
317
|
+
outcome: 'completed' | 'superseded';
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Capability-scoped command port for plugins with
|
|
321
|
+
* `operator:transmit-control` and API v2.
|
|
322
|
+
*
|
|
323
|
+
* The property is omitted from contexts without that capability. Every submit
|
|
324
|
+
* is invocation-guarded and enters the host's per-operator intent lane.
|
|
325
|
+
*/
|
|
326
|
+
export interface OperatorCommandPort {
|
|
327
|
+
/**
|
|
328
|
+
* Submits one high-level operator command through the Host intent lane.
|
|
329
|
+
* Rejects when the invocation expired, the plugin safety gate is disabled,
|
|
330
|
+
* or the current physical lifecycle cannot accept the command.
|
|
331
|
+
*/
|
|
332
|
+
submit(command: PluginOperatorCommand): Promise<PluginOperatorCommandResult>;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Read-only operating-mode projection that is safe for plugins.
|
|
203
336
|
*/
|
|
204
337
|
export interface RadioOperatingMode {
|
|
205
338
|
/**
|
|
@@ -223,7 +356,8 @@ export interface RadioOperatingMode {
|
|
|
223
356
|
*/
|
|
224
357
|
readonly descriptor: ModeDescriptor;
|
|
225
358
|
}
|
|
226
|
-
|
|
359
|
+
/** Read-only frequency, band, mode and connection state for the active radio. */
|
|
360
|
+
export interface RadioView {
|
|
227
361
|
/** Current tuned radio frequency in Hz. */
|
|
228
362
|
readonly frequency: number;
|
|
229
363
|
/** Human-readable current band label, for example `20m`. */
|
|
@@ -232,31 +366,52 @@ export interface RadioControl {
|
|
|
232
366
|
readonly mode: RadioOperatingMode;
|
|
233
367
|
/** Whether the radio transport is currently connected. */
|
|
234
368
|
readonly isConnected: boolean;
|
|
235
|
-
/** Negotiated radio capability controls. Requires radio plugin permissions. */
|
|
236
|
-
readonly capabilities: RadioCapabilitiesControl;
|
|
237
|
-
/** Physical radio power controls. Requires radio plugin permissions. */
|
|
238
|
-
readonly power: RadioPowerControl;
|
|
239
|
-
/**
|
|
240
|
-
* Requests a frequency change.
|
|
241
|
-
*
|
|
242
|
-
* The host remains responsible for serializing hardware access and enforcing
|
|
243
|
-
* any safety or capability constraints.
|
|
244
|
-
*/
|
|
245
|
-
setFrequency(freq: number): Promise<void>;
|
|
246
369
|
}
|
|
247
370
|
/**
|
|
248
371
|
* Access to the host-managed radio capability negotiation system.
|
|
249
372
|
*/
|
|
250
|
-
export interface
|
|
251
|
-
/** Returns the current capability descriptor/state snapshot.
|
|
373
|
+
export interface RadioCapabilitiesView {
|
|
374
|
+
/** Returns the current capability descriptor/state snapshot. */
|
|
252
375
|
getSnapshot(): CapabilityList;
|
|
253
|
-
/** Returns a single capability state from the current snapshot, or null.
|
|
376
|
+
/** Returns a single capability state from the current snapshot, or null. */
|
|
254
377
|
getState(id: string): CapabilityState | null;
|
|
255
|
-
/** Refreshes readable capability values and returns the updated snapshot.
|
|
378
|
+
/** Refreshes readable capability values and returns the updated snapshot. */
|
|
256
379
|
refresh(): Promise<CapabilityList>;
|
|
257
|
-
/** Writes a capability value or triggers an action capability. Requires `radio:control`. */
|
|
258
|
-
write(payload: WriteCapabilityPayload): Promise<void>;
|
|
259
380
|
}
|
|
381
|
+
/** Declarative radio mutations accepted by the host radio coordinator. */
|
|
382
|
+
export type PluginRadioCommand = {
|
|
383
|
+
type: 'set-frequency';
|
|
384
|
+
frequency: number;
|
|
385
|
+
} | {
|
|
386
|
+
/** Atomically changes band and optionally starts the radio's tuner while RF is idle. */
|
|
387
|
+
type: 'switch-band';
|
|
388
|
+
frequency: number;
|
|
389
|
+
autoTune?: boolean;
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* Capability-scoped radio command port.
|
|
393
|
+
*
|
|
394
|
+
* This port exists only for plugins with `radio:control`. It deliberately does
|
|
395
|
+
* not expose a radio connection, PTT primitive, mode switch, audio output or
|
|
396
|
+
* any other physical device object.
|
|
397
|
+
*/
|
|
398
|
+
export interface RadioCommandPort {
|
|
399
|
+
/** Submits a frequency/band command after Host physical-idle validation. */
|
|
400
|
+
submit(command: PluginRadioCommand): Promise<void>;
|
|
401
|
+
}
|
|
402
|
+
/** Explicit tuner operations; no arbitrary capability identifier is accepted. */
|
|
403
|
+
export type PluginRadioTunerCommand = {
|
|
404
|
+
type: 'set-enabled';
|
|
405
|
+
enabled: boolean;
|
|
406
|
+
} | {
|
|
407
|
+
type: 'start-manual-tune';
|
|
408
|
+
};
|
|
409
|
+
/** Capability-scoped tuner command port for `radio:tuner-control` plugins. */
|
|
410
|
+
export interface RadioTunerCommandPort {
|
|
411
|
+
/** Submits one explicit tuner operation after Host safety validation. */
|
|
412
|
+
submit(command: PluginRadioTunerCommand): Promise<void>;
|
|
413
|
+
}
|
|
414
|
+
/** Optional target profile and startup behavior for a radio power command. */
|
|
260
415
|
export interface RadioPowerSetOptions {
|
|
261
416
|
/** Profile to target. Defaults to the active profile. */
|
|
262
417
|
profileId?: string;
|
|
@@ -266,13 +421,25 @@ export interface RadioPowerSetOptions {
|
|
|
266
421
|
/**
|
|
267
422
|
* Access to physical radio power management.
|
|
268
423
|
*/
|
|
269
|
-
export interface
|
|
270
|
-
/** Returns power support information for the active or specified profile.
|
|
424
|
+
export interface RadioPowerView {
|
|
425
|
+
/** Returns power support information for the active or specified profile. */
|
|
271
426
|
getSupport(profileId?: string): Promise<RadioPowerSupportInfo>;
|
|
272
|
-
/** Returns the last known power transition state for the active or specified profile.
|
|
427
|
+
/** Returns the last known power transition state for the active or specified profile. */
|
|
273
428
|
getState(profileId?: string): RadioPowerStateEvent | null;
|
|
274
|
-
|
|
275
|
-
|
|
429
|
+
}
|
|
430
|
+
/** Declarative power-state transition accepted by `ctx.radioPowerCommands`. */
|
|
431
|
+
export type PluginRadioPowerCommand = {
|
|
432
|
+
/** Command discriminator. */
|
|
433
|
+
type: 'set-power';
|
|
434
|
+
/** Requested physical/controller power target. */
|
|
435
|
+
state: RadioPowerTarget;
|
|
436
|
+
/** Optional profile selection and automatic engine startup behavior. */
|
|
437
|
+
options?: RadioPowerSetOptions;
|
|
438
|
+
};
|
|
439
|
+
/** Capability-scoped physical power command port for `radio:power` plugins. */
|
|
440
|
+
export interface RadioPowerCommandPort {
|
|
441
|
+
/** Requests a power transition and resolves with the Host's final state. */
|
|
442
|
+
submit(command: PluginRadioPowerCommand): Promise<RadioPowerResponse>;
|
|
276
443
|
}
|
|
277
444
|
/**
|
|
278
445
|
* Filter criteria for querying QSO records from the logbook.
|
|
@@ -315,35 +482,37 @@ export interface QSOQueryFilter {
|
|
|
315
482
|
/**
|
|
316
483
|
* Callsign-bound view over a single logbook.
|
|
317
484
|
*
|
|
318
|
-
* The host resolves
|
|
319
|
-
* the handle valid
|
|
485
|
+
* The host resolves an already registered concrete logbook on each operation,
|
|
486
|
+
* which keeps the handle valid across reloads without implicitly creating data.
|
|
320
487
|
*/
|
|
321
|
-
export interface
|
|
488
|
+
export interface CallsignLogbookReadAccess {
|
|
322
489
|
/** Normalized callsign that scopes this accessor. */
|
|
323
490
|
readonly callsign: string;
|
|
324
|
-
/** Returns the resolved logbook id, or null when no logbook
|
|
491
|
+
/** Returns the resolved logbook id, or null when no logbook is registered. */
|
|
325
492
|
getLogBookId(): Promise<string | null>;
|
|
326
493
|
/** Queries QSO records matching the given filter. */
|
|
327
494
|
queryQSOs(filter: QSOQueryFilter): Promise<import('@tx5dr/contracts').QSORecord[]>;
|
|
328
495
|
/** Counts QSO records matching the given filter. */
|
|
329
496
|
countQSOs(filter?: QSOQueryFilter): Promise<number>;
|
|
330
|
-
/** Adds a new QSO record to this callsign's logbook. */
|
|
331
|
-
addQSO(record: import('@tx5dr/contracts').QSORecord): Promise<void>;
|
|
332
|
-
/** Updates partial fields of an existing QSO record. */
|
|
333
|
-
updateQSO(qsoId: string, updates: Partial<import('@tx5dr/contracts').QSORecord>): Promise<void>;
|
|
334
497
|
/** Returns current statistics for this callsign's logbook. */
|
|
335
498
|
getStatistics(): Promise<import('@tx5dr/contracts').LogBookStatistics | null>;
|
|
499
|
+
}
|
|
500
|
+
/** Durable mutation operations scoped to one normalized station callsign. */
|
|
501
|
+
export interface CallsignLogbookCommandPort {
|
|
502
|
+
/** Normalized callsign that scopes this accessor. */
|
|
503
|
+
readonly callsign: string;
|
|
504
|
+
/** Adds a QSO and resolves with the final record after durable commit. */
|
|
505
|
+
addQSO(record: import('@tx5dr/contracts').QSORecord): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
506
|
+
/** Updates a QSO and resolves with the final record after durable commit. */
|
|
507
|
+
updateQSO(qsoId: string, updates: Partial<import('@tx5dr/contracts').QSORecord>): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
336
508
|
/** Notifies the frontend that this callsign's logbook changed. */
|
|
337
509
|
notifyUpdated(operatorId?: string): Promise<void>;
|
|
338
510
|
}
|
|
339
|
-
/**
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
* without host-side special handling.
|
|
345
|
-
*/
|
|
346
|
-
export interface LogbookAccess {
|
|
511
|
+
/** Combined read/write callsign-bound logbook capability. */
|
|
512
|
+
export interface CallsignLogbookAccess extends CallsignLogbookReadAccess, CallsignLogbookCommandPort {
|
|
513
|
+
}
|
|
514
|
+
/** Read-only worked-status and QSO query capability for `logbook:read`. */
|
|
515
|
+
export interface LogbookReadAccess {
|
|
347
516
|
/** Checks whether the callsign has already been worked. */
|
|
348
517
|
hasWorked(callsign: string, options?: {
|
|
349
518
|
anyBand?: boolean;
|
|
@@ -356,14 +525,24 @@ export interface LogbookAccess {
|
|
|
356
525
|
queryQSOs(filter: QSOQueryFilter): Promise<import('@tx5dr/contracts').QSORecord[]>;
|
|
357
526
|
/** Counts QSO records matching the given filter. */
|
|
358
527
|
countQSOs(filter?: QSOQueryFilter): Promise<number>;
|
|
359
|
-
/** Returns a callsign-bound accessor suitable for global plugin instances. */
|
|
360
|
-
forCallsign(callsign: string):
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
528
|
+
/** Returns a read-only callsign-bound accessor suitable for global plugin instances. */
|
|
529
|
+
forCallsign(callsign: string): CallsignLogbookReadAccess;
|
|
530
|
+
}
|
|
531
|
+
/** Durable mutation operations exposed by the `logbook:write` permission. */
|
|
532
|
+
export interface LogbookCommandPort {
|
|
533
|
+
/** Adds a QSO and resolves with the final record after durable commit. */
|
|
534
|
+
addQSO(record: import('@tx5dr/contracts').QSORecord): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
535
|
+
/** Updates a QSO and resolves with the final record after durable commit. */
|
|
536
|
+
updateQSO(qsoId: string, updates: Partial<import('@tx5dr/contracts').QSORecord>): Promise<import('@tx5dr/contracts').QSORecord>;
|
|
365
537
|
/** Notifies the frontend to refresh logbook data (call after batch writes). */
|
|
366
538
|
notifyUpdated(): Promise<void>;
|
|
539
|
+
/** Returns a callsign-bound durable mutation port for global plugin instances. */
|
|
540
|
+
forCallsign(callsign: string): CallsignLogbookCommandPort;
|
|
541
|
+
}
|
|
542
|
+
/** @deprecated Prefer capability-specific LogbookReadAccess and LogbookCommandPort. */
|
|
543
|
+
export interface LogbookAccess extends LogbookReadAccess, LogbookCommandPort {
|
|
544
|
+
/** Returns a combined read/write accessor for the requested station callsign. */
|
|
545
|
+
forCallsign(callsign: string): CallsignLogbookAccess;
|
|
367
546
|
}
|
|
368
547
|
/**
|
|
369
548
|
* Optional constraints used when asking the host for a quieter transmit offset.
|
|
@@ -413,7 +592,9 @@ export interface BandAccess {
|
|
|
413
592
|
* decode environment.
|
|
414
593
|
*
|
|
415
594
|
* Returns `null` when the host cannot evaluate the slot or when no suitable
|
|
416
|
-
* idle window is found.
|
|
595
|
+
* idle window is found. A successful result also reserves that offset for the
|
|
596
|
+
* current operator and analyzed slot so later operators avoid selecting the
|
|
597
|
+
* same window.
|
|
417
598
|
*/
|
|
418
599
|
findIdleTransmitFrequency(options?: IdleTransmitFrequencyOptions): number | null;
|
|
419
600
|
/**
|
|
@@ -455,7 +636,8 @@ export interface PanelMeta {
|
|
|
455
636
|
*/
|
|
456
637
|
export interface UIBridge {
|
|
457
638
|
/**
|
|
458
|
-
* Publishes
|
|
639
|
+
* Publishes a JSON-compatible snapshot for the given declarative panel id.
|
|
640
|
+
* Mutating the caller's object after this call does not alter panel state.
|
|
459
641
|
*/
|
|
460
642
|
send(panelId: string, data: unknown): void;
|
|
461
643
|
/**
|
|
@@ -484,7 +666,7 @@ export interface UIBridge {
|
|
|
484
666
|
*/
|
|
485
667
|
registerPageHandler(handler: PluginUIHandler): void;
|
|
486
668
|
/**
|
|
487
|
-
* Pushes a
|
|
669
|
+
* Pushes a JSON-compatible data snapshot to the specific page session.
|
|
488
670
|
*
|
|
489
671
|
* Prefer this API whenever the plugin already knows the target session id
|
|
490
672
|
* (for example from {@link PluginUIRequestContext.pageSessionId} or
|
|
@@ -499,7 +681,7 @@ export interface UIBridge {
|
|
|
499
681
|
*/
|
|
500
682
|
listActivePageSessions(pageId: string): PluginUIPageSessionInfo[];
|
|
501
683
|
/**
|
|
502
|
-
* Pushes a
|
|
684
|
+
* Pushes a JSON-compatible data snapshot to an iframe UI page by page id.
|
|
503
685
|
*
|
|
504
686
|
* This compatibility helper only succeeds when exactly one active session of
|
|
505
687
|
* the current plugin instance matches the page id. If multiple sessions are
|
|
@@ -511,8 +693,9 @@ export interface UIBridge {
|
|
|
511
693
|
* Handler for custom messages sent from iframe UI pages.
|
|
512
694
|
*
|
|
513
695
|
* Plugins register a handler via `ctx.ui.registerPageHandler()` to receive
|
|
514
|
-
*
|
|
515
|
-
*
|
|
696
|
+
* application-defined invoke requests from their iframe-based UIs. The Host
|
|
697
|
+
* does not interpret the business schema, but it enforces the page/session
|
|
698
|
+
* authorization and JSON data boundary in both directions.
|
|
516
699
|
*/
|
|
517
700
|
export interface PluginUIHandler {
|
|
518
701
|
/**
|
|
@@ -520,49 +703,77 @@ export interface PluginUIHandler {
|
|
|
520
703
|
*
|
|
521
704
|
* @param pageId - The page that sent the message.
|
|
522
705
|
* @param action - Developer-defined action identifier.
|
|
523
|
-
* @param data -
|
|
706
|
+
* @param data - JSON-compatible snapshot from the iframe; validate it as
|
|
707
|
+
* untrusted input before use.
|
|
524
708
|
* @param requestContext - Host-authenticated page context, including any
|
|
525
709
|
* bound resource for this page session.
|
|
526
|
-
* @returns
|
|
710
|
+
* @returns A JSON-compatible response snapshot sent back to the iframe.
|
|
527
711
|
*/
|
|
528
712
|
onMessage(pageId: string, action: string, data: unknown, requestContext: PluginUIRequestContext): Promise<unknown>;
|
|
529
713
|
}
|
|
714
|
+
/** Host-authenticated user identity attached to an iframe invoke request. */
|
|
530
715
|
export interface PluginUIRequestUser {
|
|
716
|
+
/** Stable token/session identifier; not the raw credential. */
|
|
531
717
|
readonly tokenId: string;
|
|
718
|
+
/** Effective role at the time the Host authorizes the request. */
|
|
532
719
|
readonly role: 'viewer' | 'operator' | 'admin';
|
|
720
|
+
/** Operator IDs the current user is allowed to access. */
|
|
533
721
|
readonly operatorIds: string[];
|
|
722
|
+
/** Fine-grained grants associated with the authenticated user, when present. */
|
|
534
723
|
readonly permissionGrants?: PermissionGrant[];
|
|
535
724
|
}
|
|
725
|
+
/** Resource identity resolved and authorized from the page descriptor binding. */
|
|
536
726
|
export interface PluginUIBoundResource {
|
|
727
|
+
/** Kind declared by `resourceBinding`. */
|
|
537
728
|
readonly kind: 'callsign' | 'operator';
|
|
729
|
+
/** Normalized callsign or authorized operator ID. */
|
|
538
730
|
readonly value: string;
|
|
539
731
|
}
|
|
732
|
+
/** Plugin instance selected by the Host for this page request. */
|
|
540
733
|
export type PluginUIInstanceTarget = {
|
|
541
734
|
readonly kind: 'global';
|
|
542
735
|
} | {
|
|
543
736
|
readonly kind: 'operator';
|
|
544
737
|
readonly operatorId: string;
|
|
545
738
|
};
|
|
739
|
+
/** Read-only identity of one active plugin iframe page session. */
|
|
546
740
|
export interface PluginUIPageSessionInfo {
|
|
741
|
+
/** Unique ID used for exact session pushes. */
|
|
547
742
|
readonly sessionId: string;
|
|
743
|
+
/** `PluginDefinition.ui.pages` entry rendered by this session. */
|
|
548
744
|
readonly pageId: string;
|
|
745
|
+
/** Host-authorized resource binding, when the page declares one. */
|
|
549
746
|
readonly resource?: PluginUIBoundResource;
|
|
550
747
|
}
|
|
748
|
+
/** Page-session identity plus an exact push channel back to that iframe. */
|
|
551
749
|
export interface PluginUIPageContext extends PluginUIPageSessionInfo {
|
|
750
|
+
/** Sends a JSON-compatible snapshot to this exact page session. */
|
|
552
751
|
push(action: string, data?: unknown): void;
|
|
553
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Host-authenticated context passed to an iframe page handler.
|
|
755
|
+
*
|
|
756
|
+
* Treat `data` from the iframe as untrusted input. Use this context, rather
|
|
757
|
+
* than caller-supplied IDs, for authorization and storage scoping.
|
|
758
|
+
*/
|
|
554
759
|
export interface PluginUIRequestContext {
|
|
760
|
+
/** Same exact page session identifier exposed as `page.sessionId`. */
|
|
555
761
|
readonly pageSessionId: string;
|
|
762
|
+
/** User identity authorized by the Host for this request. */
|
|
556
763
|
readonly user: PluginUIRequestUser;
|
|
764
|
+
/** Bound callsign/operator, when required by the page descriptor. */
|
|
557
765
|
readonly resource?: PluginUIBoundResource;
|
|
766
|
+
/** Global or operator plugin instance receiving the request. */
|
|
558
767
|
readonly instanceTarget: PluginUIInstanceTarget;
|
|
768
|
+
/** Exact page session/push capability, valid only during the current handler invocation. */
|
|
559
769
|
readonly page: PluginUIPageContext;
|
|
560
770
|
/**
|
|
561
771
|
* Page-scoped file storage shared with iframe `tx5dr.file*()` calls.
|
|
562
772
|
*
|
|
563
773
|
* Use this in `registerPageHandler()` handlers to read files uploaded by the
|
|
564
774
|
* current iframe page session without reconstructing host-internal scope
|
|
565
|
-
* paths.
|
|
775
|
+
* paths. Both `page` and `files` are exact-invocation capabilities: do not
|
|
776
|
+
* retain and invoke them after the current `onMessage()` promise settles.
|
|
566
777
|
*/
|
|
567
778
|
readonly files: PluginFileStore;
|
|
568
779
|
}
|
|
@@ -573,9 +784,9 @@ export interface PluginUIRequestContext {
|
|
|
573
784
|
* traversal outside the sandbox is rejected by the host.
|
|
574
785
|
*/
|
|
575
786
|
export interface PluginFileStore {
|
|
576
|
-
/** Writes
|
|
787
|
+
/** Writes a copy of the Buffer, creating or replacing the file. */
|
|
577
788
|
write(path: string, data: Buffer): Promise<void>;
|
|
578
|
-
/** Reads a file. Returns `null` when the path does not exist. */
|
|
789
|
+
/** Reads a file into a new Buffer. Returns `null` when the path does not exist. */
|
|
579
790
|
read(path: string): Promise<Buffer | null>;
|
|
580
791
|
/** Deletes a file. Returns `true` if the file existed and was removed. */
|
|
581
792
|
delete(path: string): Promise<boolean>;
|