@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.
Files changed (51) hide show
  1. package/README.md +274 -17
  2. package/dist/__tests__/adif.test.js +40 -0
  3. package/dist/__tests__/adif.test.js.map +1 -1
  4. package/dist/__tests__/capability-context.test.d.ts +2 -0
  5. package/dist/__tests__/capability-context.test.d.ts.map +1 -0
  6. package/dist/__tests__/capability-context.test.js +99 -0
  7. package/dist/__tests__/capability-context.test.js.map +1 -0
  8. package/dist/__tests__/testing-utils.test.js +235 -14
  9. package/dist/__tests__/testing-utils.test.js.map +1 -1
  10. package/dist/capabilities.d.ts +37 -0
  11. package/dist/capabilities.d.ts.map +1 -0
  12. package/dist/capabilities.js +48 -0
  13. package/dist/capabilities.js.map +1 -0
  14. package/dist/context.d.ts +95 -56
  15. package/dist/context.d.ts.map +1 -1
  16. package/dist/definition.d.ts +73 -20
  17. package/dist/definition.d.ts.map +1 -1
  18. package/dist/definition.js +8 -1
  19. package/dist/definition.js.map +1 -1
  20. package/dist/helpers.d.ts +340 -129
  21. package/dist/helpers.d.ts.map +1 -1
  22. package/dist/hooks.d.ts +54 -34
  23. package/dist/hooks.d.ts.map +1 -1
  24. package/dist/host-dependencies.d.ts +101 -0
  25. package/dist/host-dependencies.d.ts.map +1 -1
  26. package/dist/index.d.ts +9 -5
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +5 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/runtime.d.ts +164 -9
  31. package/dist/runtime.d.ts.map +1 -1
  32. package/dist/runtime.js +9 -1
  33. package/dist/runtime.js.map +1 -1
  34. package/dist/settings.d.ts +31 -0
  35. package/dist/settings.d.ts.map +1 -1
  36. package/dist/sync.d.ts +98 -6
  37. package/dist/sync.d.ts.map +1 -1
  38. package/dist/sync.js +6 -0
  39. package/dist/sync.js.map +1 -1
  40. package/dist/testing/index.d.ts +70 -12
  41. package/dist/testing/index.d.ts.map +1 -1
  42. package/dist/testing/index.js +307 -104
  43. package/dist/testing/index.js.map +1 -1
  44. package/dist/utils/adif.d.ts.map +1 -1
  45. package/dist/utils/adif.js +6 -2
  46. package/dist/utils/adif.js.map +1 -1
  47. package/dist/utils/qso-text-fields.d.ts +7 -1
  48. package/dist/utils/qso-text-fields.d.ts.map +1 -1
  49. package/dist/utils/qso-text-fields.js +74 -6
  50. package/dist/utils/qso-text-fields.js.map +1 -1
  51. package/package.json +4 -4
package/dist/context.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { KVStore, PluginLogger, PluginTimers, OperatorControl, RadioControl, LogbookAccess, BandAccess, UIBridge, PluginFileStore, PluginNetworkControl } from './helpers.js';
1
+ import type { KVStore, PluginLogger, PluginTimers, OperatorSnapshot, OperatorCommandPort, RadioView, RadioCapabilitiesView, RadioCommandPort, RadioTunerCommandPort, RadioPowerView, RadioPowerCommandPort, LogbookReadAccess, LogbookAccess, BandAccess, UIBridge, PluginFileStore, PluginNetworkControl, PluginEventBus } from './helpers.js';
2
+ import type { PluginPermission } from '@tx5dr/contracts';
2
3
  import type { LogbookSyncRegistrar } from './sync.js';
3
4
  import type { HostSettingsControl } from './settings.js';
4
5
  import type { HostDependencies } from './host-dependencies.js';
@@ -15,12 +16,13 @@ import type { HostDependencies } from './host-dependencies.js';
15
16
  * here, plugin code should treat it as unavailable rather than reaching into
16
17
  * TX-5DR internals.
17
18
  */
18
- export interface PluginContext {
19
+ export interface PluginContextBase {
19
20
  /**
20
21
  * Resolved plugin configuration values.
21
22
  *
22
- * The host validates and persists settings, then injects the final values into
23
- * this readonly map before invoking hooks or lifecycle methods. Use
23
+ * The host validates and persists settings, then supplies a detached snapshot
24
+ * before invoking hooks or lifecycle methods. Mutating nested values does not
25
+ * update persistence; call {@link PluginContextBase.updateConfig} instead. Use
24
26
  * {@link PluginHooks.onConfigChange} to react to updates.
25
27
  */
26
28
  readonly config: Readonly<Record<string, unknown>>;
@@ -64,24 +66,12 @@ export interface PluginContext {
64
66
  * prefer this over raw `setInterval` calls inside plugin code.
65
67
  */
66
68
  readonly timers: PluginTimers;
69
+ /** Read-only snapshot and query surface for the current operator. */
70
+ readonly operator: OperatorSnapshot;
67
71
  /**
68
- * Control surface for the current operator.
72
+ * Read-only projection of the physical radio state.
69
73
  */
70
- readonly operator: OperatorControl;
71
- /**
72
- * Access to the physical radio state and tuning controls.
73
- */
74
- readonly radio: RadioControl;
75
- /**
76
- * Full logbook access — read-only queries, record writes and UI notifications.
77
- *
78
- * Provides the original read-only helpers (`hasWorked`, `hasWorkedDXCC`,
79
- * `hasWorkedGrid`) plus advanced query (`queryQSOs`, `countQSOs`), write
80
- * (`addQSO`, `updateQSO`) and notification (`notifyUpdated`) capabilities.
81
- * Sync providers and other data-oriented plugins use the write methods to
82
- * self-orchestrate their flow without host-side special handling.
83
- */
84
- readonly logbook: LogbookAccess;
74
+ readonly radio: RadioView;
85
75
  /**
86
76
  * Read-only access to current-band and slot decode data.
87
77
  */
@@ -99,42 +89,91 @@ export interface PluginContext {
99
89
  * For structured JSON data, prefer {@link PluginContext.store} instead.
100
90
  */
101
91
  readonly files: PluginFileStore;
102
- /**
103
- * Logbook sync registration entry point.
104
- *
105
- * Utility plugins that implement logbook synchronization call
106
- * `ctx.logbookSync.register(provider)` during `onLoad` to register their
107
- * sync provider. The host manages the provider lifecycle and UI integration.
108
- */
92
+ }
93
+ type CapabilityProperty<Permissions extends readonly PluginPermission[], Permission extends PluginPermission, Property extends object> = number extends Permissions['length'] ? object : Permission extends Permissions[number] ? Property : object;
94
+ type SettingsCapability<Permissions extends readonly PluginPermission[]> = number extends Permissions['length'] ? object : Extract<Permissions[number], `settings:${string}`> extends never ? object : {
95
+ readonly settings: CapabilityProperty<Permissions, 'settings:ft8', Pick<HostSettingsControl, 'ft8'>> & CapabilityProperty<Permissions, 'settings:decode-windows', Pick<HostSettingsControl, 'decodeWindows'>> & CapabilityProperty<Permissions, 'settings:realtime', Pick<HostSettingsControl, 'realtime'>> & CapabilityProperty<Permissions, 'settings:frequency-presets', Pick<HostSettingsControl, 'frequencyPresets'>> & CapabilityProperty<Permissions, 'settings:station', Pick<HostSettingsControl, 'station'>> & CapabilityProperty<Permissions, 'settings:psk-reporter', Pick<HostSettingsControl, 'pskReporter'>> & CapabilityProperty<Permissions, 'settings:ntp', Pick<HostSettingsControl, 'ntp'>>;
96
+ };
97
+ type LogbookCapability<Permissions extends readonly PluginPermission[]> = number extends Permissions['length'] ? object : 'logbook:write' extends Permissions[number] ? {
98
+ readonly logbook: LogbookAccess;
99
+ } : 'logbook:read' extends Permissions[number] ? {
100
+ readonly logbook: LogbookReadAccess;
101
+ } : object;
102
+ /**
103
+ * Plugin context whose privileged ports are derived from literal manifest
104
+ * permissions. Capabilities that were not declared do not exist in the type.
105
+ *
106
+ * Host handles are invocation-scoped. In particular, a `Response` returned by
107
+ * `ctx.fetch` and its Headers/body reader must be consumed before the current
108
+ * Host callback settles; retaining the native handle for later use results in
109
+ * `PLUGIN_INVOCATION_EXPIRED`.
110
+ */
111
+ export type PluginContextFor<Permissions extends readonly PluginPermission[]> = PluginContextBase & CapabilityProperty<Permissions, 'operator:transmit-control', {
112
+ readonly operatorCommands: OperatorCommandPort;
113
+ }> & CapabilityProperty<Permissions, 'radio:read', {
114
+ readonly radioCapabilities: RadioCapabilitiesView;
115
+ readonly radioPower: RadioPowerView;
116
+ }> & CapabilityProperty<Permissions, 'radio:control', {
117
+ readonly radioCommands: RadioCommandPort;
118
+ }> & CapabilityProperty<Permissions, 'radio:tuner-control', {
119
+ readonly radioTunerCommands: RadioTunerCommandPort;
120
+ }> & CapabilityProperty<Permissions, 'radio:power', {
121
+ readonly radioPowerCommands: RadioPowerCommandPort;
122
+ }> & LogbookCapability<Permissions> & CapabilityProperty<Permissions, 'logbook:sync', {
109
123
  readonly logbookSync: LogbookSyncRegistrar;
110
- /**
111
- * Permission-gated host settings control surface.
112
- *
113
- * Each namespace requires the matching `settings:*` manifest permission.
114
- */
115
- readonly settings: HostSettingsControl;
116
- /**
117
- * Permission-gated network capabilities.
118
- *
119
- * UDP sockets are host-managed and automatically closed when the plugin
120
- * instance unloads. This is intentionally protocol-agnostic; protocol codecs
121
- * such as WSJT-X UDP belong inside plugins.
122
- */
123
- readonly network?: PluginNetworkControl;
124
- /**
125
- * Host-owned runtime dependencies exposed to plugins.
126
- *
127
- * Native dependencies such as Hamlib are loaded by the host process. Each
128
- * dependency is optional and requires its own manifest permission; feature
129
- * detect before use.
130
- */
131
- readonly hostDependencies: HostDependencies;
132
- /**
133
- * Permission-gated HTTP client.
134
- *
135
- * This method is only available when the plugin declares the corresponding
136
- * network permission. Treat it as optional and feature-detect before calling.
137
- */
138
- readonly fetch?: (url: string, init?: RequestInit) => Promise<Response>;
124
+ }> & SettingsCapability<Permissions> & CapabilityProperty<Permissions, 'network', {
125
+ readonly network: PluginNetworkControl;
126
+ readonly fetch: (url: string, init?: RequestInit) => Promise<Response>;
127
+ }> & CapabilityProperty<Permissions, 'plugin:event-bus', {
128
+ readonly eventBus: PluginEventBus;
129
+ }> & CapabilityProperty<Permissions, 'host:hamlib', {
130
+ readonly hostDependencies: HostDependencies & Required<Pick<HostDependencies, 'hamlib'>>;
131
+ }>;
132
+ /** Safe default context for code that has not declared literal permissions. */
133
+ export type PluginContext = PluginContextFor<readonly []>;
134
+ /**
135
+ * Teardown-only context passed to `onUnload`.
136
+ *
137
+ * Cleanup may inspect the read-only operator state, flush plugin-owned state
138
+ * and release plugin-owned files. The Host also permits previously acquired
139
+ * native-resource and UI handles to finish cleanup, but does not reopen radio,
140
+ * network, event-bus, logbook or command capabilities while the instance is
141
+ * being revoked.
142
+ */
143
+ export type PluginCleanupContext = Pick<PluginContextBase, 'store' | 'log' | 'timers' | 'files' | 'operator'>;
144
+ /** Host-side erased runtime shape. Public plugin definitions should use `definePlugin()`. */
145
+ export type RuntimePluginContext = PluginContextBase & Partial<{
146
+ operatorCommands: OperatorCommandPort;
147
+ radioCapabilities: RadioCapabilitiesView;
148
+ radioCommands: RadioCommandPort;
149
+ radioTunerCommands: RadioTunerCommandPort;
150
+ radioPower: RadioPowerView;
151
+ radioPowerCommands: RadioPowerCommandPort;
152
+ logbook: LogbookReadAccess | LogbookAccess;
153
+ logbookSync: LogbookSyncRegistrar;
154
+ settings: Partial<HostSettingsControl>;
155
+ network: PluginNetworkControl;
156
+ eventBus: PluginEventBus;
157
+ hostDependencies: HostDependencies;
158
+ fetch: (url: string, init?: RequestInit) => Promise<Response>;
159
+ }>;
160
+ /**
161
+ * Deliberately narrow context captured by a speculative strategy runtime.
162
+ * Decisions can inspect operator state and emit a result, but cannot retain a
163
+ * command, radio, logbook-write, network, timer or UI capability.
164
+ */
165
+ export interface StrategyPluginContext {
166
+ /** Detached snapshot of the strategy plugin's resolved configuration. */
167
+ readonly config: Readonly<Record<string, unknown>>;
168
+ /** Logger scoped to this strategy instance. */
169
+ readonly log: PluginLogger;
170
+ /** Read-only operator snapshot; mutation ports are deliberately absent. */
171
+ readonly operator: OperatorSnapshot;
172
+ }
173
+ /** Minimal context used to evaluate a transmit-control eligibility predicate. */
174
+ export interface PluginEligibilityContext {
175
+ /** Current detached configuration snapshot used by the synchronous gate. */
176
+ readonly config: Readonly<Record<string, unknown>>;
139
177
  }
178
+ export {};
140
179
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,QAAQ,EACR,eAAe,EACf,oBAAoB,EACrB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAEnD;;;;;;;;OAQG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5D;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE;QACd;;WAEG;QACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;QAEzB;;WAEG;QACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;KAC5B,CAAC;IAEF;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAE3B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAE9B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IAEnC;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAE7B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAEhC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;IAEtB;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAEhC;;;;;;OAMG;IACH,QAAQ,CAAC,WAAW,EAAE,oBAAoB,CAAC;IAE3C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IAEvC;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAExC;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAE5C;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACzE"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { PluginSettingDescriptor, PluginQuickAction, PluginQuickSetting, PluginPanelDescriptor, PluginPermission, PluginType, PluginInstanceScope, PluginUIPageDescriptor } from '@tx5dr/contracts';
2
- import type { PluginContext } from './context.js';
2
+ import type { PluginCleanupContext, PluginContextFor, PluginEligibilityContext, StrategyPluginContext } from './context.js';
3
3
  import type { PluginHooks } from './hooks.js';
4
4
  import type { StrategyRuntime } from './runtime.js';
5
5
  /**
@@ -21,32 +21,43 @@ import type { StrategyRuntime } from './runtime.js';
21
21
  *
22
22
  * @example
23
23
  * ```js
24
- * /** @type {import('@tx5dr/plugin-api').PluginDefinition} *\/
25
- * export default {
24
+ * import { definePlugin } from '@tx5dr/plugin-api';
25
+ *
26
+ * export default definePlugin({
27
+ * apiVersion: 2,
26
28
  * name: 'my-plugin',
27
29
  * version: '1.0.0',
28
30
  * type: 'utility',
29
31
  * description: 'Annotates interesting decoded stations.',
32
+ * permissions: [],
30
33
  * hooks: {
31
34
  * onDecode(messages, ctx) {
32
35
  * ctx.log.info('decoded', { count: messages.length });
33
36
  * },
34
37
  * },
35
- * };
38
+ * });
36
39
  * ```
37
40
  *
38
41
  * @example
39
42
  * ```ts
40
- * import type { PluginDefinition } from '@tx5dr/plugin-api';
43
+ * import { definePlugin } from '@tx5dr/plugin-api';
41
44
  *
42
- * const plugin: PluginDefinition = {
45
+ * export default definePlugin({
46
+ * apiVersion: 2,
43
47
  * name: 'my-strategy',
44
48
  * version: '1.0.0',
45
49
  * type: 'strategy',
46
- * createStrategyRuntime(ctx) {
50
+ * createStrategyRuntime() {
47
51
  * return {
52
+ * checkpoint() {
53
+ * return {};
54
+ * },
55
+ * restore() {},
48
56
  * decide() {
49
- * return { stop: false };
57
+ * return {
58
+ * transmission: null,
59
+ * snapshot: this.getSnapshot(),
60
+ * };
50
61
  * },
51
62
  * getTransmitText() {
52
63
  * return null;
@@ -61,12 +72,18 @@ import type { StrategyRuntime } from './runtime.js';
61
72
  * reset() {},
62
73
  * };
63
74
  * },
64
- * };
65
- *
66
- * export default plugin;
75
+ * });
67
76
  * ```
68
77
  */
69
- export interface PluginDefinition {
78
+ export interface PluginDefinition<Permissions extends readonly PluginPermission[] = readonly []> {
79
+ /**
80
+ * Public API contract version. All new plugins should use `2`.
81
+ *
82
+ * API v2 is required for strategy plugins and for utility plugins that request
83
+ * any mutation capability: `operator:transmit-control`, `radio:control`,
84
+ * `radio:tuner-control`, `radio:power`, `logbook:write` or `logbook:sync`.
85
+ */
86
+ apiVersion?: 2;
70
87
  /**
71
88
  * Stable machine-readable plugin identifier.
72
89
  *
@@ -91,11 +108,19 @@ export interface PluginDefinition {
91
108
  * do not own the core automation state machine.
92
109
  */
93
110
  type: PluginType;
111
+ /** Optional strategy capabilities advertised to Host UI and routing. */
112
+ strategyFeatures?: {
113
+ /** Declares the `QueuedStrategyRuntime` assisted-target queue contract. */
114
+ targetQueue?: 1;
115
+ };
94
116
  /**
95
117
  * Controls whether the host creates one instance per operator or a single
96
118
  * shared instance for the whole station.
97
119
  *
98
120
  * Defaults to `operator` when omitted.
121
+ * Global scope is utility-only. It cannot use operator-scoped settings or
122
+ * quick settings, and only global-compatible hooks/panels are accepted by the
123
+ * loader. Use it for station-wide sync, network services and radio policy.
99
124
  */
100
125
  instanceScope?: PluginInstanceScope;
101
126
  /**
@@ -111,7 +136,7 @@ export interface PluginDefinition {
111
136
  * Permissions allow the host to gate sensitive features such as network
112
137
  * access. Always declare the smallest set that the plugin truly needs.
113
138
  */
114
- permissions?: PluginPermission[];
139
+ permissions?: Permissions;
115
140
  /**
116
141
  * Declarative settings schema for generated configuration forms.
117
142
  *
@@ -195,15 +220,15 @@ export interface PluginDefinition {
195
220
  * should be omitted for utility plugins. The returned runtime becomes the
196
221
  * operator's active automation controller.
197
222
  */
198
- createStrategyRuntime?(ctx: PluginContext): StrategyRuntime;
223
+ createStrategyRuntime?(ctx: StrategyPluginContext): StrategyRuntime;
199
224
  /**
200
225
  * Runs after the plugin instance has been loaded and the context is ready.
201
226
  *
202
- * Use this for startup work such as warming caches, scheduling timers or
203
- * sending initial panel data. Keep it fast; long-running work should be
204
- * deferred or done asynchronously.
227
+ * Use this for startup work such as warming caches, scheduling Host timers or
228
+ * sending initial panel data. Await required asynchronous work before returning;
229
+ * do not detach continuations that retain Host capabilities after the callback.
205
230
  */
206
- onLoad?(ctx: PluginContext): void | Promise<void>;
231
+ onLoad?(ctx: PluginContextFor<Permissions>): void | Promise<void>;
207
232
  /**
208
233
  * Runs before the plugin instance is unloaded.
209
234
  *
@@ -211,13 +236,41 @@ export interface PluginDefinition {
211
236
  * handled through the host abstractions. Any timers created via
212
237
  * {@link PluginContext.timers} are cleared automatically by the host.
213
238
  */
214
- onUnload?(ctx: PluginContext): void | Promise<void>;
239
+ onUnload?(ctx: PluginCleanupContext): void | Promise<void>;
215
240
  /**
216
241
  * Event and pipeline hooks implemented by the plugin.
217
242
  *
218
243
  * Hooks let utility plugins observe or transform the message flow, and let the
219
244
  * active strategy participate in decision making.
220
245
  */
221
- hooks?: PluginHooks;
246
+ hooks?: PluginHooks<Permissions>;
247
+ /**
248
+ * Safety gate for the operator command port.
249
+ *
250
+ * Plugins that declare `operator:transmit-control` must implement this or
251
+ * {@link isAutoCallEnabled}. The host evaluates it immediately before each
252
+ * command so disabled remote-control or integration features cannot retain
253
+ * command authority.
254
+ */
255
+ isTransmitControlEnabled?(ctx: PluginEligibilityContext): boolean;
256
+ /**
257
+ * Marks an operator-scoped plugin as an automatic calling controller and
258
+ * reports whether that behavior is currently enabled.
259
+ *
260
+ * The host uses this declaration for the auto-call indicator and pause UI.
261
+ * It also acts as the command-port safety gate when
262
+ * {@link isTransmitControlEnabled} is omitted. Integrations that can submit
263
+ * occasional external commands but are not auto-call controllers should
264
+ * implement only {@link isTransmitControlEnabled}.
265
+ */
266
+ isAutoCallEnabled?(ctx: PluginEligibilityContext): boolean;
222
267
  }
268
+ /** Type-erased plugin definition used by the host after module loading. */
269
+ export type AnyPluginDefinition = PluginDefinition<any>;
270
+ /**
271
+ * Defines a plugin while preserving literal permissions for capability-aware
272
+ * callback context inference. New plugins should use this helper instead of
273
+ * widening their definition to `PluginDefinition`.
274
+ */
275
+ export declare function definePlugin<const Permissions extends readonly PluginPermission[] = readonly []>(definition: PluginDefinition<Permissions>): PluginDefinition<Permissions>;
223
276
  //# sourceMappingURL=definition.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAChB,UAAU,EACV,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;;;;OAOG;IACH,IAAI,EAAE,UAAU,CAAC;IAEjB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAEpC;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;IAEnD;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAEnC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAErC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,MAAM,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAEjC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE;QAAE,MAAM,EAAE,CAAC,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAA;KAAE,CAAC;IAEhD;;;;;;;;;;;;OAYG;IACH,EAAE,CAAC,EAAE;QACH,yEAAyE;QACzE,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,kCAAkC;QAClC,KAAK,CAAC,EAAE,sBAAsB,EAAE,CAAC;KAClC,CAAC;IAEF;;;;;;OAMG;IACH,qBAAqB,CAAC,CAAC,GAAG,EAAE,aAAa,GAAG,eAAe,CAAC;IAE5D;;;;;;OAMG;IACH,MAAM,CAAC,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD;;;;;OAKG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,9 @@
1
- export {};
1
+ /**
2
+ * Defines a plugin while preserving literal permissions for capability-aware
3
+ * callback context inference. New plugins should use this helper instead of
4
+ * widening their definition to `PluginDefinition`.
5
+ */
6
+ export function definePlugin(definition) {
7
+ return definition;
8
+ }
2
9
  //# sourceMappingURL=definition.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"definition.js","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"definition.js","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"AAoTA;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAE1B,UAAyC;IACzC,OAAO,UAAU,CAAC;AACpB,CAAC"}