@phreshos/client 0.1.12 → 0.1.14

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
@@ -51,11 +51,11 @@ same canonical capability:
51
51
  ```ts
52
52
  const program = await current.program()
53
53
 
54
- await program.permissions.granted("pointer")
55
- await current.permissions.request("pointer")
56
- await current.permissions.timeout(5_000).request("pointer")
54
+ await program.permission.granted("pointer")
55
+ await current.permission.request("pointer")
56
+ await current.permission.timeout(5_000).request("pointer")
57
57
 
58
- current.permissions === program.permissions
58
+ current.permission === program.permission
59
59
  ```
60
60
 
61
61
  `granted()` reads the effective decision without prompting. `request()` asks
@@ -63,6 +63,20 @@ only when no known decision can answer immediately and returns `null` if its
63
63
  deadline expires. The desktop remains the internal enforcement boundary, but
64
64
  permission ownership does not alter the public shape of `host`.
65
65
 
66
+ The Client Host exposes only direct, exact Service handles. Creating a handle
67
+ does not read or start the Service and exposes no Service registry:
68
+
69
+ ```ts
70
+ const service = host.service({
71
+ program: "counter",
72
+ endpoint: "server",
73
+ name: "state"
74
+ })
75
+
76
+ await service.waitReady()
77
+ if (await service.enabled()) await service.channel.ask("value")
78
+ ```
79
+
66
80
  A Client may traverse `Process.parent()` through any number of ancestors in
67
81
  its own Program. The first parent outside that Program is structurally hidden
68
82
  and returned as `null`; no cross-Program Process handle enters the client. If
@@ -133,6 +147,21 @@ authoritative, subscribable Window capability and its `window.local` physical
133
147
  representation on this desktop. Local reads and updates have no events and do
134
148
  not change Server state. `current.window` is only convenient access to the
135
149
  executing Client's canonical Window; it has no additional authority.
150
+
151
+ A Program may converge its Clients on one named Process without a manual
152
+ `find()`/`create()` race:
153
+
154
+ ```ts
155
+ const shared = await program.process.findOrCreate({
156
+ name: "shared-server",
157
+ server: true,
158
+ client: false
159
+ })
160
+ ```
161
+
162
+ The authoritative Core returns the existing Process only when its normalized
163
+ launch is equivalent; a conflicting launch rejects without changing it.
164
+
136
165
  When both dimensions must change, `setGeometry({ position, size })` commits
137
166
  them through one authoritative request and produces one `geometry` event.
138
167
  Calling `move()` and `resize()` sequentially or through `Promise.all()` remains
package/dist/channel.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Channel as CoreChannel, ChannelCapture as CoreChannelCapture, ChannelEvents as CoreChannelEvents, ChannelMessage as CoreChannelMessage, ServiceDefinition } from "@phreshos/core";
1
+ import type { Channel as CoreChannel, ChannelCapture as CoreChannelCapture, ChannelEvents as CoreChannelEvents, ChannelMessage as CoreChannelMessage } from "@phreshos/core";
2
2
  import { type Endpoint } from "./domain.js";
3
3
  /** One value addressed to the current Client, with a client-visible sender. */
4
4
  export type ChannelMessage<Payload = unknown> = CoreChannelMessage<Payload, Endpoint | null>;
@@ -7,6 +7,6 @@ export type ChannelEvents<Events extends object> = CoreChannelEvents<Events, End
7
7
  /** Every event observable through the current Client's Channel. */
8
8
  export type ChannelCapture<Events extends object = {}> = CoreChannelCapture<Events, Endpoint | null>;
9
9
  /** Events explicitly accepted by the current Client. */
10
- export interface Channel<Events extends object = {}> extends CoreChannel<Events, Endpoint | null, ServiceDefinition> {
10
+ export interface Channel<Events extends object = {}> extends CoreChannel<Events, Endpoint | null> {
11
11
  }
12
12
  export declare const channel: Channel;
package/dist/channel.js CHANGED
@@ -12,7 +12,7 @@ class ClientChannel extends Events {
12
12
  publish(event, payload = undefined) {
13
13
  wire.send("end-host", "emit", event, payload);
14
14
  }
15
- async enableService(definition) { await enableCurrentService(definition); }
15
+ async enableService(name) { await enableCurrentService(name); }
16
16
  async disableService() { await disableCurrentService(); }
17
17
  }
18
18
  function message(value) {
package/dist/current.d.ts CHANGED
@@ -9,7 +9,7 @@ export interface Current<Events extends object = {}> extends Channel<Events>, Pi
9
9
  /** Presentation capability of this current Client. */
10
10
  readonly window: Window;
11
11
  /** The same permission capability exposed by the current Program. */
12
- readonly permissions: Program["permissions"];
12
+ readonly permission: Program["permission"];
13
13
  /** Returns the Process represented by this Client. */
14
14
  process(): Promise<Process>;
15
15
  /** Returns the accessible parent Process, or `null` when none exists. */
package/dist/current.js CHANGED
@@ -3,7 +3,7 @@ import Deadline from "./deadline.js";
3
3
  import { Client, Server, ServerTrafficHandle, TrafficHandle, bindEvents, endpointEvents, process, program, window as windowHandle } from "./domain.js";
4
4
  import wire from "./wire.js";
5
5
  import { endpointService } from "./service.js";
6
- import { currentProgramPermissions } from "./permissions.js";
6
+ import { currentProgramPermission } from "./permissions.js";
7
7
  const ServerBase = Server;
8
8
  const ClientBase = Client;
9
9
  class CurrentServerHandle extends ServerBase {
@@ -41,7 +41,7 @@ let currentServer;
41
41
  let currentClient;
42
42
  function owner() {
43
43
  if (!ownerPromise) {
44
- const resolving = wire.request(["process"]).then(answer => {
44
+ const resolving = wire.request(["current-process"]).then(answer => {
45
45
  return process(answer[0], { server: currentServer, client: currentClient });
46
46
  });
47
47
  const retained = resolving.catch(error => {
@@ -84,7 +84,7 @@ currentClient = new CurrentClientHandle(owner);
84
84
  class ClientCurrent {
85
85
  server = currentServer;
86
86
  window = currentClient.window;
87
- permissions = currentProgramPermissions;
87
+ permission = currentProgramPermission;
88
88
  constructor() {
89
89
  bindChannel(this, channel);
90
90
  }
@@ -94,7 +94,7 @@ class ClientCurrent {
94
94
  return answer[0] ? process(answer[0]) : null;
95
95
  }
96
96
  async program() {
97
- const answer = await wire.request(["program"]);
97
+ const answer = await wire.request(["current-program"]);
98
98
  return program(answer[0]);
99
99
  }
100
100
  async option(name) {
package/dist/domain.d.ts CHANGED
@@ -1,9 +1,20 @@
1
- import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerObserver as CoreAnswerObserver, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskObserver as CoreAskObserver, type Cleanup, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointTraffic as CoreEndpointTraffic, type EndpointDeclaration, type Exit, type Launch, type LocalWindow, type Permissions, type ServerTraffic as CoreServerTraffic, type TrafficMessage as CoreTrafficMessage, type TrafficCapture as CoreTrafficCapture, type TrafficEvents as CoreTrafficEvents, type Window as CoreWindow, type WindowState } from "@phreshos/core";
1
+ import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerObserver as CoreAnswerObserver, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskObserver as CoreAskObserver, type Cleanup, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointTraffic as CoreEndpointTraffic, type Exit, type Launch, type LocalWindow, type Permission, type Position, type ProgramProcess as CoreProgramProcess, type ServerTraffic as CoreServerTraffic, type Size, type TrafficMessage as CoreTrafficMessage, type TrafficCapture as CoreTrafficCapture, type TrafficEvents as CoreTrafficEvents, type Window as CoreWindow, type WindowState } from "@phreshos/core";
2
2
  import Events from "./events.js";
3
3
  export interface HandleAddress {
4
4
  identity: string;
5
5
  reference: string;
6
6
  }
7
+ export interface EndpointDeclarationRecord {
8
+ start: boolean;
9
+ hasService: boolean;
10
+ }
11
+ export interface ClientDeclarationRecord extends EndpointDeclarationRecord {
12
+ title: string | null;
13
+ size: Size | null;
14
+ position: Position | null;
15
+ layer: ClientDeclaration["layer"];
16
+ minimize: boolean | null;
17
+ }
7
18
  export interface ProgramRecord {
8
19
  reference: string;
9
20
  identity: string;
@@ -11,8 +22,8 @@ export interface ProgramRecord {
11
22
  name: string;
12
23
  version: string | null;
13
24
  description: string | null;
14
- server: EndpointDeclaration | null;
15
- client: ClientDeclaration | null;
25
+ server: EndpointDeclarationRecord | null;
26
+ client: ClientDeclarationRecord | null;
16
27
  }
17
28
  export interface ProcessRecord {
18
29
  reference: string;
@@ -30,19 +41,28 @@ export interface EndpointReference {
30
41
  }
31
42
  export type WindowRecord = WindowState;
32
43
  /** Program handle visible inside a structurally isolated Client. */
33
- export type Program<Events extends object = {}> = Omit<CoreProgram<Events>, "processes" | "firstProcess" | "lastProcess" | "getProcess" | "createProcess"> & {
44
+ export type Program<Events extends object = {}> = Omit<CoreProgram<Events>, "process"> & {
34
45
  /** Effective permission decisions owned by this Program. */
35
- readonly permissions: Permissions;
46
+ readonly permission: Permission;
47
+ /** Operations and lifecycle observation for this Program's Processes. */
48
+ readonly process: ProgramProcess;
49
+ };
50
+ /** Client-visible Process operations scoped to one Program. */
51
+ export type ProgramProcess = Omit<CoreProgramProcess, "list" | "first" | "last" | "find" | "create" | "findOrCreate"> & {
36
52
  /** Returns every live Process of this Program visible to the current Client. */
37
- processes(): Promise<Process[]>;
53
+ list(): Promise<Process[]>;
38
54
  /** Returns the earliest-started visible live Process, or `null` when none exist. */
39
- firstProcess(): Promise<Process | null>;
55
+ first(): Promise<Process | null>;
40
56
  /** Returns the latest-started visible live Process, or `null` when none exist. */
41
- lastProcess(): Promise<Process | null>;
57
+ last(): Promise<Process | null>;
42
58
  /** Finds a visible live Process by runtime identity or Program-local name. */
43
- getProcess(identityOrName: string): Promise<Process | null>;
59
+ find(identityOrName: string): Promise<Process | null>;
44
60
  /** Creates one Process of this same Program. */
45
- createProcess(launch?: Launch): Promise<Process>;
61
+ create(launch?: Launch): Promise<Process>;
62
+ /** Finds the named Process or atomically creates it with the same resolved launch. */
63
+ findOrCreate(launch: Launch & Readonly<{
64
+ name: string;
65
+ }>): Promise<Process>;
46
66
  };
47
67
  /** Process handle visible inside a structurally isolated Client. */
48
68
  export type Process<Events extends object = {}> = Omit<CoreProcess<Events>, "server" | "client" | "program" | "parent"> & {
package/dist/domain.js CHANGED
@@ -5,7 +5,7 @@ import HandleRegistry from "./handle-registry.js";
5
5
  import { area, sql, store } from "./storage.js";
6
6
  import wire from "./wire.js";
7
7
  import { endpointService } from "./service.js";
8
- import { currentProgramPermissions } from "./permissions.js";
8
+ import { currentProgramPermission } from "./permissions.js";
9
9
  const handles = new HandleRegistry();
10
10
  const ProgramBase = CoreProgram;
11
11
  const ProcessBase = CoreProcess;
@@ -19,43 +19,31 @@ class ProgramHandle extends ProgramBase {
19
19
  store = store();
20
20
  logs = sql("logs");
21
21
  database = sql("database");
22
- permissions = currentProgramPermissions;
22
+ permission = currentProgramPermission;
23
+ process;
23
24
  record;
24
25
  constructor(record) {
25
26
  super();
26
27
  this.identity = record.identity;
27
28
  this.reference = record.reference;
28
29
  this.record = record;
29
- bindEvents(this, scoped("host-end", record.reference, programEvent));
30
+ this.process = new ProgramProcessHandle(record.reference);
31
+ bindEvents(this, scoped("program-host", record.reference, programEvent));
30
32
  }
31
33
  get name() { return this.record.name; }
32
34
  get version() { return this.record.version; }
33
35
  get description() { return this.record.description; }
34
- get server() { return this.record.server; }
35
- get client() { return this.record.client; }
36
+ get server() {
37
+ return this.record.server ? declaration("server", this.record.server) : null;
38
+ }
39
+ get client() {
40
+ return this.record.client ? clientDeclaration(this.record.client) : null;
41
+ }
36
42
  update(record) {
37
43
  if (record.reference !== this.reference)
38
44
  throw new Error("A Program handle cannot become another Program");
39
45
  this.record = record;
40
46
  }
41
- async processes() {
42
- const answer = await wire.request(["processes"]);
43
- return answer[0].map(record => process(record));
44
- }
45
- async firstProcess() {
46
- return chronological(await this.processes())[0] ?? null;
47
- }
48
- async lastProcess() {
49
- return chronological(await this.processes()).at(-1) ?? null;
50
- }
51
- async getProcess(identityOrName) {
52
- const answer = await wire.request(["program-process", undefined, identityOrName]);
53
- return answer[0] ? process(answer[0]) : null;
54
- }
55
- async createProcess(launch = {}) {
56
- const answer = await wire.request(["create-process", undefined, launch]);
57
- return process(answer[0]);
58
- }
59
47
  async icon(size = "medium") {
60
48
  const answer = await wire.request(["icon", size]);
61
49
  return new Blob([Uint8Array.from(answer[0])], { type: "image/png" });
@@ -66,8 +54,58 @@ class ProgramHandle extends ProgramBase {
66
54
  }
67
55
  async uninstall(everything = false) { await wire.request(["uninstall", undefined, everything]); }
68
56
  async forget() { await wire.request(["forget"]); }
57
+ }
58
+ function declaration(endpoint, record) {
59
+ return Object.freeze({
60
+ start: record.start,
61
+ hasService: () => record.hasService,
62
+ docs: () => endpointDocs(endpoint, record.hasService)
63
+ });
64
+ }
65
+ function clientDeclaration(record) {
66
+ return Object.freeze({
67
+ ...declaration("client", record),
68
+ title: record.title,
69
+ size: record.size,
70
+ position: record.position,
71
+ layer: record.layer,
72
+ minimize: record.minimize
73
+ });
74
+ }
75
+ async function endpointDocs(endpoint, declared) {
76
+ if (!declared)
77
+ return null;
78
+ const answer = await wire.request(["endpoint-docs", endpoint]);
79
+ return answer[0];
80
+ }
81
+ class ProgramProcessHandle {
82
+ constructor(program) {
83
+ bindEvents(this, scoped("program-process", program, programProcessEvent));
84
+ }
85
+ async list() {
86
+ const answer = await wire.request(["program-process-list"]);
87
+ return answer[0].map(record => process(record));
88
+ }
89
+ async first() {
90
+ return chronological(await this.list())[0] ?? null;
91
+ }
92
+ async last() {
93
+ return chronological(await this.list()).at(-1) ?? null;
94
+ }
95
+ async find(identityOrName) {
96
+ const answer = await wire.request(["program-process-find", identityOrName]);
97
+ return answer[0] ? process(answer[0]) : null;
98
+ }
99
+ async create(launch = {}) {
100
+ const answer = await wire.request(["program-process-create", launch]);
101
+ return process(answer[0]);
102
+ }
103
+ async findOrCreate(launch) {
104
+ const answer = await wire.request(["program-process-find-or-create", launch]);
105
+ return process(answer[0]);
106
+ }
69
107
  async exitAll() {
70
- const answer = await wire.request(["exit-all"]);
108
+ const answer = await wire.request(["program-process-exit-all"]);
71
109
  return answer[0];
72
110
  }
73
111
  }
@@ -333,13 +371,16 @@ function unscoped(subject, values) {
333
371
  return values;
334
372
  return values[0] === subject ? values.slice(1) : null;
335
373
  }
336
- function programEvent(event, values) {
374
+ function programProcessEvent(event, values) {
337
375
  if (event === "endpointStart" || event === "endpointStop")
338
376
  return lifecycleEndpoint(values[0], values[1]);
339
- if (event === "processCreate")
377
+ if (event === "create")
340
378
  return process(values[0]);
341
- if (event === "processExit")
379
+ if (event === "exit")
342
380
  return { process: process(values[0]), ...exit(values[1], values[2]) };
381
+ return undefined;
382
+ }
383
+ function programEvent(event, values) {
343
384
  if (event === "uninstall")
344
385
  return { everythingRemoved: values[0] === true };
345
386
  return undefined;
package/dist/host.d.ts CHANGED
@@ -1,6 +1,11 @@
1
- import type { ClientServiceHandler, ServedFile, ServerServiceHandler, Theme, ThemeProperties } from "@phreshos/core";
1
+ import type { ClientServiceHandler, ServedFile, ServerServiceHandler, ServiceKey, Theme, ThemeProperties } from "@phreshos/core";
2
2
  import { type HostPointer } from "./pointer.js";
3
3
  import { type HostDesktop } from "./desktop.js";
4
+ type ServiceEndpoint = ServiceKey["endpoint"];
5
+ type ServiceAddress<Endpoint extends ServiceEndpoint> = Omit<ServiceKey, "endpoint"> & Readonly<{
6
+ endpoint: Endpoint;
7
+ }>;
8
+ type ServiceHandle<Endpoint extends ServiceEndpoint, Events extends object> = Endpoint extends "server" ? ServerServiceHandler<Events> : ClientServiceHandler<Events>;
4
9
  /** Desktop capabilities structurally available to a Client endpoint. */
5
10
  export interface Host {
6
11
  /** Read-only system Theme explicitly read from and observed through the desktop host. */
@@ -13,18 +18,13 @@ export interface Host {
13
18
  serve(value: unknown): Promise<ServedFile>;
14
19
  /** Performs an unrestricted server-side fetch on behalf of this Client. */
15
20
  fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
16
- /** Returns a stable handle for one exact Server service identity. */
17
- service<ServiceEvents extends object = {}>(key: {
18
- program: string;
19
- endpoint: "server";
20
- name: string;
21
- }): ServerServiceHandler<ServiceEvents>;
22
- /** Returns a stable handle for one exact Client service identity. */
23
- service<ServiceEvents extends object = {}>(key: {
24
- program: string;
25
- endpoint: "client";
26
- name: string;
27
- }): ClientServiceHandler<ServiceEvents>;
21
+ /** Returns a precisely typed stable handle for either service endpoint. */
22
+ service<Endpoint extends ServiceEndpoint>(key: ServiceAddress<Endpoint>): ServiceHandle<Endpoint, {}>;
23
+ /** Returns a typed stable handle for one exact Server service identity. */
24
+ service<ServiceEvents extends object>(key: ServiceAddress<"server">): ServerServiceHandler<ServiceEvents>;
25
+ /** Returns a typed stable handle for one exact Client service identity. */
26
+ service<ServiceEvents extends object>(key: ServiceAddress<"client">): ClientServiceHandler<ServiceEvents>;
28
27
  }
29
28
  /** Desktop capabilities available to this Client. */
30
29
  export declare const host: Host;
30
+ export {};
package/dist/host.js CHANGED
@@ -3,11 +3,12 @@ import ClientTheme from "./theme.js";
3
3
  import wire from "./wire.js";
4
4
  import ClientPointer, {} from "./pointer.js";
5
5
  import ClientDesktop, {} from "./desktop.js";
6
- import { service as serviceHandle } from "./service.js";
6
+ import { prepareService } from "./service.js";
7
7
  class ClientHost {
8
8
  theme = new ClientTheme();
9
9
  desktop = new ClientDesktop();
10
10
  pointer = new ClientPointer();
11
+ service(key) { return prepareService(key); }
11
12
  async serve(value) {
12
13
  const source = content(value);
13
14
  const channel = new MessageChannel();
@@ -78,9 +79,6 @@ class ClientHost {
78
79
  });
79
80
  return response;
80
81
  }
81
- service(key) {
82
- return serviceHandle(key);
83
- }
84
82
  }
85
83
  function requestHeaders(normalized, supplied) {
86
84
  if (!supplied)
package/dist/main.d.ts CHANGED
@@ -4,5 +4,5 @@ export { type DesktopEvents, type DesktopSize, type HostDesktop } from "./deskto
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
- export { Client, Endpoint, Process, Program, Server, type Window, 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, Permissions, Position, ProgramArea, ProgramEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermissions, Timeoutable, Theme, ThemeEvents, ThemeProperties, Value, WritableTheme, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, SurfaceSettings, Transaction } from "@phreshos/core";
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, ProgramArea, 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";
@@ -1,10 +1,10 @@
1
- import type { PermissionDecision, PermissionName, Permissions, TimedPermissions } from "@phreshos/core";
1
+ import type { Permission, PermissionDecision, PermissionName, TimedPermission } from "@phreshos/core";
2
2
  /** Client access to the current Program's effective permission decisions. */
3
- export default class ClientPermissions implements Permissions {
3
+ export default class ClientPermission implements Permission {
4
4
  granted(name: PermissionName): Promise<PermissionDecision>;
5
5
  request(name: PermissionName): Promise<PermissionDecision>;
6
- timeout(milliseconds: number): TimedPermissions;
6
+ timeout(milliseconds: number): TimedPermission;
7
7
  private requestWithin;
8
8
  }
9
9
  /** The one Program-owned permission capability visible in this isolated Client. */
10
- export declare const currentProgramPermissions: ClientPermissions;
10
+ export declare const currentProgramPermission: ClientPermission;
@@ -1,7 +1,7 @@
1
1
  import wire from "./wire.js";
2
2
  const defaultPermissionTimeout = 30_000;
3
3
  /** Client access to the current Program's effective permission decisions. */
4
- export default class ClientPermissions {
4
+ export default class ClientPermission {
5
5
  async granted(name) {
6
6
  const answer = await wire.request(["permission-granted", name]);
7
7
  return answer[0];
@@ -20,4 +20,4 @@ export default class ClientPermissions {
20
20
  }
21
21
  }
22
22
  /** The one Program-owned permission capability visible in this isolated Client. */
23
- export const currentProgramPermissions = new ClientPermissions();
23
+ export const currentProgramPermission = new ClientPermission();
package/dist/service.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import { type ClientServiceHandler, type ServerServiceHandler, type ServiceDefinition, type ServiceHandler, type ServiceKey } from "@phreshos/core";
1
+ import { type ClientServiceHandler, type ServerServiceHandler, type ServiceHandler, type ServiceKey } from "@phreshos/core";
2
2
  import type { HandleAddress } from "./domain.js";
3
- export declare function service<EventsMap extends object = {}>(key: ServiceKey & {
3
+ export declare function prepareService<EventsMap extends object = {}>(key: ServiceKey & {
4
4
  endpoint: "server";
5
5
  }): ServerServiceHandler<EventsMap>;
6
- export declare function service<EventsMap extends object = {}>(key: ServiceKey & {
6
+ export declare function prepareService<EventsMap extends object = {}>(key: ServiceKey & {
7
7
  endpoint: "client";
8
8
  }): ClientServiceHandler<EventsMap>;
9
- export declare function service(key: ServiceKey): ServiceHandler;
10
- export declare function enableCurrentService(definition: ServiceDefinition): Promise<void>;
9
+ export declare function prepareService(key: ServiceKey): ServiceHandler;
10
+ export declare function enableCurrentService(name: string): Promise<void>;
11
11
  export declare function disableCurrentService(): Promise<void>;
12
12
  export declare function endpointService<EventsMap extends object = {}>(target: HandleAddress | null, endpoint: "server"): Promise<ServerServiceHandler<EventsMap> | null>;
13
13
  export declare function endpointService<EventsMap extends object = {}>(target: HandleAddress | null, endpoint: "client"): Promise<ClientServiceHandler<EventsMap> | null>;
package/dist/service.js CHANGED
@@ -46,47 +46,43 @@ class ClientChannelHandle extends Events {
46
46
  }
47
47
  class ServerHandler extends ServerServiceBase {
48
48
  key;
49
- program;
50
- endpoint = "server";
51
49
  name;
52
50
  channel;
53
51
  constructor(key) {
54
52
  super();
55
53
  this.key = key;
56
- this.program = key.program;
57
54
  this.name = key.name;
58
55
  this.channel = new ServerChannelHandle(key);
59
56
  bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
60
57
  }
61
- async disabled() {
62
- const answer = await wire.request(["service-disabled", this.key]);
58
+ async enabled() {
59
+ const answer = await wire.request(["service-enabled", this.key]);
63
60
  return answer[0];
64
61
  }
65
- async docs() {
66
- const answer = await wire.request(["service-docs", this.key]);
67
- return answer[0];
62
+ async waitReady(timeout) {
63
+ await wire.request(["service-wait-ready", this.key, timeout], timeout);
68
64
  }
69
65
  }
70
66
  class ClientHandler extends ClientServiceBase {
71
67
  key;
72
- program;
73
- endpoint = "client";
74
68
  name;
75
69
  channel;
76
70
  constructor(key) {
77
71
  super();
78
72
  this.key = key;
79
- this.program = key.program;
80
73
  this.name = key.name;
81
74
  this.channel = new ClientChannelHandle(key);
82
75
  bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
83
76
  }
84
- async disabled() {
85
- const answer = await wire.request(["service-disabled", this.key]);
77
+ async enabled() {
78
+ const answer = await wire.request(["service-enabled", this.key]);
86
79
  return answer[0];
87
80
  }
81
+ async waitReady(timeout) {
82
+ await wire.request(["service-wait-ready", this.key, timeout], timeout);
83
+ }
88
84
  }
89
- export function service(key) {
85
+ export function prepareService(key) {
90
86
  if (!isServiceKey(key))
91
87
  throw new Error("A complete service key is required");
92
88
  const normalized = Object.freeze({ program: key.program, endpoint: key.endpoint, name: key.name });
@@ -97,15 +93,15 @@ export function service(key) {
97
93
  : new ClientHandler(normalized);
98
94
  });
99
95
  }
100
- export async function enableCurrentService(definition) {
101
- await wire.request(["enable-service", definition]);
96
+ export async function enableCurrentService(name) {
97
+ await wire.request(["enable-service", name]);
102
98
  }
103
99
  export async function disableCurrentService() {
104
100
  await wire.request(["disable-service"]);
105
101
  }
106
102
  export async function endpointService(target, endpoint) {
107
103
  const answer = await wire.request(["endpoint-service", target, endpoint]);
108
- return answer[0] ? service(answer[0]) : null;
104
+ return answer[0] ? prepareService(answer[0]) : null;
109
105
  }
110
106
  function serviceEvents(key, scope) {
111
107
  return [
package/dist/theme.js CHANGED
@@ -5,28 +5,14 @@ import wire from "./wire.js";
5
5
  export default class ClientTheme extends Events {
6
6
  constructor() {
7
7
  super((event, listener, impossible) => wire.on("host-theme", event, value => {
8
- if (isObject(value))
9
- listener(createThemeSnapshot(value));
8
+ listener(createThemeSnapshot(value));
10
9
  }, null, impossible), observer => wire.onAll("host-theme", (event, value) => {
11
- if (typeof event === "string" && isObject(value))
10
+ if (typeof event === "string")
12
11
  observer(event, createThemeSnapshot(value));
13
12
  }));
14
13
  }
15
14
  async snapshot() {
16
- const answer = await wire.request(["theme"]);
17
- if (!isObject(answer[0]))
18
- throw new Error("The desktop returned an invalid Theme snapshot");
19
- return createThemeSnapshot(answer[0]);
15
+ const [theme] = await wire.request(["theme"]);
16
+ return createThemeSnapshot(theme);
20
17
  }
21
18
  }
22
- function isObject(value) {
23
- if (typeof value !== "object" || value === null || Array.isArray(value))
24
- return false;
25
- const theme = value;
26
- const surface = theme.surface;
27
- return typeof theme.background === "string" && typeof theme.foreground === "string" && typeof theme.accent === "string"
28
- && typeof theme.spacing === "number" && typeof theme.radius === "number"
29
- && typeof surface === "object" && surface !== null
30
- && typeof surface.grain === "number" && typeof surface.animation === "number"
31
- && typeof surface.backdrop === "number" && typeof surface.opacity === "number";
32
- }
package/dist/wire.js CHANGED
@@ -62,7 +62,7 @@ class Wire {
62
62
  /** Resolves this endpoint's Process address only for operations that need it. */
63
63
  identity() {
64
64
  if (!this.identityPromise) {
65
- const resolving = this.request(["process"]).then(value => {
65
+ const resolving = this.request(["current-process"]).then(value => {
66
66
  const [record] = value;
67
67
  if (typeof record?.identity !== "string" || typeof record.reference !== "string") {
68
68
  throw new Error("The desktop returned an invalid Process identity");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/client",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "The SDK used by a Program's client endpoint.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -41,18 +41,19 @@
41
41
  "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
42
42
  "check": "tsc --noEmit",
43
43
  "build": "node --run clean && tsc --noEmit false --outDir dist --rootDir source",
44
+ "verify:completions": "node scripts/verify-completions.mjs",
44
45
  "verify:package": "node scripts/verify-package.mjs",
45
- "verify": "node --run check && node --run build && node --run verify:package",
46
+ "verify": "node --run check && node --run verify:completions && node --run build && node --run verify:package",
46
47
  "prepack": "node --run build"
47
48
  },
48
49
  "peerDependencies": {
49
- "@phreshos/core": "^0.1.8"
50
+ "@phreshos/core": "^0.1.11"
50
51
  },
51
52
  "dependencies": {
52
53
  "@msgpack/msgpack": "^3.1.3"
53
54
  },
54
55
  "devDependencies": {
55
- "@phreshos/core": "^0.1.8",
56
+ "@phreshos/core": "^0.1.11",
56
57
  "typescript": "^6.0.3"
57
58
  }
58
59
  }