@deck-shelves/host 1.2.0 → 1.3.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 CHANGED
@@ -11,13 +11,15 @@
11
11
  [![npm version](https://img.shields.io/npm/v/@deck-shelves/host?logo=npm&color=cb3837)](https://www.npmjs.com/package/@deck-shelves/host)
12
12
  [![npm downloads](https://img.shields.io/npm/dt/@deck-shelves/host?label=downloads&logo=npm&color=blue)](https://www.npmjs.com/package/@deck-shelves/host)
13
13
  [![npm monthly](https://img.shields.io/npm/dm/@deck-shelves/host?label=monthly&logo=npm&color=blue)](https://www.npmjs.com/package/@deck-shelves/host)
14
- [![Tests](https://img.shields.io/badge/tests-2%20passed-brightgreen?logo=vitest&logoColor=white)](src/contract/index.test.ts)
14
+ [![Tests](https://img.shields.io/badge/tests-7%20passed-brightgreen?logo=vitest&logoColor=white)](src/contract/index.test.ts)
15
15
  [![Types](https://img.shields.io/npm/types/@deck-shelves/host?logo=typescript&logoColor=white)](src/contract/index.ts)
16
16
  [![Bundle size](https://img.shields.io/bundlephobia/minzip/@deck-shelves/host?label=minzip&color=blue)](https://bundlephobia.com/package/@deck-shelves/host)
17
17
  [![License](https://img.shields.io/npm/l/@deck-shelves/host?color=blue)](LICENSE)
18
18
  [![Node](https://img.shields.io/node/v/@deck-shelves/host?logo=node.js&logoColor=white)](package.json)
19
- [![Platform](https://img.shields.io/badge/platform-SteamOS%20%C2%B7%20Linux%20%C2%B7%20Windows-purple?logo=steamdeck&logoColor=white)](https://github.com/ValveSoftware/SteamOS)
19
+ [![Platform](https://img.shields.io/badge/platform-SteamOS%20%C2%B7%20Linux%20%C2%B7%20macOS%20%C2%B7%20Windows-purple?logo=steamdeck&logoColor=white)](https://github.com/ValveSoftware/SteamOS)
20
20
  [![Deck Shelves](https://img.shields.io/badge/host-Deck%20Shelves-purple)](https://github.com/santojon/Deck-Shelves)
21
+ [![Forks](https://img.shields.io/github/forks/santojon/Deck-Shelves-HOST?style=flat&color=blue)](https://github.com/santojon/Deck-Shelves-HOST/network/members)
22
+ [![Clones](https://img.shields.io/endpoint?url=https%3A%2F%2Fsantojon.github.io%2FDeck-Shelves%2Fstats%2Fclones-host.json)](https://github.com/santojon/Deck-Shelves-HOST/graphs/traffic)
21
23
  [![Sponsor](https://img.shields.io/badge/Sponsor-GitHub-ea4aaa?logo=github&logoColor=white)](https://github.com/sponsors/santojon)
22
24
  [![Ko-fi](https://img.shields.io/badge/Support%20me%20on%20Ko--fi-F16061?logo=ko-fi&logoColor=white)](https://ko-fi.com/santojon)
23
25
 
@@ -51,7 +53,7 @@ host↔bundle contract, a different audience.
51
53
 
52
54
  | Path | What it is |
53
55
  |---|---|
54
- | `src/contract/` | **`HostApi` types** (`HOST_API_VERSION`, `lifecycle`, `rpc`, `ui`, `routes`, `notifications`, `platform`, optional `qam`). The single source of truth both hosts and the bundle build against. |
56
+ | `src/contract/` | **`HostApi` types** (`HOST_API_VERSION`, `lifecycle`, `rpc`, `ui`, `routes`, `notifications`, `platform`, optional `React`/`ReactDOM`/`jsx`, `qam`, `mainMenu`, `updates`). The single source of truth both hosts and the bundle build against. |
55
57
 
56
58
  ## Usage
57
59
 
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  // src/contract/index.ts
4
- var HOST_API_VERSION = "1.1.0";
4
+ var HOST_API_VERSION = "1.2.0";
5
5
 
6
6
  exports.HOST_API_VERSION = HOST_API_VERSION;
package/dist/index.d.cts CHANGED
@@ -6,16 +6,26 @@
6
6
  * adapter fulfils this shape, so the bundle's call sites depend only on it
7
7
  * and never on a specific host's UI library directly.
8
8
  *
9
- * Additive-only Optional UI-surface capabilities are additive
10
- * namespaces a host implements *if it can* and the bundle feature-detects:
9
+ * Optional capabilities are additive members a host implements *if it can* and
10
+ * the bundle feature-detects:
11
11
  * - `qam` — a Quick Access Menu tab.
12
12
  * - `mainMenu` — an entry in the Steam Main Menu / left rail.
13
+ * - `updates` — self-install of plugin updates (see `HostUpdates`).
14
+ * - `React` / `ReactDOM` / `jsx` — the host's React stack, so the bundle's
15
+ * react shims can resolve React from the host instead of a loader global.
13
16
  * The contract names no concrete host; a host may implement any, all, or none.
14
17
  *
15
18
  * Dependency-free by design (mirrors `@deck-shelves/api`): supporting data
16
19
  * types are inlined.
17
20
  */
18
- declare const HOST_API_VERSION: "1.1.0";
21
+ /**
22
+ * Compatibility version of the contract SHAPE — independent of this package's
23
+ * npm release version. Bump semver here on shape changes: MINOR for additive,
24
+ * backward-compatible members (a new optional namespace / field); MAJOR on a
25
+ * breaking change. 1.2.0 added the optional `updates` namespace and the
26
+ * optional `React`/`ReactDOM`/`jsx` UI-surface members.
27
+ */
28
+ declare const HOST_API_VERSION: "1.2.0";
19
29
  interface PluginDescriptor {
20
30
  name: string;
21
31
  version: string;
@@ -177,6 +187,20 @@ interface HostMainMenu {
177
187
  */
178
188
  registerEntry(entry: MainMenuEntry): () => void;
179
189
  }
190
+ /** Optional: a host that can obtain + apply plugin updates itself (no manual
191
+ * file install). A loader that can only hand the user a file does NOT implement
192
+ * this — the bundle then falls back to the manual download flow. */
193
+ interface HostUpdates {
194
+ /** True if this host can self-install (drives the button: "Install" vs "Download"). */
195
+ canSelfInstall(): boolean;
196
+ /** Obtain the release and swap it in, then reload. `assetUrl`/`assetName` point
197
+ * at the bundle artifact the host injects (the IIFE). */
198
+ applyUpdate(release: {
199
+ version: string;
200
+ assetUrl?: string;
201
+ assetName?: string;
202
+ }): Promise<void>;
203
+ }
180
204
  /**
181
205
  * What the host process provides to the Deck Shelves bundle. The bundle receives
182
206
  * this at startup as `window.__SHELVES_HOST__` and uses it to register itself,
@@ -195,6 +219,18 @@ interface HostApi {
195
219
  readonly lifecycle: HostLifecycle;
196
220
  readonly rpc: HostRpc;
197
221
  readonly ui: HostUi;
222
+ /**
223
+ * Steam's React stack, exposed so a bundle's `react` / `react-dom` /
224
+ * `jsx-runtime` shims can resolve React from the host instead of from
225
+ * loader-published globals — the path a *sole* host (no loader) needs, since
226
+ * no loader is present to publish them. Kept dependency-free: cast to
227
+ * `React` / `ReactDOM` / the jsx-runtime at the call site, as with {@link HostUi}.
228
+ * Optional — a loader-backed host may omit them and let the bundle fall back to
229
+ * the loader's own React globals.
230
+ */
231
+ readonly React?: unknown;
232
+ readonly ReactDOM?: unknown;
233
+ readonly jsx?: unknown;
198
234
  readonly routes: HostRoutes;
199
235
  readonly notifications?: HostNotifications;
200
236
  readonly platform: PlatformApi;
@@ -203,6 +239,9 @@ interface HostApi {
203
239
  /** Optional Main Menu (left-rail) surface; present only on hosts that
204
240
  * support it. Injected/shown only while it has content (see `HostMainMenu`). */
205
241
  readonly mainMenu?: HostMainMenu;
242
+ /** Optional self-update surface; present only on hosts that can obtain and
243
+ * apply an update themselves (see `HostUpdates`). */
244
+ readonly updates?: HostUpdates;
206
245
  }
207
246
  /** Shape of the runtime global the host installs in the renderer. */
208
247
  type ShelvesHostGlobal = HostApi;
@@ -227,7 +266,19 @@ declare global {
227
266
  * `__SHELVES_QAM__`. Equivalent to calling `registerPanel` for each.
228
267
  */
229
268
  __SHELVES_QAM_PENDING__?: QamPanel[];
269
+ /**
270
+ * Tab-ownership handshake. A host stamps this with its owner kind (e.g.
271
+ * `"shelveshub"`) the moment its own Deck Shelves QAM tab is actually
272
+ * inserted into the strip — NOT when `__SHELVES_QAM__` is first created
273
+ * (that happens at boot, before any tab exists). A bundle running under a
274
+ * different loader that also renders its own native tab retracts it once
275
+ * this is set, so exactly one Deck Shelves tab survives and it is the
276
+ * host's. Because it is stamped only on real insertion, a host that never
277
+ * inserts leaves the bundle's own tab in place as the fallback rather than
278
+ * both vanishing. Unset means no host has claimed the tab.
279
+ */
280
+ __SHELVES_QAM_OWNER__?: string;
230
281
  }
231
282
  }
232
283
 
233
- export { type Disposable, HOST_API_VERSION, type HostApi, type HostLifecycle, type HostMainMenu, type HostNotifications, type HostQam, type HostRoutes, type HostRpc, type HostUi, type MainMenuEntry, type PlatformApi, type PlatformAppMeta, type PlatformCollection, type PlatformTab, type PluginDescriptor, type QamPanel, type ShelfSource, type ShelvesHostGlobal, type ToastOptions };
284
+ export { type Disposable, HOST_API_VERSION, type HostApi, type HostLifecycle, type HostMainMenu, type HostNotifications, type HostQam, type HostRoutes, type HostRpc, type HostUi, type HostUpdates, type MainMenuEntry, type PlatformApi, type PlatformAppMeta, type PlatformCollection, type PlatformTab, type PluginDescriptor, type QamPanel, type ShelfSource, type ShelvesHostGlobal, type ToastOptions };
package/dist/index.d.ts CHANGED
@@ -6,16 +6,26 @@
6
6
  * adapter fulfils this shape, so the bundle's call sites depend only on it
7
7
  * and never on a specific host's UI library directly.
8
8
  *
9
- * Additive-only Optional UI-surface capabilities are additive
10
- * namespaces a host implements *if it can* and the bundle feature-detects:
9
+ * Optional capabilities are additive members a host implements *if it can* and
10
+ * the bundle feature-detects:
11
11
  * - `qam` — a Quick Access Menu tab.
12
12
  * - `mainMenu` — an entry in the Steam Main Menu / left rail.
13
+ * - `updates` — self-install of plugin updates (see `HostUpdates`).
14
+ * - `React` / `ReactDOM` / `jsx` — the host's React stack, so the bundle's
15
+ * react shims can resolve React from the host instead of a loader global.
13
16
  * The contract names no concrete host; a host may implement any, all, or none.
14
17
  *
15
18
  * Dependency-free by design (mirrors `@deck-shelves/api`): supporting data
16
19
  * types are inlined.
17
20
  */
18
- declare const HOST_API_VERSION: "1.1.0";
21
+ /**
22
+ * Compatibility version of the contract SHAPE — independent of this package's
23
+ * npm release version. Bump semver here on shape changes: MINOR for additive,
24
+ * backward-compatible members (a new optional namespace / field); MAJOR on a
25
+ * breaking change. 1.2.0 added the optional `updates` namespace and the
26
+ * optional `React`/`ReactDOM`/`jsx` UI-surface members.
27
+ */
28
+ declare const HOST_API_VERSION: "1.2.0";
19
29
  interface PluginDescriptor {
20
30
  name: string;
21
31
  version: string;
@@ -177,6 +187,20 @@ interface HostMainMenu {
177
187
  */
178
188
  registerEntry(entry: MainMenuEntry): () => void;
179
189
  }
190
+ /** Optional: a host that can obtain + apply plugin updates itself (no manual
191
+ * file install). A loader that can only hand the user a file does NOT implement
192
+ * this — the bundle then falls back to the manual download flow. */
193
+ interface HostUpdates {
194
+ /** True if this host can self-install (drives the button: "Install" vs "Download"). */
195
+ canSelfInstall(): boolean;
196
+ /** Obtain the release and swap it in, then reload. `assetUrl`/`assetName` point
197
+ * at the bundle artifact the host injects (the IIFE). */
198
+ applyUpdate(release: {
199
+ version: string;
200
+ assetUrl?: string;
201
+ assetName?: string;
202
+ }): Promise<void>;
203
+ }
180
204
  /**
181
205
  * What the host process provides to the Deck Shelves bundle. The bundle receives
182
206
  * this at startup as `window.__SHELVES_HOST__` and uses it to register itself,
@@ -195,6 +219,18 @@ interface HostApi {
195
219
  readonly lifecycle: HostLifecycle;
196
220
  readonly rpc: HostRpc;
197
221
  readonly ui: HostUi;
222
+ /**
223
+ * Steam's React stack, exposed so a bundle's `react` / `react-dom` /
224
+ * `jsx-runtime` shims can resolve React from the host instead of from
225
+ * loader-published globals — the path a *sole* host (no loader) needs, since
226
+ * no loader is present to publish them. Kept dependency-free: cast to
227
+ * `React` / `ReactDOM` / the jsx-runtime at the call site, as with {@link HostUi}.
228
+ * Optional — a loader-backed host may omit them and let the bundle fall back to
229
+ * the loader's own React globals.
230
+ */
231
+ readonly React?: unknown;
232
+ readonly ReactDOM?: unknown;
233
+ readonly jsx?: unknown;
198
234
  readonly routes: HostRoutes;
199
235
  readonly notifications?: HostNotifications;
200
236
  readonly platform: PlatformApi;
@@ -203,6 +239,9 @@ interface HostApi {
203
239
  /** Optional Main Menu (left-rail) surface; present only on hosts that
204
240
  * support it. Injected/shown only while it has content (see `HostMainMenu`). */
205
241
  readonly mainMenu?: HostMainMenu;
242
+ /** Optional self-update surface; present only on hosts that can obtain and
243
+ * apply an update themselves (see `HostUpdates`). */
244
+ readonly updates?: HostUpdates;
206
245
  }
207
246
  /** Shape of the runtime global the host installs in the renderer. */
208
247
  type ShelvesHostGlobal = HostApi;
@@ -227,7 +266,19 @@ declare global {
227
266
  * `__SHELVES_QAM__`. Equivalent to calling `registerPanel` for each.
228
267
  */
229
268
  __SHELVES_QAM_PENDING__?: QamPanel[];
269
+ /**
270
+ * Tab-ownership handshake. A host stamps this with its owner kind (e.g.
271
+ * `"shelveshub"`) the moment its own Deck Shelves QAM tab is actually
272
+ * inserted into the strip — NOT when `__SHELVES_QAM__` is first created
273
+ * (that happens at boot, before any tab exists). A bundle running under a
274
+ * different loader that also renders its own native tab retracts it once
275
+ * this is set, so exactly one Deck Shelves tab survives and it is the
276
+ * host's. Because it is stamped only on real insertion, a host that never
277
+ * inserts leaves the bundle's own tab in place as the fallback rather than
278
+ * both vanishing. Unset means no host has claimed the tab.
279
+ */
280
+ __SHELVES_QAM_OWNER__?: string;
230
281
  }
231
282
  }
232
283
 
233
- export { type Disposable, HOST_API_VERSION, type HostApi, type HostLifecycle, type HostMainMenu, type HostNotifications, type HostQam, type HostRoutes, type HostRpc, type HostUi, type MainMenuEntry, type PlatformApi, type PlatformAppMeta, type PlatformCollection, type PlatformTab, type PluginDescriptor, type QamPanel, type ShelfSource, type ShelvesHostGlobal, type ToastOptions };
284
+ export { type Disposable, HOST_API_VERSION, type HostApi, type HostLifecycle, type HostMainMenu, type HostNotifications, type HostQam, type HostRoutes, type HostRpc, type HostUi, type HostUpdates, type MainMenuEntry, type PlatformApi, type PlatformAppMeta, type PlatformCollection, type PlatformTab, type PluginDescriptor, type QamPanel, type ShelfSource, type ShelvesHostGlobal, type ToastOptions };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // src/contract/index.ts
2
- var HOST_API_VERSION = "1.1.0";
2
+ var HOST_API_VERSION = "1.2.0";
3
3
 
4
4
  export { HOST_API_VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deck-shelves/host",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Deck Shelves host contract — the HostApi types all host adapters and the bundle build against.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -75,12 +75,12 @@
75
75
  }
76
76
  },
77
77
  "devDependencies": {
78
- "@typescript-eslint/parser": "^8.67.0",
78
+ "@typescript-eslint/parser": "^8.70.0",
79
79
  "eslint": "^9.39.5",
80
80
  "jsdom": "^25.0.1",
81
81
  "tsup": "^8.5.1",
82
82
  "typescript": "^5.9.3",
83
- "vitest": "^3.2.7"
83
+ "vitest": "^4.1.11"
84
84
  },
85
85
  "packageManager": "pnpm@10.33.0"
86
86
  }