@vestra-player/sdk 1.5.6 → 1.5.8

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.
@@ -73,9 +73,8 @@ export interface Disposable {
73
73
  * | `timeout` | A time limit ran out (`openStream` start-up, `tools.run`, `system.*` after 10 s). |
74
74
  * | `stream`, `device` | `openStream`: the producer failed / the audio device is not ready. |
75
75
  * | `backend-uncertain` | The player's backend blocked a filter stage; call `retry()`. |
76
- * | `cleanup` | {@link PackageTrial.stop} could not confirm cleanup; call it again. |
77
76
  * | `limit` | A player-wide limit was reached (for example the number of commands). |
78
- * | `reserved` | {@link Packages.validate}, {@link PackageCandidate.trial}, {@link PackageCandidate.install}: the id is `vestra` or starts with `vestra.` (any case), which only the player's built-in plugins use (player 0.4.1). |
77
+ * | `reserved` | {@link Packages.validate}, {@link PackageCandidate.install}: the id is `vestra` or starts with `vestra.` (any case), which only the player's built-in plugins use (player 0.4.1). |
79
78
  * | `versionMismatch` | The package needs a newer player (`minPlayerVersion`) or another `apiVersion`. |
80
79
  * | `ui-prepare` | The player could not prepare a mounted panel or a settings page (its Slint file does not compile, a bound property is not declared, or another plugin's panel holds the slot); `message` has the diagnostics. |
81
80
  * | `ui` | The player could not apply a property to a tool window or a settings page, or could not build the Options window (a property the Slint file does not declare, a value of the wrong type); `message` has Slint's reason. |
@@ -195,7 +194,7 @@ export interface Scope extends Disposable {
195
194
  readonly media: MediaCapture;
196
195
  /** Page-URL to stream-URL resolvers (`source.resolve`). */
197
196
  readonly resolvers: Resolvers;
198
- /** The installed plugins; validation, trials, installs (`plugins.manage` for changes). */
197
+ /** The installed plugins; validation, installs (`plugins.manage` for changes). */
199
198
  readonly packages: Packages;
200
199
  /** On-screen messages over the video (`ui.osd`). */
201
200
  readonly osd: Osd;
@@ -320,8 +319,7 @@ export interface Plugin extends Scope {
320
319
  readonly id: string;
321
320
  /**
322
321
  * A writable directory for this plugin's own files (settings, remembered
323
- * folders). Kept across restarts, disabling and upgrades; a trial gets a
324
- * temporary one. Write files atomically: to a temporary name, then rename.
322
+ * folders). Kept across restarts, disabling and upgrades. Write files atomically: to a temporary name, then rename.
325
323
  * Throws `unavailable` when the player provided none.
326
324
  *
327
325
  * Location: `~/Library/Application Support/Vestra/plugin-data/<id>/` on
@@ -3002,9 +3000,15 @@ export interface Stream extends Disposable {
3002
3000
  /**
3003
3001
  * The stream stopped for a reason outside this plugin: `error` is null when
3004
3002
  * another source replaced it or the user closed it, otherwise the failure
3005
- * (`stream`, `invalid` for a bad frame, …). Not called when the plugin
3006
- * itself stops it (`stop()`, `dispose()`, deactivation): the promise of
3007
- * `stop()` is that notification.
3003
+ * (`stream` with the player's text, `invalid` for a bad frame, …). Not
3004
+ * called when the plugin itself stops it (`stop()`, `dispose()`,
3005
+ * deactivation): the promise of `stop()` is that notification.
3006
+ *
3007
+ * The player retires a stream's producer as soon as it is replaced or
3008
+ * closed; the SDK reads the player's state to tell that from a failure
3009
+ * (before 1.5.7 a replaced or closed stream could get `unavailable`
3010
+ * "producer absent"). Without `playback.read` a failure the SDK has not
3011
+ * seen yet is reported as null.
3008
3012
  */
3009
3013
  onStop(listener: (error: PluginError | null) => void): Disposable;
3010
3014
  /**
@@ -3147,7 +3151,8 @@ export interface Player {
3147
3151
  * activation, pausing and acknowledgement. Frames travel over a local
3148
3152
  * connection the SDK sets up: a Unix socket on macOS and Linux, loopback
3149
3153
  * TCP with a token on Windows. Rejects with a {@link StreamStartError}
3150
- * (`timeout`), `stream` or `device`.
3154
+ * (`timeout`), `stream`, `device`, or `replaced` when another source
3155
+ * replaced it (or it was closed) before it ran.
3151
3156
  *
3152
3157
  * @example
3153
3158
  * ```ts
@@ -3937,6 +3942,14 @@ export interface FileResolverOptions {
3937
3942
  /**
3938
3943
  * What a file resolver returns: a stream, with the same frame contract as
3939
3944
  * {@link StreamOptions} (size limits, `frame()` on the audio clock).
3945
+ *
3946
+ * After `resolve` returns, the SDK starts the stream while the open waits.
3947
+ * If that fails, the open fails with the player's words instead of the
3948
+ * SDK's (the detail goes to the log): `device` "音频输出设备不可用",
3949
+ * `timeout` "启动超时", `busy` "播放器正忙,请稍后重试", other start-up
3950
+ * failures "无法启动播放". An error thrown by `frame()` keeps its message.
3951
+ * To give your own text for a file you can tell is unusable (a bad header),
3952
+ * check it in `resolve` and throw there.
3940
3953
  */
3941
3954
  export interface ResolvedStream {
3942
3955
  /** Always `"stream"`. */
@@ -3993,6 +4006,47 @@ export interface Resolvers {
3993
4006
  * error's message.
3994
4007
  */
3995
4008
  add(options: ResolverOptions): Resolver;
4009
+ /**
4010
+ * The extensions some plugin's file resolver handles (SDK 1.5.8, player
4011
+ * 0.4.2): lower case, without the dot, sorted, each once; not who
4012
+ * registered them. Reading needs no capability; `[]` from older players.
4013
+ * Without `events` it is the list from when the plugin first read it.
4014
+ * Use it to accept such files beside the player's own media types, for
4015
+ * example in a playlist's drop or folder scan.
4016
+ *
4017
+ * @example
4018
+ * ```ts
4019
+ * const MEDIA = ["mp4", "mkv", "mp3"];
4020
+ * const accepted = (path: string) => {
4021
+ * const extension = path.split(".").pop()?.toLowerCase() ?? "";
4022
+ * return MEDIA.includes(extension) || plugin.resolvers.fileTypes.includes(extension);
4023
+ * };
4024
+ * ```
4025
+ *
4026
+ * @capability None.
4027
+ * @player 0.4.2
4028
+ * @since 1.5.8
4029
+ */
4030
+ readonly fileTypes: readonly string[];
4031
+ /**
4032
+ * {@link Resolvers.fileTypes} changed: a file resolver was added, or its
4033
+ * plugin stopped. Needs `events`; without it the listener never fires.
4034
+ *
4035
+ * @param listener - Receives the new list.
4036
+ * @returns A handle that removes the listener.
4037
+ *
4038
+ * @example
4039
+ * ```ts
4040
+ * plugin.resolvers.onFileTypesChange((types) => {
4041
+ * void plugin.osd.show(`Also accepted: ${types.join(", ")}`);
4042
+ * });
4043
+ * ```
4044
+ *
4045
+ * @capability `events`
4046
+ * @player 0.4.2
4047
+ * @since 1.5.8
4048
+ */
4049
+ onFileTypesChange(listener: (types: string[]) => void): Disposable;
3996
4050
  }
3997
4051
  /**
3998
4052
  * The manifest of a package, as validated. The display fields and
@@ -4084,11 +4138,6 @@ export interface PackageCandidate {
4084
4138
  readonly manifest: PackageManifest;
4085
4139
  /** The checks the player ran. */
4086
4140
  readonly checks: string[];
4087
- /**
4088
- * Load it temporarily alongside the installed plugins. A reserved id
4089
- * (`vestra.*`) fails with `reserved`.
4090
- */
4091
- trial(): Promise<PackageTrial>;
4092
4141
  /**
4093
4142
  * Install it. The same source upgrades the installed plugin (its data
4094
4143
  * stays); another source fails with `conflict` unless `replace` is set;
@@ -4099,17 +4148,6 @@ export interface PackageCandidate {
4099
4148
  */
4100
4149
  install(options?: InstallOptions): Promise<PackageChange>;
4101
4150
  }
4102
- /** A trial load of a package. Disposing it stops the trial and waits for cleanup. */
4103
- export interface PackageTrial extends Disposable {
4104
- /** The trial's status as the player reports it. */
4105
- readonly status: string;
4106
- /**
4107
- * Stop and wait until the host confirmed cleanup. Rejects with code
4108
- * `cleanup` when the host stopped the trial without confirming cleanup;
4109
- * calling `stop()` again then asks the host again.
4110
- */
4111
- stop(): Promise<void>;
4112
- }
4113
4151
  /** The progress of an install, rollback or uninstall. */
4114
4152
  export type PackageChangeStatus = "preparing" | "activating" | "restoring" | "committed" | "cancelled" | "failed" | "failedDegraded" | "tooLate";
4115
4153
  /**
@@ -4134,8 +4172,8 @@ export interface PluginInfo {
4134
4172
  id: string;
4135
4173
  /** Version, `null` when unknown. */
4136
4174
  version: string | null;
4137
- /** From the install library, a `--plugin` development load, or a trial. */
4138
- origin: "installed" | "development" | "trial";
4175
+ /** From the install library, or a `--plugin` development load. */
4176
+ origin: "installed" | "development";
4139
4177
  /** Run state. */
4140
4178
  state: "starting" | "running" | "disabled" | "failed" | "stopped";
4141
4179
  /** Why it failed (`state: "failed"`). */
@@ -4151,7 +4189,7 @@ export interface PluginInfo {
4151
4189
  * replaced. `false` from players before 0.2.0.
4152
4190
  */
4153
4191
  builtin: boolean;
4154
- /** Where an installed plugin came from; `null` for development loads and trials. */
4192
+ /** Where an installed plugin came from; `null` for development loads. */
4155
4193
  source: PackageSource | null;
4156
4194
  /**
4157
4195
  * Its data directory (`plugin.dataDirectory` of that plugin); `null` when
@@ -4207,6 +4245,10 @@ export interface Packages {
4207
4245
  * Validate a package directory (`plugins.manage`). A package whose id is
4208
4246
  * reserved for the built-in plugins (`vestra` or `vestra.*`) fails with
4209
4247
  * `reserved` (player 0.4.1).
4248
+ *
4249
+ * The player keeps the 32 most recent candidates of all plugins; an
4250
+ * older one's `install` fails with `stale` (validate again). Before
4251
+ * player 0.4.2 the 33rd validation of a run failed with `busy` instead.
4210
4252
  */
4211
4253
  validate(directory: string): Promise<PackageCandidate>;
4212
4254
  /**
@@ -4225,7 +4267,7 @@ export interface Packages {
4225
4267
  /**
4226
4268
  * Stop or start a plugin (`plugins.manage`). An installed plugin stays
4227
4269
  * disabled across launches; a development load only for this run.
4228
- * `invalid` to disable oneself, `unsupported` for a trial, `missing` unknown.
4270
+ * `invalid` to disable oneself, `missing` unknown.
4229
4271
  * `busy` while it is being installed.
4230
4272
  */
4231
4273
  setEnabled(id: string, enabled: boolean): Promise<void>;
@@ -4233,7 +4275,7 @@ export interface Packages {
4233
4275
  * Remove an installed plugin (`plugins.manage`); `removeData` also deletes
4234
4276
  * its data directory, otherwise reinstalling it from the same source
4235
4277
  * reconnects to its data. Its user bindings stay (as orphans).
4236
- * `unsupported` for built-ins, development loads and trials, `missing`
4278
+ * `unsupported` for built-ins and development loads, `missing`
4237
4279
  * unknown, `invalid` for oneself.
4238
4280
  */
4239
4281
  uninstall(id: string, options?: {
@@ -19,6 +19,8 @@ export declare class WindowService {
19
19
  start(): void;
20
20
  /** The palette, as the `window.state` reply carried it (`plugin.appearance`). */
21
21
  appearance: string | undefined;
22
+ /** The file resolvers' extensions, as the reply carried them (`plugin.resolvers.fileTypes`). */
23
+ fileTypes: string[] | undefined;
22
24
  accept(facts: WindowFacts): void;
23
25
  read(): Promise<void>;
24
26
  watch(listener: (state: WindowState) => void): {
@@ -52,9 +52,13 @@ export class WindowService {
52
52
  }
53
53
  /** The palette, as the `window.state` reply carried it (`plugin.appearance`). */
54
54
  appearance;
55
+ /** The file resolvers' extensions, as the reply carried them (`plugin.resolvers.fileTypes`). */
56
+ fileTypes;
55
57
  accept(facts) {
56
58
  if (facts.appearance)
57
59
  this.appearance = facts.appearance;
60
+ if (Array.isArray(facts.fileTypes))
61
+ this.fileTypes = [...facts.fileTypes];
58
62
  const next = view(facts);
59
63
  if (JSON.stringify(next) === JSON.stringify(this.state))
60
64
  return;
package/dist/wire.d.ts CHANGED
@@ -25,12 +25,6 @@ export interface PackageValidation {
25
25
  checks: string[];
26
26
  trustedExecution: true;
27
27
  }
28
- export interface PackageTrial {
29
- trialId: number;
30
- activationOwner?: number;
31
- status: string;
32
- cleanupConfirmed?: boolean;
33
- }
34
28
  export interface InstalledPackage {
35
29
  revision: number;
36
30
  current: null | {
@@ -102,7 +96,7 @@ export declare class HostError extends Error {
102
96
  export interface PackageInfo {
103
97
  id: string;
104
98
  version: string | null;
105
- origin: "installed" | "development" | "trial";
99
+ origin: "installed" | "development";
106
100
  state: "starting" | "running" | "disabled" | "failed" | "stopped";
107
101
  error: string | null;
108
102
  capabilities: string[];
@@ -175,6 +169,8 @@ export interface WindowFacts {
175
169
  };
176
170
  /** The palette (`plugin.appearance`), with the `window.state` reply; a newer player may name a palette this SDK does not list. */
177
171
  appearance?: string;
172
+ /** The file resolvers' extensions (`plugin.resolvers.fileTypes`; player 0.4.2). */
173
+ fileTypes?: string[];
178
174
  }
179
175
  /** One timeline mark as the player merges them (`timeline.marks`). */
180
176
  export interface WireMark {
@@ -530,11 +526,6 @@ export declare function createWire(transport: Transport): {
530
526
  validate: (directory: string, apiVersion?: string) => Promise<PackageValidation>;
531
527
  /** Player 0.2.0: a `.vxp` file. */
532
528
  validateArchive: (file: string) => Promise<PackageValidation>;
533
- /** `requestId` recovers a lost first reply in this owner session. */
534
- trial: (token: string, requestId: string) => Promise<PackageTrial>;
535
- trialStateByRequest: (requestId: string) => Promise<PackageTrial>;
536
- trialState: (trialId: number) => Promise<PackageTrial>;
537
- stopTrial: (trialId: number) => Promise<PackageTrial>;
538
529
  installed: (id: string) => Promise<InstalledPackage>;
539
530
  /** `source` and `replace` are sent only when given (player 0.2.0). */
540
531
  install: (token: string, expectedRevision: number, requestId: string, options?: {
package/dist/wire.js CHANGED
@@ -258,16 +258,6 @@ export function createWire(transport) {
258
258
  validateArchive: (file) => transport.request("packages.validateArchive", {
259
259
  file,
260
260
  }),
261
- /** `requestId` recovers a lost first reply in this owner session. */
262
- trial: (token, requestId) => transport.request("packages.trial", {
263
- token,
264
- requestId,
265
- }),
266
- trialStateByRequest: (requestId) => transport.request("packages.trialStateByRequest", {
267
- requestId,
268
- }),
269
- trialState: (trialId) => transport.request("packages.trialState", { trialId }),
270
- stopTrial: (trialId) => transport.request("packages.stopTrial", { trialId }),
271
261
  installed: (id) => transport.request("packages.installed", { id }),
272
262
  /** `source` and `replace` are sent only when given (player 0.2.0). */
273
263
  install: (token, expectedRevision, requestId, options = {}) => transport.request("packages.install", {
package/llms-full.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @vestra-player/sdk 1.5.6: full reference
1
+ # @vestra-player/sdk 1.5.8: full reference
2
2
 
3
3
  > Everything needed to write a Vestra Player plugin (plugin API `vestra/1`): the guides, every public declaration of `@vestra-player/sdk` and `@vestra-player/sdk/testing` with its documentation, and the complete source of the example plugins. Generated from the SDK's sources by scripts/llms.mjs; the short index is llms.txt.
4
4
 
@@ -124,7 +124,7 @@ A manifest with every field. `id`, `apiVersion`, `entry` and `capabilities` are
124
124
  }
125
125
  ```
126
126
 
127
- Unknown fields, unknown capabilities and paths that leave the package directory (including through symbolic links) are rejected. The id's characters are ASCII letters, digits, `.`, `-` and `_`; the official plugin repository additionally requires `<GitHub user or organization>.<plugin name>`. The `vestra.` prefix (and the id `vestra`, in any case) is reserved for the plugins built into the player: from player 0.4.1 the player refuses to install or trial such a package (`reserved`). Every other plugin is a third-party plugin, including those hosted in the official repository.
127
+ Unknown fields, unknown capabilities and paths that leave the package directory (including through symbolic links) are rejected. The id's characters are ASCII letters, digits, `.`, `-` and `_`; the official plugin repository additionally requires `<GitHub user or organization>.<plugin name>`. The `vestra.` prefix (and the id `vestra`, in any case) is reserved for the plugins built into the player: from player 0.4.1 the player refuses to install such a package (`reserved`). Every other plugin is a third-party plugin, including those hosted in the official repository.
128
128
 
129
129
  | Capability | Opens |
130
130
  | -------------------------------------- | ------------------------------------------------------------------------------------------------------- |
@@ -998,9 +998,8 @@ change).
998
998
  | `timeout` | A time limit ran out (`openStream` start-up, `tools.run`, `system.*` after 10 s). |
999
999
  | `stream`, `device` | `openStream`: the producer failed / the audio device is not ready. |
1000
1000
  | `backend-uncertain` | The player's backend blocked a filter stage; call `retry()`. |
1001
- | `cleanup` | PackageTrial.stop could not confirm cleanup; call it again. |
1002
1001
  | `limit` | A player-wide limit was reached (for example the number of commands). |
1003
- | `reserved` | Packages.validate, PackageCandidate.trial, PackageCandidate.install: the id is `vestra` or starts with `vestra.` (any case), which only the player's built-in plugins use (player 0.4.1). |
1002
+ | `reserved` | Packages.validate, PackageCandidate.install: the id is `vestra` or starts with `vestra.` (any case), which only the player's built-in plugins use (player 0.4.1). |
1004
1003
  | `versionMismatch` | The package needs a newer player (`minPlayerVersion`) or another `apiVersion`. |
1005
1004
  | `ui-prepare` | The player could not prepare a mounted panel or a settings page (its Slint file does not compile, a bound property is not declared, or another plugin's panel holds the slot); `message` has the diagnostics. |
1006
1005
  | `ui` | The player could not apply a property to a tool window or a settings page, or could not build the Options window (a property the Slint file does not declare, a value of the wrong type); `message` has Slint's reason. |
@@ -1080,7 +1079,7 @@ interface Scope extends Disposable {
1080
1079
  readonly media: MediaCapture;
1081
1080
  /** Page-URL to stream-URL resolvers (`source.resolve`). */
1082
1081
  readonly resolvers: Resolvers;
1083
- /** The installed plugins; validation, trials, installs (`plugins.manage` for changes). */
1082
+ /** The installed plugins; validation, installs (`plugins.manage` for changes). */
1084
1083
  readonly packages: Packages;
1085
1084
  /** On-screen messages over the video (`ui.osd`). */
1086
1085
  readonly osd: Osd;
@@ -1174,8 +1173,7 @@ interface Plugin extends Scope {
1174
1173
  readonly id: string;
1175
1174
  /**
1176
1175
  * A writable directory for this plugin's own files (settings, remembered
1177
- * folders). Kept across restarts, disabling and upgrades; a trial gets a
1178
- * temporary one. Write files atomically: to a temporary name, then rename.
1176
+ * folders). Kept across restarts, disabling and upgrades. Write files atomically: to a temporary name, then rename.
1179
1177
  * Throws `unavailable` when the player provided none.
1180
1178
  *
1181
1179
  * Location: `~/Library/Application Support/Vestra/plugin-data/<id>/` on
@@ -2616,7 +2614,17 @@ interface Stream extends Disposable {
2616
2614
  /** The stream is producing frames. */
2617
2615
  readonly running: boolean;
2618
2616
  /**
2619
- * The stream stopped for a reason outside this plugin: `error` is null when another source replaced it or the user closed it, otherwise the failure (`stream`, `invalid` for a bad frame, …). Not called when the plugin itself stops it (`stop()`, `dispose()`, deactivation): the promise of `stop()` is that notification.
2617
+ * The stream stopped for a reason outside this plugin: `error` is null when
2618
+ * another source replaced it or the user closed it, otherwise the failure
2619
+ * (`stream` with the player's text, `invalid` for a bad frame, …). Not
2620
+ * called when the plugin itself stops it (`stop()`, `dispose()`,
2621
+ * deactivation): the promise of `stop()` is that notification.
2622
+ *
2623
+ * The player retires a stream's producer as soon as it is replaced or
2624
+ * closed; the SDK reads the player's state to tell that from a failure
2625
+ * (before 1.5.7 a replaced or closed stream could get `unavailable`
2626
+ * "producer absent"). Without `playback.read` a failure the SDK has not
2627
+ * seen yet is reported as null.
2620
2628
  */
2621
2629
  onStop(listener: (error: PluginError | null) => void): Disposable;
2622
2630
  /**
@@ -2758,7 +2766,8 @@ interface Player {
2758
2766
  * activation, pausing and acknowledgement. Frames travel over a local
2759
2767
  * connection the SDK sets up: a Unix socket on macOS and Linux, loopback
2760
2768
  * TCP with a token on Windows. Rejects with a StreamStartError
2761
- * (`timeout`), `stream` or `device`.
2769
+ * (`timeout`), `stream`, `device`, or `replaced` when another source
2770
+ * replaced it (or it was closed) before it ran.
2762
2771
  */
2763
2772
  openStream(options: StreamOptions): Promise<Stream>;
2764
2773
  /** Pause or resume. */
@@ -3389,6 +3398,14 @@ interface FileResolverOptions {
3389
3398
  What a file resolver returns: a stream, with the same frame contract as
3390
3399
  StreamOptions (size limits, `frame()` on the audio clock).
3391
3400
 
3401
+ After `resolve` returns, the SDK starts the stream while the open waits.
3402
+ If that fails, the open fails with the player's words instead of the
3403
+ SDK's (the detail goes to the log): `device` "音频输出设备不可用",
3404
+ `timeout` "启动超时", `busy` "播放器正忙,请稍后重试", other start-up
3405
+ failures "无法启动播放". An error thrown by `frame()` keeps its message.
3406
+ To give your own text for a file you can tell is unusable (a bad header),
3407
+ check it in `resolve` and throw there.
3408
+
3392
3409
  ```ts
3393
3410
  interface ResolvedStream {
3394
3411
  /** Always `"stream"`. */
@@ -3431,6 +3448,20 @@ interface Resolvers {
3431
3448
  * Register a resolver. When anyone opens a matching URL or file the player calls `resolve`; `signal` aborts when the open is cancelled or replaced. One scheme + host pair, and one extension, has one resolver (`conflict` otherwise). A rejected `resolve` fails the open with the error's message.
3432
3449
  */
3433
3450
  add(options: ResolverOptions): Resolver;
3451
+ /**
3452
+ * The extensions some plugin's file resolver handles (SDK 1.5.8, player 0.4.2): lower case, without the dot, sorted, each once; not who registered them. Reading needs no capability; `[]` from older players. Without `events` it is the list from when the plugin first read it. Use it to accept such files beside the player's own media types, for example in a playlist's drop or folder scan.
3453
+ * @capability None.
3454
+ * @player 0.4.2
3455
+ */
3456
+ readonly fileTypes: readonly string[];
3457
+ /**
3458
+ * Resolvers.fileTypes changed: a file resolver was added, or its plugin stopped. Needs `events`; without it the listener never fires.
3459
+ * @param listener - Receives the new list.
3460
+ * @returns A handle that removes the listener.
3461
+ * @capability `events`
3462
+ * @player 0.4.2
3463
+ */
3464
+ onFileTypesChange(listener: (types: string[]) => void): Disposable;
3434
3465
  }
3435
3466
  ```
3436
3467
 
@@ -3536,8 +3567,6 @@ interface PackageCandidate {
3536
3567
  readonly manifest: PackageManifest;
3537
3568
  /** The checks the player ran. */
3538
3569
  readonly checks: string[];
3539
- /** Load it temporarily alongside the installed plugins. A reserved id (`vestra.*`) fails with `reserved`. */
3540
- trial(): Promise<PackageTrial>;
3541
3570
  /**
3542
3571
  * Install it. The same source upgrades the installed plugin (its data stays); another source fails with `conflict` unless `replace` is set; a built-in id fails with `unsupported` (built-ins update with the player). A reserved id (`vestra` or `vestra.*`, which every built-in uses) fails with `reserved` (player 0.4.1; already when validated), as only the player installs built-in plugins.
3543
3572
  */
@@ -3545,21 +3574,6 @@ interface PackageCandidate {
3545
3574
  }
3546
3575
  ```
3547
3576
 
3548
- ### PackageTrial
3549
-
3550
- A trial load of a package. Disposing it stops the trial and waits for cleanup.
3551
-
3552
- ```ts
3553
- interface PackageTrial extends Disposable {
3554
- /** The trial's status as the player reports it. */
3555
- readonly status: string;
3556
- /**
3557
- * Stop and wait until the host confirmed cleanup. Rejects with code `cleanup` when the host stopped the trial without confirming cleanup; calling `stop()` again then asks the host again.
3558
- */
3559
- stop(): Promise<void>;
3560
- }
3561
- ```
3562
-
3563
3577
  ### PackageChangeStatus
3564
3578
 
3565
3579
  The progress of an install, rollback or uninstall.
@@ -3598,8 +3612,8 @@ interface PluginInfo {
3598
3612
  id: string;
3599
3613
  /** Version, `null` when unknown. */
3600
3614
  version: string | null;
3601
- /** From the install library, a `--plugin` development load, or a trial. */
3602
- origin: "installed" | "development" | "trial";
3615
+ /** From the install library, or a `--plugin` development load. */
3616
+ origin: "installed" | "development";
3603
3617
  /** Run state. */
3604
3618
  state: "starting" | "running" | "disabled" | "failed" | "stopped";
3605
3619
  /** Why it failed (`state: "failed"`). */
@@ -3612,7 +3626,7 @@ interface PluginInfo {
3612
3626
  previousVersion: string | null;
3613
3627
  /** Shipped with the player: it can be disabled, not uninstalled or replaced. `false` from players before 0.2.0. */
3614
3628
  builtin: boolean;
3615
- /** Where an installed plugin came from; `null` for development loads and trials. */
3629
+ /** Where an installed plugin came from; `null` for development loads. */
3616
3630
  source: PackageSource | null;
3617
3631
  /** Its data directory (`plugin.dataDirectory` of that plugin); `null` when the player has none. The path's layout is not part of the API. */
3618
3632
  dataDirectory: string | null;
@@ -3648,7 +3662,13 @@ need no capability; everything else needs `plugins.manage`.
3648
3662
  ```ts
3649
3663
  interface Packages {
3650
3664
  /**
3651
- * Validate a package directory (`plugins.manage`). A package whose id is reserved for the built-in plugins (`vestra` or `vestra.*`) fails with `reserved` (player 0.4.1).
3665
+ * Validate a package directory (`plugins.manage`). A package whose id is
3666
+ * reserved for the built-in plugins (`vestra` or `vestra.*`) fails with
3667
+ * `reserved` (player 0.4.1).
3668
+ *
3669
+ * The player keeps the 32 most recent candidates of all plugins; an
3670
+ * older one's `install` fails with `stale` (validate again). Before
3671
+ * player 0.4.2 the 33rd validation of a run failed with `busy` instead.
3652
3672
  */
3653
3673
  validate(directory: string): Promise<PackageCandidate>;
3654
3674
  /**
@@ -3662,11 +3682,11 @@ interface Packages {
3662
3682
  /** The new list after any start, stop, failure, install or uninstall. No capability. */
3663
3683
  onChange(listener: (plugins: PluginInfo[]) => void): Disposable;
3664
3684
  /**
3665
- * Stop or start a plugin (`plugins.manage`). An installed plugin stays disabled across launches; a development load only for this run. `invalid` to disable oneself, `unsupported` for a trial, `missing` unknown. `busy` while it is being installed.
3685
+ * Stop or start a plugin (`plugins.manage`). An installed plugin stays disabled across launches; a development load only for this run. `invalid` to disable oneself, `missing` unknown. `busy` while it is being installed.
3666
3686
  */
3667
3687
  setEnabled(id: string, enabled: boolean): Promise<void>;
3668
3688
  /**
3669
- * Remove an installed plugin (`plugins.manage`); `removeData` also deletes its data directory, otherwise reinstalling it from the same source reconnects to its data. Its user bindings stay (as orphans). `unsupported` for built-ins, development loads and trials, `missing` unknown, `invalid` for oneself.
3689
+ * Remove an installed plugin (`plugins.manage`); `removeData` also deletes its data directory, otherwise reinstalling it from the same source reconnects to its data. Its user bindings stay (as orphans). `unsupported` for built-ins and development loads, `missing` unknown, `invalid` for oneself.
3670
3690
  */
3671
3691
  uninstall(id: string, options?: {
3672
3692
  removeData?: boolean;
@@ -4260,6 +4280,10 @@ interface FakeProducer {
4260
4280
  videoFrames: number;
4261
4281
  /** `true` once the plugin released the producer (stream stopped or disposed). */
4262
4282
  released: boolean;
4283
+ /**
4284
+ * `true` once the player retired the producer, as the real player does at once: another source replaced it (`open`, another stream, a resolved file), the source was closed, or TestHost.failStream failed it. A retired producer's `producer.state` answers `unavailable`.
4285
+ */
4286
+ retired: boolean;
4263
4287
  /**
4264
4288
  * `true` if the frame connection closed while this stream was still the player's source: the player reports that as a playback failure ("producer transport closed"). `stop()` closes the source first.
4265
4289
  */
@@ -4698,6 +4722,12 @@ class TestHost implements Transport {
4698
4722
  /** Exact host names it resolves (a web resolver), with `schemes`. */
4699
4723
  hosts?: string[];
4700
4724
  }[];
4725
+ /** What `plugin.resolvers.fileTypes` reports: the extensions of this plugin's file resolvers and of TestHost.otherResolvers, sorted, each once. */
4726
+ fileTypes(): string[];
4727
+ /**
4728
+ * Push `resolvers.fileTypes` when TestHost.fileTypes changed: called after this plugin's resolvers come and go; call it yourself after changing TestHost.otherResolvers (another plugin started or stopped).
4729
+ */
4730
+ updateFileTypes(): void;
4701
4731
  /**
4702
4732
  * File opens waiting for this plugin's file resolver (`extensions`), by request id: the path and how it was opened. Opening a matching path (`player.open`, TestHost.openElsewhere) sends `source.resolveRequest`; the answer selects the stream, fails the open or, once the source changed, is `stale`. Any later source change cancels it (`source.resolveCancel`). Web resolvers are not simulated: emit `source.resolveRequest` yourself.
4703
4733
  */
@@ -4854,6 +4884,12 @@ class TestHost implements Transport {
4854
4884
  * @param phase - The phase to end in (default `"ready"`).
4855
4885
  */
4856
4886
  openElsewhere(source: string, phase?: SourceState["phase"]): void;
4887
+ /**
4888
+ * Simulate a generated stream failing in the player (its audio device lost, its frame connection broken), like the player: the source fails with `message` (`phase: "error"`) and the producer is retired at once. The stream's `onStop` listeners get a `stream` error with `message`.
4889
+ * @param producerId - The producer, from TestHost.producers.
4890
+ * @param message - The player's failure text.
4891
+ */
4892
+ failStream(producerId: number, message: string): void;
4857
4893
  /**
4858
4894
  * Press a binding like the host's table would: panel-focus bindings first when `panel` is true, then global ones; the earliest registration wins a conflict. Resolves after the command ran; rejects when nothing is bound or the command is disabled or busy. Bindings use the reference syntax (`"Space"`, `"Ctrl+O"`, `"Mouse:WheelUp"`); the player's core actions match first, and user overrides in `bindings.overlay` apply.
4859
4895
  */
@@ -4876,7 +4912,7 @@ class TestHost implements Transport {
4876
4912
  /** Its version, `null` when unknown. */
4877
4913
  version: string | null;
4878
4914
  /** Where it was loaded from. */
4879
- origin: "installed" | "development" | "trial";
4915
+ origin: "installed" | "development";
4880
4916
  /** Its run state. */
4881
4917
  state: "starting" | "running" | "disabled" | "failed" | "stopped";
4882
4918
  /** Why it failed (`state: "failed"`). */
@@ -5030,7 +5066,7 @@ own sensible display cap well below `perPlugin`.
5030
5066
 
5031
5067
  ```ts
5032
5068
  const COMMAND_LIMITS: {
5033
- /** Commands one plugin instance may have at the same time (160). A trial package is its own instance. */
5069
+ /** Commands one plugin instance may have at the same time (160). */
5034
5070
  readonly perPlugin: 160;
5035
5071
  /** Commands of every plugin plus the player's own, together (1024). */
5036
5072
  readonly total: 1024;
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.5.6: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls; it runs in its own Node.js process. llms-full.txt, next to this file, has every declaration, the guides and the examples; L<n> below are its line numbers.
3
+ > @vestra-player/sdk 1.5.8: write plugins for Vestra Player in TypeScript (plugin API `vestra/1`). A plugin is a folder with `manifest.json` and an entry module whose default export `activate(plugin)` the player calls; it runs in its own Node.js process. llms-full.txt, next to this file, has every declaration, the guides and the examples; L<n> below are its line numbers.
4
4
 
5
5
  **How plugins run**
6
6
 
@@ -136,7 +136,7 @@ test("shows the position", async () => {
136
136
  - `aborted`: An `AbortSignal` fired
137
137
  - `timeout`: A time limit ran out
138
138
  - `limit`: A player-wide limit was reached
139
- - `reserved`: Packages.validate, PackageCandidate.trial, PackageCandidate.install: the id is `vestra` or starts with `vestra.`, which only the player's built-in plugins use
139
+ - `reserved`: Packages.validate, PackageCandidate.install: the id is `vestra` or starts with `vestra.`, which only the player's built-in plugins use
140
140
  - `versionMismatch`: The package needs a newer player or another `apiVersion`
141
141
  - `ui-prepare`: The player could not prepare a mounted panel or a settings page
142
142
  - `ui`: The player could not apply a property to a tool window or a settings page, or could not build the Options window
@@ -150,54 +150,54 @@ test("shows the position", async () => {
150
150
  - [Concept: Windows, panels, settings pages, dialogs and the OSD](https://vestrasys.github.io/vestra-player-sdk/documents/Windows__panels__settings_pages__dialogs_and_the_OSD.html): L281–626
151
151
  - [How-to: Keep a slider responsive while dragging](https://vestrasys.github.io/vestra-player-sdk/documents/Keep_a_slider_responsive_while_dragging.html): L627–801
152
152
  - [How-to: Remember a window's state between runs](https://vestrasys.github.io/vestra-player-sdk/documents/Remember_a_window_s_state_between_runs.html): L802–931
153
- - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L932–4153
154
- - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html) (TestHost, startPlugin): L4154–5084
153
+ - [API: @vestra-player/sdk](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk.html): L932–4173
154
+ - [API: @vestra-player/sdk/testing](https://vestrasys.github.io/vestra-player-sdk/modules/_vestra-player_sdk_testing.html) (TestHost, startPlugin): L4174–5120
155
155
 
156
156
  ## API
157
157
 
158
158
  Where each module of `plugin` is declared:
159
159
 
160
- - Plugin L1152, Scope L1023, PluginError L974, Disposable L947
161
- - plugin.commands: Commands L1349
162
- - plugin.windows: Windows L1554
163
- - plugin.panels: Panels L2005
164
- - plugin.dialogs: Dialogs L2287
165
- - plugin.player: Player L2716
166
- - plugin.video: VideoFilter L2964, VideoView L3027, RenderOptions L3069
167
- - plugin.audio: AudioFilter L2988
168
- - plugin.subtitles: Subtitles L3167
169
- - plugin.media: MediaCapture L3325
170
- - plugin.resolvers: Resolvers L3422
171
- - plugin.packages: Packages L3643
172
- - plugin.osd: Osd L2901
173
- - plugin.host: Host L2864
174
- - plugin.clipboard: Clipboard L3750
175
- - plugin.power: Power L3770
176
- - plugin.tools: Tools L3803
177
- - plugin.storage: Storage L3846
178
- - plugin.system: System L3869
179
- - plugin.secrets: Secrets L3887
180
- - plugin.window: MainWindow L2128
181
- - plugin.app: App L2209
182
- - plugin.bindings: Bindings L3720
183
- - plugin.ui: Ui L1441
184
- - plugin.timeline: Timeline L2085
185
- - plugin.drops: Drops L1851
186
- - plugin.appearance: Appearance L1880
187
- - plugin.settings: Settings L1938
160
+ - Plugin L1151, Scope L1022, PluginError L974, Disposable L947
161
+ - plugin.commands: Commands L1347
162
+ - plugin.windows: Windows L1552
163
+ - plugin.panels: Panels L2003
164
+ - plugin.dialogs: Dialogs L2285
165
+ - plugin.player: Player L2724
166
+ - plugin.video: VideoFilter L2973, VideoView L3036, RenderOptions L3078
167
+ - plugin.audio: AudioFilter L2997
168
+ - plugin.subtitles: Subtitles L3176
169
+ - plugin.media: MediaCapture L3334
170
+ - plugin.resolvers: Resolvers L3439
171
+ - plugin.packages: Packages L3657
172
+ - plugin.osd: Osd L2910
173
+ - plugin.host: Host L2873
174
+ - plugin.clipboard: Clipboard L3770
175
+ - plugin.power: Power L3790
176
+ - plugin.tools: Tools L3823
177
+ - plugin.storage: Storage L3866
178
+ - plugin.system: System L3889
179
+ - plugin.secrets: Secrets L3907
180
+ - plugin.window: MainWindow L2126
181
+ - plugin.app: App L2207
182
+ - plugin.bindings: Bindings L3740
183
+ - plugin.ui: Ui L1439
184
+ - plugin.timeline: Timeline L2083
185
+ - plugin.drops: Drops L1849
186
+ - plugin.appearance: Appearance L1878
187
+ - plugin.settings: Settings L1936
188
188
 
189
189
  ## Examples
190
190
 
191
191
  Complete plugins with tests, in [examples/](https://github.com/vestrasys/vestra-player-sdk/tree/main/examples) and in llms-full.txt:
192
192
 
193
- - hello: The README Quick start: one menu command and an on-screen message. L5498–5587
194
- - blur: A tool window with one slider that blurs the playing video. L5588–5849
195
- - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L5850–6182
196
- - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L6183–6367
197
- - panel-menu: A sidebar panel with its own right-click menu. L6368–6573
198
- - menus: Radio menu items, separator groups and a menu item whose title changes. L6574–6733
199
- - theme-widgets: A tool window drawn with the player's theme and widgets. L6734–6897
200
- - markdown: A window that shows a Markdown document. L6898–7091
193
+ - hello: The README Quick start: one menu command and an on-screen message. L5534–5623
194
+ - blur: A tool window with one slider that blurs the playing video. L5624–5885
195
+ - signal-generator: Generates its own video and audio: colour bars and a 1 kHz tone. L5886–6218
196
+ - bookmark: A tool window bound to the playback state: remember the position and jump back to it. L6219–6403
197
+ - panel-menu: A sidebar panel with its own right-click menu. L6404–6609
198
+ - menus: Radio menu items, separator groups and a menu item whose title changes. L6610–6769
199
+ - theme-widgets: A tool window drawn with the player's theme and widgets. L6770–6933
200
+ - markdown: A window that shows a Markdown document. L6934–7127
201
201
 
202
202
  ## Optional
203
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.5.6",
3
+ "version": "1.5.8",
4
4
  "description": "Plugin API types, test host and plugin CLI for Vestra Player (apiVersion vestra/1)",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {