@phreshos/client 0.1.18 → 0.1.20

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
@@ -15,26 +15,27 @@ provide its runtime environment.
15
15
 
16
16
  It uses the domain objects and shared contracts from `@phreshos/core` through
17
17
  a peer dependency. It does not redefine those objects, own server-side
18
- capabilities, or contain host and transport implementations.
19
-
20
- Its `Host` contract exposes only desktop-session capabilities: the public
21
- system Theme, desktop and pointer state, publicly served values, and
22
- unrestricted server-side Fetch. `host.theme.snapshot()` explicitly and
23
- asynchronously reads the current snapshot retained by the desktop host.
24
- `host.theme.subscribe("change", listener)` is an ordinary live subscription:
25
- it receives only complete replacements published after registration, with no
26
- initial value or replay. The Client cannot write the authoritative value. The
27
- Host does not expose system-wide Program or Process discovery.
18
+ capabilities, or contain System and transport implementations.
19
+
20
+ Its `System` contract exposes only desktop-session capabilities: read-only
21
+ System Appearance, the mutable Theme local to this Desktop, desktop and pointer
22
+ state, publicly served values, and unrestricted server-side Fetch.
23
+ `system.appearance.snapshot()` reads complete unresolved authoritative state.
24
+ `system.theme.snapshot()` reads only the effective `"light" | "dark"` mode;
25
+ `update("default")` resumes following the native environment. Both expose
26
+ ordinary live-only `change` subscriptions with no initial replay. A Client
27
+ cannot write authoritative Appearance or discover system-wide Programs and
28
+ Processes.
28
29
 
29
30
  Desktop and pointer access are independent objects rather than events merged
30
- into Host:
31
+ into System:
31
32
 
32
33
  ```ts
33
- const desktop = await host.desktop.size()
34
- const stopDesktop = host.desktop.subscribe("resize", next => undefined)
34
+ const desktop = await system.desktop.size()
35
+ const stopDesktop = system.desktop.subscribe("resize", next => undefined)
35
36
 
36
- const position = await host.pointer.position()
37
- const stopPointer = host.pointer.subscribe("move", next => undefined)
37
+ const position = await system.pointer.position()
38
+ const stopPointer = system.pointer.subscribe("move", next => undefined)
38
39
  ```
39
40
 
40
41
  The desktop size is the complete desktop area containing this Client,
@@ -44,7 +45,7 @@ movement both require the `pointer` permission. Reads are asynchronous
44
45
  requests; subscriptions receive only future publications and never replay a
45
46
  retained value.
46
47
 
47
- Permission decisions belong to the current Program, not to the desktop Host.
48
+ Permission decisions belong to the current Program, not to the desktop System.
48
49
  They are available from a Program handle and flattened through `current` as the
49
50
  same canonical capability:
50
51
 
@@ -61,13 +62,13 @@ current.permission === program.permission
61
62
  `granted()` reads the effective decision without prompting. `request()` asks
62
63
  only when no known decision can answer immediately and returns `null` if its
63
64
  deadline expires. The desktop remains the internal enforcement boundary, but
64
- permission ownership does not alter the public shape of `host`.
65
+ permission ownership does not alter the public shape of `system`.
65
66
 
66
- The Client Host exposes only direct, exact Service handles. Creating a handle
67
+ The Client System exposes only direct, exact Service handles. Creating a handle
67
68
  does not read or start the Service and exposes no Service registry:
68
69
 
69
70
  ```ts
70
- const service = host.service({
71
+ const service = system.service({
71
72
  program: "counter",
72
73
  endpoint: "server",
73
74
  name: "state"
@@ -115,7 +116,7 @@ absence or incarnation loss rejects without turning the boundary into a waiter.
115
116
  The package provides two contextual runtime entry points:
116
117
 
117
118
  ```ts
118
- import { host, current } from "@phreshos/client"
119
+ import { system, current } from "@phreshos/client"
119
120
  ```
120
121
 
121
122
  It also re-exports the shared Core runtime classes—`Program`, `Process`,
@@ -137,12 +138,12 @@ reserved for directed publications, questions, and answers.
137
138
 
138
139
  Messages sent by an Endpoint in the same Program contain that real Endpoint in
139
140
  `from`. If the sender belongs to another Program, `from` is `null`; the foreign
140
- identity is removed by the server host before the message reaches the desktop.
141
+ identity is removed by the authoritative System before the message reaches the desktop.
141
142
  The same rule applies to Client-visible traffic destinations.
142
143
 
143
144
  Client-visible Program handles can operate only within their own Program.
144
145
  They deliberately omit `install()` and `fork()`, and their `Storage` values never
145
- expose host filesystem paths. Every Client-side Window handle exposes the same
146
+ expose native filesystem paths. Every Client-side Window handle exposes the same
146
147
  authoritative, subscribable Window capability and its `window.local` physical
147
148
  representation on this desktop. Local reads and updates have no events and do
148
149
  not change Server state. `current.window` is only convenient access to the
@@ -177,30 +178,26 @@ them through one authoritative request and produces one `geometry` event.
177
178
  Calling `move()` and `resize()` sequentially or through `Promise.all()` remains
178
179
  two independent operations and can expose an intermediate state remotely.
179
180
 
180
- An `under` or `over` representation may request one local host-rendered Surface:
181
+ An `under` or `over` representation may request one local system-rendered Surface:
181
182
 
182
183
  ```ts
183
- await current.window.local.surface.set({
184
- opacity: 0.65,
185
- radius: "large"
186
- }, { duration: 240, easing: "ease-out", wait: true })
184
+ await current.window.local.surface.set({ duration: 240, easing: "ease-out", wait: true })
187
185
 
188
- await current.window.local.surface.remove()
186
+ await current.window.local.surface.remove({ duration: 180, easing: "ease-in" })
189
187
  ```
190
188
 
191
189
  The desktop holds local state only for the lifetime of that iframe
192
190
  representation. Reloading or destroying it resets the representation from
193
191
  authoritative truth, while other desktops remain unaffected. Program code may
194
- synchronize desired settings through its Server and explicitly apply them
195
- again. `set()` with no settings creates a sharp, fully opaque Surface.
196
- Opacity is a finite number from `0` through `1`; zero retains the Surface node.
197
- Radius accepts a nonnegative pixel number, a Theme-derived `ScaleLevel`, or
198
- `"full"`. Only `remove()` restores exact `null` and immediately removes the
199
- node. The optional transaction uses milliseconds and a stable named or cubic
200
- Bézier easing; the desktop performs the motion, honors reduced motion, and does
201
- does not restore anything when a new iframe representation begins. The sharp
192
+ synchronize desired presence through its Server and explicitly apply it
193
+ again. `set()` and `remove()` accept only a required `VisibilityTransition`;
194
+ Programs cannot configure the System Surface's material, opacity, or radius.
195
+ The transition uses milliseconds and a stable named or cubic Bézier easing;
196
+ the desktop performs the motion, honors reduced motion, and removes the Surface
197
+ only after its exit transition completes. `wait: true` makes the request settle
198
+ with that transition. A new iframe representation restores nothing. The
202
199
  container follows the iframe geometry while the independently rounded Surface
203
- neither clips nor masks Client content. `window` and `wallpaper` layers reject
200
+ neither clips nor masks Client content. The ordinary `window` layer rejects
204
201
  the capability.
205
202
 
206
203
  `program.icon()` requests the current Program's guaranteed PNG `Blob` on
@@ -0,0 +1,15 @@
1
+ import Events from "./events.js";
2
+ /** Read-only System Appearance reached through the Desktop boundary. */
3
+ export default class ClientAppearance extends Events {
4
+ constructor();
5
+ snapshot(): Promise<Readonly<{
6
+ background: import("@phreshos/core").ThemedValue<string, string>;
7
+ foreground: import("@phreshos/core").ThemedValue<string, string>;
8
+ accent: import("@phreshos/core").ThemedValue<string, string>;
9
+ spacing: import("@phreshos/core").ThemedValue<number>;
10
+ radius: import("@phreshos/core").ThemedValue<number>;
11
+ surface: import("@phreshos/core").ThemedValue<import("@phreshos/core").AppearanceSurface, import("@phreshos/core").AppearanceSurface>;
12
+ signInWallpaper: import("@phreshos/core").ThemedValue<string | null, string | null>;
13
+ desktopWallpaper: import("@phreshos/core").ThemedValue<string | null, string | null>;
14
+ }>>;
15
+ }
@@ -0,0 +1,18 @@
1
+ import { createAppearanceSnapshot } from "@phreshos/core";
2
+ import Events from "./events.js";
3
+ import wire from "./wire.js";
4
+ /** Read-only System Appearance reached through the Desktop boundary. */
5
+ export default class ClientAppearance extends Events {
6
+ constructor() {
7
+ super((event, listener, impossible) => wire.on("host-appearance", event, value => {
8
+ listener(createAppearanceSnapshot(value));
9
+ }, null, impossible), observer => wire.onAll("host-appearance", (event, value) => {
10
+ if (typeof event === "string")
11
+ observer(event, createAppearanceSnapshot(value));
12
+ }));
13
+ }
14
+ async snapshot() {
15
+ const [appearance] = await wire.request(["appearance"]);
16
+ return createAppearanceSnapshot(appearance);
17
+ }
18
+ }
package/dist/desktop.d.ts CHANGED
@@ -11,7 +11,7 @@ export type DesktopEvents = {
11
11
  resize: DesktopSize;
12
12
  };
13
13
  /** Explicit desktop size reads and future resizes. */
14
- export interface HostDesktop extends Subscribable<DesktopEvents, never> {
14
+ export interface SystemDesktop extends Subscribable<DesktopEvents, never> {
15
15
  /** Reads the complete current desktop area in CSS pixels. */
16
16
  size(): Promise<DesktopSize>;
17
17
  }
package/dist/desktop.js CHANGED
@@ -17,7 +17,7 @@ export default class ClientDesktop extends Events {
17
17
  const answer = await wire.request(["desktop"]);
18
18
  const size = createSize(answer[0]);
19
19
  if (!size)
20
- throw new Error("The host returned an invalid desktop size");
20
+ throw new Error("The system returned an invalid desktop size");
21
21
  return size;
22
22
  }
23
23
  }
package/dist/domain.js CHANGED
@@ -308,10 +308,12 @@ class LocalWindowSurfaceHandle {
308
308
  constructor(target) {
309
309
  this.target = target;
310
310
  }
311
- async set(settings = {}, transaction) {
312
- await wire.request(["windowLocalSurfaceSet", await this.target(), settings, transaction]);
311
+ async set(transition) {
312
+ await wire.request(["windowLocalSurfaceSet", await this.target(), transition]);
313
+ }
314
+ async remove(transition) {
315
+ await wire.request(["windowLocalSurfaceRemove", await this.target(), transition]);
313
316
  }
314
- async remove() { await wire.request(["windowLocalSurfaceRemove", await this.target()]); }
315
317
  }
316
318
  function deferredScoped(route, target, convert) {
317
319
  return [
@@ -408,7 +410,7 @@ function lifecycleEndpoint(record, kind) {
408
410
  return owner.server;
409
411
  if (kind === "client")
410
412
  return owner.client;
411
- throw new Error("The host returned an invalid Endpoint lifecycle event");
413
+ throw new Error("The system returned an invalid Endpoint lifecycle event");
412
414
  }
413
415
  export function exit(code, signal) {
414
416
  const namedSignal = stringOrNull(signal);
package/dist/main.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- export { host, type Host } from "./host.js";
2
- export { type HostPointer, type PointerEvents, type PointerPosition } from "./pointer.js";
3
- export { type DesktopEvents, type DesktopSize, type HostDesktop } from "./desktop.js";
1
+ export { system, type System } from "./system.js";
2
+ export { type SystemPointer, type PointerEvents, type PointerPosition } from "./pointer.js";
3
+ export { type DesktopEvents, type DesktopSize, type SystemDesktop } from "./desktop.js";
4
4
  export { current, type Current, type CurrentServer } from "./current.js";
5
5
  export { type Channel, type ChannelCapture, type ChannelEvents, type ChannelMessage } from "./channel.js";
6
6
  export { ClientServiceHandler, ServerServiceHandler, ServiceHandler, type ClientServiceChannel, type ServerServiceChannel, type ServiceChannel, type ServiceKey, type ServiceLifecycleEvents } from "@phreshos/core";
7
7
  export { Client, Endpoint, Process, Program, Server, type Window, type ProgramProcess, type AnswerCapture, type AnswerMessage, type AnswerObserver, type AskCapture, type AskMessage, type AskObserver, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
8
- export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permission, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermission, Timeoutable, Theme, ThemeEvents, ThemeProperties, Value, WritableTheme, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, SurfaceSettings, Transaction } from "@phreshos/core";
8
+ export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permission, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermission, Timeoutable, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, SystemTheme, ThemedValue, Theme, ThemeEvents, ThemePreference, Value, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, VisibilityTransition, Transaction } from "@phreshos/core";
package/dist/main.js CHANGED
@@ -1,4 +1,4 @@
1
- export { host } from "./host.js";
1
+ export { system } from "./system.js";
2
2
  export {} from "./pointer.js";
3
3
  export {} from "./desktop.js";
4
4
  export { current } from "./current.js";
package/dist/pointer.d.ts CHANGED
@@ -13,7 +13,7 @@ export type PointerEvents = {
13
13
  move: PointerPosition;
14
14
  };
15
15
  /** Permission-guarded pointer positions and future movement. */
16
- export interface HostPointer extends Subscribable<PointerEvents, never> {
16
+ export interface SystemPointer extends Subscribable<PointerEvents, never> {
17
17
  /**
18
18
  * Reads the current desktop pointer position, or `null` before one is known.
19
19
  * Rejects unless the `pointer` permission is currently granted.
@@ -1,19 +1,21 @@
1
- import type { ClientServiceHandler, ServedFile, ServerServiceHandler, ServiceKey, Theme, ThemeProperties } from "@phreshos/core";
2
- import { type HostPointer } from "./pointer.js";
3
- import { type HostDesktop } from "./desktop.js";
1
+ import type { AppearanceSource, ClientServiceHandler, ServedFile, ServerServiceHandler, ServiceKey, SystemTheme } from "@phreshos/core";
2
+ import { type SystemPointer } from "./pointer.js";
3
+ import { type SystemDesktop } from "./desktop.js";
4
4
  type ServiceEndpoint = ServiceKey["endpoint"];
5
5
  type ServiceAddress<Endpoint extends ServiceEndpoint> = Omit<ServiceKey, "endpoint"> & Readonly<{
6
6
  endpoint: Endpoint;
7
7
  }>;
8
8
  type ServiceHandle<Endpoint extends ServiceEndpoint, Events extends object> = Endpoint extends "server" ? ServerServiceHandler<Events> : ClientServiceHandler<Events>;
9
9
  /** Desktop capabilities structurally available to a Client endpoint. */
10
- export interface Host {
11
- /** Read-only system Theme explicitly read from and observed through the desktop host. */
12
- readonly theme: Theme<ThemeProperties>;
10
+ export interface System {
11
+ /** Complete unresolved Appearance read from the System authority. */
12
+ readonly appearance: AppearanceSource;
13
+ /** Mutable effective Theme local to this Desktop. */
14
+ readonly theme: SystemTheme;
13
15
  /** Layer-independent desktop size reads and live updates. */
14
- readonly desktop: HostDesktop;
16
+ readonly desktop: SystemDesktop;
15
17
  /** Permission-guarded desktop pointer reads and live movement. */
16
- readonly pointer: HostPointer;
18
+ readonly pointer: SystemPointer;
17
19
  /** Stores one value as a publicly reachable file. */
18
20
  serve(value: unknown): Promise<ServedFile>;
19
21
  /** Performs an unrestricted server-side fetch on behalf of this Client. */
@@ -26,5 +28,5 @@ export interface Host {
26
28
  service<ServiceEvents extends object>(key: ServiceAddress<"client">): ClientServiceHandler<ServiceEvents>;
27
29
  }
28
30
  /** Desktop capabilities available to this Client. */
29
- export declare const host: Host;
31
+ export declare const system: System;
30
32
  export {};
@@ -1,10 +1,12 @@
1
+ import ClientAppearance from "./appearance.js";
1
2
  import { content } from "./content.js";
2
3
  import ClientTheme from "./theme.js";
3
4
  import wire from "./wire.js";
4
5
  import ClientPointer, {} from "./pointer.js";
5
6
  import ClientDesktop, {} from "./desktop.js";
6
7
  import { prepareService } from "./service.js";
7
- class ClientHost {
8
+ class ClientSystem {
9
+ appearance = new ClientAppearance();
8
10
  theme = new ClientTheme();
9
11
  desktop = new ClientDesktop();
10
12
  pointer = new ClientPointer();
@@ -121,4 +123,4 @@ function closeControl(signal, port, abort) {
121
123
  port.close();
122
124
  }
123
125
  /** Desktop capabilities available to this Client. */
124
- export const host = new ClientHost();
126
+ export const system = new ClientSystem();
package/dist/theme.d.ts CHANGED
@@ -1,13 +1,8 @@
1
+ import { type Theme, type ThemePreference } from "@phreshos/core";
1
2
  import Events from "./events.js";
2
- /** Read-only system Theme reached explicitly through the desktop boundary. */
3
+ /** Mutable Theme local to the current Desktop. */
3
4
  export default class ClientTheme extends Events {
4
5
  constructor();
5
- snapshot(): Promise<Readonly<{
6
- background: string;
7
- foreground: string;
8
- accent: string;
9
- spacing: number;
10
- radius: number;
11
- surface: import("@phreshos/core").ThemeSurface;
12
- }>>;
6
+ snapshot(): Promise<Theme>;
7
+ update(theme: ThemePreference): Promise<void>;
13
8
  }
package/dist/theme.js CHANGED
@@ -1,18 +1,19 @@
1
- import { createThemeSnapshot } from "@phreshos/core";
1
+ import {} from "@phreshos/core";
2
2
  import Events from "./events.js";
3
3
  import wire from "./wire.js";
4
- /** Read-only system Theme reached explicitly through the desktop boundary. */
4
+ /** Mutable Theme local to the current Desktop. */
5
5
  export default class ClientTheme extends Events {
6
6
  constructor() {
7
- super((event, listener, impossible) => wire.on("host-theme", event, value => {
8
- listener(createThemeSnapshot(value));
9
- }, null, impossible), observer => wire.onAll("host-theme", (event, value) => {
7
+ super((event, listener, impossible) => wire.on("host-theme", event, value => listener(value), null, impossible), observer => wire.onAll("host-theme", (event, value) => {
10
8
  if (typeof event === "string")
11
- observer(event, createThemeSnapshot(value));
9
+ observer(event, value);
12
10
  }));
13
11
  }
14
12
  async snapshot() {
15
13
  const [theme] = await wire.request(["theme"]);
16
- return createThemeSnapshot(theme);
14
+ return theme;
15
+ }
16
+ async update(theme) {
17
+ await wire.request(["update-theme", theme]);
17
18
  }
18
19
  }
package/dist/wire.js CHANGED
@@ -12,7 +12,6 @@ class Wire {
12
12
  impossible = new Map();
13
13
  identityPromise = null;
14
14
  constructor() {
15
- this.send("boundary", "document", crypto.randomUUID());
16
15
  window.addEventListener("message", event => {
17
16
  if (event.source !== this.parent || !Array.isArray(event.data))
18
17
  return;
@@ -320,7 +319,7 @@ class Wire {
320
319
  stream.failure = new Error("The boundary produced a stream value before opening the stream");
321
320
  else if (operation === "data") {
322
321
  if (stream.queue.length >= maximumStreamQueue)
323
- stream.failure = new Error(`Host stream queue exceeded its capacity of ${maximumStreamQueue}`);
322
+ stream.failure = new Error(`System stream queue exceeded its capacity of ${maximumStreamQueue}`);
324
323
  else
325
324
  stream.queue.push(value);
326
325
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/client",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "description": "The SDK used by a Program's client endpoint.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -47,13 +47,13 @@
47
47
  "prepack": "node --run build"
48
48
  },
49
49
  "peerDependencies": {
50
- "@phreshos/core": "^0.1.14"
50
+ "@phreshos/core": "^0.1.17"
51
51
  },
52
52
  "dependencies": {
53
53
  "@msgpack/msgpack": "^3.1.3"
54
54
  },
55
55
  "devDependencies": {
56
- "@phreshos/core": "^0.1.14",
56
+ "@phreshos/core": "^0.1.17",
57
57
  "typescript": "^6.0.3"
58
58
  }
59
59
  }