@vestra-player/sdk 1.5.54 → 1.5.55

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.5.55
4
+
5
+ **Who must change: nobody, but check `TestHost` tests that update a disabled plugin.** Additive: `InstallOptions` gains `enabled` and `TestHost` gains `installKeepsDisabled`, `apiVersion` stays `vestra/1`, no new capability, and existing code still compiles. Older players ignore the option, and an install there always enables the plugin. One `TestHost` behaviour change at run time: like player 0.6.4, it now keeps a `disabled` entry disabled when the plugin under test installs an update or reinstall over it without `enabled`, where it used to make it `running`. A test that expects the update to run (the player's Store test did) sets `host.installKeepsDisabled = false` to keep playing players up to 0.6.3, or asserts the new rule; a test that covers both runs once with each.
6
+
7
+ - **Choose whether an installed plugin runs** (`candidate.install({ enabled })`). Planned for player 0.6.4 (not released yet), where an update or reinstall (from any source, `replace` included) keeps the installed plugin's state when `enabled` is left out: a plugin the user or a store disabled stays disabled, and only its files are installed. A first install runs as before. `true` enables and starts it, for a store that disabled a revoked version and installs the update that fixes it (if the new version fails to start, the old one is restored, still disabled); `false` installs it disabled, stopping a running version and dropping its session values as `setEnabled(id, false)` does. Built-in plugins always run. The SDK sends `enabled` only when given and refuses anything but a boolean with `invalid` before sending. On players up to 0.6.3 the option is ignored and every install runs; call `packages.setEnabled` after the install is committed when the state matters there.
8
+ - **`TestHost`** models player 0.6.4 by default: a first install is `running`, an update of a `disabled` entry stays `disabled` unless `enabled: true`, `enabled: false` installs it `disabled`, and a non-boolean `enabled` on the wire is `invalid` (`null` reads as absent, as in the player). `installKeepsDisabled = false` plays players up to 0.6.3 instead: every install is `running` and `enabled` is ignored. `packageCalls` records `install`'s `enabled` when it was given.
9
+ - **Stop reasons, corrected** (SDK 1.5.54 docs, from the player's implementation for 0.6.4, still planned). `upgrade` also covers a new version stopped because its install was cancelled (the previous version comes back). `idle` comes only with lazy activation, which is off by default (`VESTRA_LAZY=1`), and also parks a plugin whose first run it judges lazy right after it is ready, not only after a while without use. `failed` is not sent for a full input queue: an own process is killed, and a member's full queue restarts the shared runner as a whole, as does an unreadable line from the runner itself. In those cases, and when an own process fails to activate or the process has already exited, the plugin gets no stop at all and its `onDispose` cleanups do not run. `host-lost` is not proof of a crash: player 0.6.4 sends `quit` on a normal exit, while an older player could close its pipes first when its window closed. [How a plugin runs › Restarts](docs/explanation/plugins.md#restarts-activate-can-run-more-than-once) now states as fact that plugins sharing one process are restarted together (player 0.5.6 and later). `StopReason`, [The plugin model](docs/explanation/plugins.md#stopping) and [Host and Node](docs/explanation/host-and-node.md#stopping-a-plugin) say so.
10
+ - _Docs_: `InstallOptions.enabled`, `PackageCandidate.install` (with an example; the `replace` example no longer promises a running plugin), `TestHost.candidates` and `TestHost.installKeepsDisabled` describe it.
11
+
3
12
  ## 1.5.54
4
13
 
5
14
  **Who must change: nobody, but check cleanup functions passed directly to `onDispose`.** Additive: `Plugin.stopReason`, the types `StopReason` and `DisposeInfo`, an optional argument for `onDispose` cleanups, `TestHost.previousCrash`, an optional `code` for `TestHost.failVideoShaders` and `stop({ reason })` / `disconnect()` from `startPlugin`; `apiVersion` stays `vestra/1`, no new capability, and existing code still compiles. One runtime difference: with this SDK in the player (or in `TestHost`) an `onDispose` cleanup is called with one argument, the `DisposeInfo`. A function that takes optional parameters of its own and is passed directly now receives it as its first argument: `plugin.onDispose(controller.abort.bind(controller))` aborts with the `DisposeInfo` as the abort reason, `onDispose(save)` with `save(force?: boolean)` gets an object as `force`. Wrap such a function (`() => controller.abort()`). The stop reasons are planned for player 0.6.4; `previous-crash` comes from player 0.6.3.
Binary file
@@ -83,9 +83,11 @@ function installOptions(options) {
83
83
  return {};
84
84
  if (typeof options !== "object" || options === null)
85
85
  throw failure("invalid", "install options must be an object");
86
- const { source, replace } = options;
86
+ const { source, replace, enabled } = options;
87
87
  if (replace !== undefined && typeof replace !== "boolean")
88
88
  throw failure("invalid", "replace must be a boolean");
89
+ if (enabled !== undefined && typeof enabled !== "boolean")
90
+ throw failure("invalid", "enabled must be a boolean");
89
91
  if (source !== undefined) {
90
92
  const kinds = ["repository", "link", "file", "workshop"];
91
93
  if (typeof source !== "object" ||
@@ -99,6 +101,7 @@ function installOptions(options) {
99
101
  return {
100
102
  ...(source === undefined ? {} : { source: { ...source } }),
101
103
  ...(replace ? { replace: true } : {}),
104
+ ...(enabled === undefined ? {} : { enabled }),
102
105
  };
103
106
  }
104
107
  function candidate(scope, directory, checked) {
@@ -2436,7 +2436,7 @@ export declare class TestHost implements Transport {
2436
2436
  method: "setEnabled" | "reload" | "uninstall" | "install";
2437
2437
  /** The target plugin id. */
2438
2438
  id: string;
2439
- /** `setEnabled`'s value. */
2439
+ /** `setEnabled`'s value, or `install`'s `enabled` option when given. */
2440
2440
  enabled?: boolean;
2441
2441
  /** `uninstall`'s `removeData` option. */
2442
2442
  removeData?: boolean;
@@ -2450,7 +2450,13 @@ export declare class TestHost implements Transport {
2450
2450
  * `.vxp` file) → its manifest. Other paths fail with `missing`. `install`
2451
2451
  * adds or upgrades the entry in {@link TestHost.packages} following the
2452
2452
  * player's source rules: the same source upgrades, another source needs
2453
- * `replace` (`conflict` otherwise), a built-in id is `unsupported`.
2453
+ * `replace` (`conflict` otherwise), a built-in id is `unsupported`. As
2454
+ * player 0.6.4 (planned), the installed entry's `state` follows
2455
+ * {@link "@vestra-player/sdk"!InstallOptions.enabled}: a first install
2456
+ * runs, an update of a `disabled` entry stays `disabled` unless `enabled`
2457
+ * is `true`, and `enabled: false` installs it `disabled`. Set
2458
+ * {@link TestHost.installKeepsDisabled} to `false` for the older players'
2459
+ * rule (every install runs).
2454
2460
  *
2455
2461
  * @example Offer a package file the plugin can install
2456
2462
  * The plugin can validate and install `/downloads/someone.demo-1.0.0.vxp`
@@ -2467,6 +2473,32 @@ export declare class TestHost implements Transport {
2467
2473
  * ```
2468
2474
  */
2469
2475
  readonly candidates: Map<string, PackageManifest>;
2476
+ /**
2477
+ * Which player's install rule `packages.install` plays. `true` (default,
2478
+ * SDK 1.5.55): player 0.6.4 (planned), where an update or reinstall
2479
+ * keeps a `disabled` plugin disabled unless the plugin passes
2480
+ * {@link "@vestra-player/sdk"!InstallOptions.enabled}. `false`: players
2481
+ * up to 0.6.3, where every install runs the plugin and `enabled` is
2482
+ * ignored (it is still checked and recorded in
2483
+ * {@link TestHost.packageCalls}). A test written for the older rule that
2484
+ * updates a disabled plugin sets it to `false`; one that keeps testing
2485
+ * both runs once with each.
2486
+ *
2487
+ * @example Keep a test written for players up to 0.6.3
2488
+ * The plugin under test installs an update over a plugin the user
2489
+ * disabled and expects it to run afterwards, as players up to 0.6.3 do.
2490
+ * With `false` the host plays that rule; the default plays player 0.6.4,
2491
+ * where the update stays disabled.
2492
+ * ```ts
2493
+ * declare const host: TestHost;
2494
+ * // ---cut---
2495
+ * host.installKeepsDisabled = false; // every install runs, as before 0.6.4
2496
+ * ```
2497
+ *
2498
+ * @capability None.
2499
+ * @since 1.5.55
2500
+ */
2501
+ installKeepsDisabled: boolean;
2470
2502
  /**
2471
2503
  * Remote images (`plugin.ui.markdown`, `plugin.ui.remoteImage`): address
2472
2504
  * → a local PNG or JPEG file served for it, or `null` for a failing
@@ -3099,7 +3099,13 @@ export class TestHost {
3099
3099
  * `.vxp` file) → its manifest. Other paths fail with `missing`. `install`
3100
3100
  * adds or upgrades the entry in {@link TestHost.packages} following the
3101
3101
  * player's source rules: the same source upgrades, another source needs
3102
- * `replace` (`conflict` otherwise), a built-in id is `unsupported`.
3102
+ * `replace` (`conflict` otherwise), a built-in id is `unsupported`. As
3103
+ * player 0.6.4 (planned), the installed entry's `state` follows
3104
+ * {@link "@vestra-player/sdk"!InstallOptions.enabled}: a first install
3105
+ * runs, an update of a `disabled` entry stays `disabled` unless `enabled`
3106
+ * is `true`, and `enabled: false` installs it `disabled`. Set
3107
+ * {@link TestHost.installKeepsDisabled} to `false` for the older players'
3108
+ * rule (every install runs).
3103
3109
  *
3104
3110
  * @example Offer a package file the plugin can install
3105
3111
  * The plugin can validate and install `/downloads/someone.demo-1.0.0.vxp`
@@ -3116,6 +3122,32 @@ export class TestHost {
3116
3122
  * ```
3117
3123
  */
3118
3124
  candidates = new Map();
3125
+ /**
3126
+ * Which player's install rule `packages.install` plays. `true` (default,
3127
+ * SDK 1.5.55): player 0.6.4 (planned), where an update or reinstall
3128
+ * keeps a `disabled` plugin disabled unless the plugin passes
3129
+ * {@link "@vestra-player/sdk"!InstallOptions.enabled}. `false`: players
3130
+ * up to 0.6.3, where every install runs the plugin and `enabled` is
3131
+ * ignored (it is still checked and recorded in
3132
+ * {@link TestHost.packageCalls}). A test written for the older rule that
3133
+ * updates a disabled plugin sets it to `false`; one that keeps testing
3134
+ * both runs once with each.
3135
+ *
3136
+ * @example Keep a test written for players up to 0.6.3
3137
+ * The plugin under test installs an update over a plugin the user
3138
+ * disabled and expects it to run afterwards, as players up to 0.6.3 do.
3139
+ * With `false` the host plays that rule; the default plays player 0.6.4,
3140
+ * where the update stays disabled.
3141
+ * ```ts
3142
+ * declare const host: TestHost;
3143
+ * // ---cut---
3144
+ * host.installKeepsDisabled = false; // every install runs, as before 0.6.4
3145
+ * ```
3146
+ *
3147
+ * @capability None.
3148
+ * @since 1.5.55
3149
+ */
3150
+ installKeepsDisabled = true;
3119
3151
  /**
3120
3152
  * Remote images (`plugin.ui.markdown`, `plugin.ui.remoteImage`): address
3121
3153
  * → a local PNG or JPEG file served for it, or `null` for a failing
@@ -5105,6 +5137,10 @@ export class TestHost {
5105
5137
  if (source.kind === "bundled")
5106
5138
  throw new HostError("invalid", "source bundled is reserved");
5107
5139
  const replace = p.replace === true;
5140
+ // As player 0.6.4: `enabled` is a boolean, or absent (`null` too).
5141
+ const requested = p.enabled ?? undefined;
5142
+ if (requested !== undefined && typeof requested !== "boolean")
5143
+ throw new HostError("invalid", "enabled must be a boolean");
5108
5144
  const entry = this.packages.find((each) => each.id === manifest.id);
5109
5145
  if (entry?.builtin)
5110
5146
  throw new HostError("unsupported", "built-in plugins are updated with the player");
@@ -5120,12 +5156,19 @@ export class TestHost {
5120
5156
  id: manifest.id,
5121
5157
  source,
5122
5158
  replace,
5159
+ ...(requested === undefined ? {} : { enabled: requested }),
5123
5160
  });
5161
+ // As player 0.6.4: a first install runs; an update or reinstall (any
5162
+ // source, `replace` included) keeps a disabled plugin disabled unless
5163
+ // `enabled` is given. Players up to 0.6.3 run every install.
5164
+ const enabled = !this.installKeepsDisabled
5165
+ ? true
5166
+ : (requested ?? entry?.state !== "disabled");
5124
5167
  const next = {
5125
5168
  id: manifest.id,
5126
5169
  version: manifest.version,
5127
5170
  origin: "installed",
5128
- state: "running",
5171
+ state: enabled ? "running" : "disabled",
5129
5172
  error: null,
5130
5173
  capabilities: [...manifest.capabilities],
5131
5174
  directory: `/test-host/packages/${manifest.id}`,
@@ -592,15 +592,33 @@ export interface Plugin extends Scope {
592
592
  * - `uninstall`: the user uninstalls it.
593
593
  * - `reload`: it is reloaded (from the plugin manager, or a development
594
594
  * load with `--plugin`).
595
- * - `upgrade`: a new version replaces it.
596
- * - `idle`: lazy activation stops it after a while without use; its next
597
- * use starts it again ([Idle plugins](../../docs/explanation/plugins.md#idle-plugins-are-stopped)).
598
- * - `failed`: the player stops it for a fault (planned to cover an
599
- * unreadable or oversized line, a malformed request, an input queue that
600
- * overflowed, an uncaught exception in the shared process).
595
+ * - `upgrade`: an install or update replaces the running version; also a
596
+ * new version stopped because its install was cancelled (the previous
597
+ * version comes back).
598
+ * - `idle`: lazy activation parks it; its next use starts it again
599
+ * ([Idle plugins](../../docs/explanation/plugins.md#idle-plugins-are-stopped)).
600
+ * Lazy activation is off by default (`VESTRA_LAZY=1` turns it on); when
601
+ * on, it parks a plugin after a while without use, and parks one whose
602
+ * first run it judges lazy right after it is ready.
603
+ * - `failed`: the player stops it for a fault it can still tell the plugin
604
+ * about (planned to cover a malformed request, an unreadable or
605
+ * oversized line, an activation timeout, an uncaught exception the
606
+ * shared process attributes to this plugin).
601
607
  * - `host-lost`: the plugin's runner saw its pipe to the player close
602
608
  * without a stop: the player crashed or was killed. Requests are refused
603
- * with `disabled`; clean up locally only.
609
+ * with `disabled`; clean up locally only. Player 0.6.4 sends `quit` on a
610
+ * normal exit; read `host-lost` as "the player is gone", not as proof of
611
+ * a crash.
612
+ *
613
+ * Some ends are no stop at all: the plugin gets no reason and its
614
+ * {@link Scope.onDispose} cleanups do not run. That is the case when its
615
+ * own process is killed because its input queue is full or a write to it
616
+ * failed, when its own process fails to activate (the runner exits without
617
+ * cleaning up), when the process has already exited, and when the shared
618
+ * runner is restarted as a whole (a hang, a fault it cannot attribute, an
619
+ * unreadable line of its own, a member's full input queue, the runner
620
+ * exiting): every member is killed with it. Keep state that must survive
621
+ * such an end in storage as it changes, not only in a cleanup.
604
622
  *
605
623
  * Planned for player 0.6.4: the player sends the reason with
606
624
  * `runtime.stop` and its runner reports `host-lost`; the exact list may
@@ -8516,6 +8534,27 @@ export interface InstallOptions {
8516
8534
  * reconnects to it.
8517
8535
  */
8518
8536
  replace?: boolean;
8537
+ /**
8538
+ * Whether the plugin runs once installed (SDK 1.5.55). Left out, a first
8539
+ * install runs and an update or reinstall (from any source, `replace`
8540
+ * included) keeps the installed plugin's state: one the user or a store
8541
+ * disabled stays disabled, and only its files are installed (nothing
8542
+ * starts it, so the change commits without an `activating` step). `true`
8543
+ * enables and starts it, for example a version the store disabled that
8544
+ * an update fixes; if it fails to start, the old version is restored,
8545
+ * still disabled. `false` installs it disabled: a running version is
8546
+ * stopped and its session values go, as with {@link Packages.setEnabled}.
8547
+ * Built-in plugins always run (and cannot be installed over). A value
8548
+ * that is not a boolean is `invalid`.
8549
+ *
8550
+ * Planned for player 0.6.4: older players ignore it and an install there
8551
+ * always runs the plugin, so after a committed install call
8552
+ * {@link Packages.setEnabled} when the state matters.
8553
+ *
8554
+ * @player 0.6.4 (planned); older players ignore it (an install always
8555
+ * enables the plugin).
8556
+ */
8557
+ enabled?: boolean;
8519
8558
  }
8520
8559
  /**
8521
8560
  * A validated package directory, from {@link Packages.validate}.
@@ -8560,9 +8599,14 @@ export interface PackageCandidate {
8560
8599
  * a built-in id fails with `unsupported` (built-ins update with the
8561
8600
  * player). A reserved id (`vestra` or `vestra.*`, which every built-in
8562
8601
  * uses) fails with `reserved` (player 0.4.1; already when validated), as
8563
- * only the player installs built-in plugins.
8602
+ * only the player installs built-in plugins. An update keeps a disabled
8603
+ * plugin disabled unless `enabled` says otherwise (planned for player
8604
+ * 0.6.4; see {@link InstallOptions.enabled}).
8564
8605
  *
8565
8606
  * @capability plugins.manage
8607
+ * @throws `invalid` (rejects) An option is malformed: a `source` the
8608
+ * player does not accept, or a `replace` or `enabled` that is not a
8609
+ * boolean.
8566
8610
  * @throws `conflict` (rejects) The plugin is installed from another source
8567
8611
  * and `replace` is not set.
8568
8612
  * @throws `unsupported` (rejects) A built-in plugin's id.
@@ -8585,7 +8629,19 @@ export interface PackageCandidate {
8585
8629
  * declare const candidate: PackageCandidate;
8586
8630
  * // ---cut---
8587
8631
  * const change = await candidate.install({ replace: true });
8588
- * await change.done; // "committed" once the new plugin runs
8632
+ * await change.done; // "committed" once installed (running unless it stays disabled)
8633
+ * ```
8634
+ *
8635
+ * @example Enable a fixed version of a plugin the store disabled
8636
+ * A store disabled a version it revoked; the update that fixes it
8637
+ * should run again. Without `enabled` the update keeps the plugin
8638
+ * disabled (player 0.6.4); `true` enables it once installed. Older
8639
+ * players ignore the option and enable every install.
8640
+ * ```ts
8641
+ * declare const candidate: PackageCandidate;
8642
+ * // ---cut---
8643
+ * const change = await candidate.install({ enabled: true });
8644
+ * await change.done; // "committed" once the fixed version runs
8589
8645
  * ```
8590
8646
  *
8591
8647
  * @since 1.0.0
package/dist/wire.d.ts CHANGED
@@ -731,10 +731,15 @@ export declare function createWire(transport: Transport): {
731
731
  /** Player 0.2.0: a `.vxp` file. */
732
732
  validateArchive: (file: string) => Promise<PackageValidation>;
733
733
  installed: (id: string) => Promise<InstalledPackage>;
734
- /** `source` and `replace` are sent only when given (player 0.2.0). */
734
+ /**
735
+ * `source` and `replace` are sent only when given (player 0.2.0);
736
+ * `enabled` only when given (planned for player 0.6.4, older players
737
+ * ignore it).
738
+ */
735
739
  install: (token: string, expectedRevision: number, requestId: string, options?: {
736
740
  source?: WireSource;
737
741
  replace?: boolean;
742
+ enabled?: boolean;
738
743
  }) => Promise<PackageOperation>;
739
744
  operation: (operationId: string) => Promise<PackageOperation>;
740
745
  cancel: (operationId: string) => Promise<PackageOperation>;
package/dist/wire.js CHANGED
@@ -341,13 +341,20 @@ export function createWire(transport) {
341
341
  file,
342
342
  }),
343
343
  installed: (id) => transport.request("packages.installed", { id }),
344
- /** `source` and `replace` are sent only when given (player 0.2.0). */
344
+ /**
345
+ * `source` and `replace` are sent only when given (player 0.2.0);
346
+ * `enabled` only when given (planned for player 0.6.4, older players
347
+ * ignore it).
348
+ */
345
349
  install: (token, expectedRevision, requestId, options = {}) => transport.request("packages.install", {
346
350
  token,
347
351
  expectedRevision,
348
352
  requestId,
349
353
  ...(options.source === undefined ? {} : { source: options.source }),
350
354
  ...(options.replace ? { replace: true } : {}),
355
+ ...(options.enabled === undefined
356
+ ? {}
357
+ : { enabled: options.enabled }),
351
358
  }),
352
359
  operation: (operationId) => transport.request("packages.operation", {
353
360
  operationId,
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vestra Player SDK
2
2
 
3
- > @vestra-player/sdk 1.5.54: 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. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
3
+ > @vestra-player/sdk 1.5.55: 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. The documentation is a tree of short pages read by path, like `go doc`: the same pages and API as https://vestrasys.github.io/vestra-player-sdk/.
4
4
 
5
5
  ## Read the documentation
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestra-player/sdk",
3
- "version": "1.5.54",
3
+ "version": "1.5.55",
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": {