@phreshos/client 0.1.27 → 0.1.28

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
@@ -75,17 +75,22 @@ does not read or start the Service and exposes no Service registry:
75
75
  ```ts
76
76
  const service = system.service({
77
77
  program: "counter",
78
- endpoint: "server",
79
- name: "state"
78
+ process: "main",
79
+ endpoint: "server"
80
80
  })
81
81
 
82
82
  await service.waitReady()
83
- if (await service.enabled()) await service.ask("value")
83
+ if (await service.exists()) await service.ask("value")
84
84
 
85
85
  service.subscribe("changed", message => console.log(message))
86
- service.lifecycle.subscribe("disable", () => console.log("unavailable"))
86
+ service.lifecycle.subscribe("stop", () => console.log("unavailable"))
87
87
  ```
88
88
 
89
+ The `program` coordinate is required for a Program-local Process name. When
90
+ `process` is an exact globally unique identity, use
91
+ `system.service({ process, endpoint })`; that handle never retargets a
92
+ replacement Process.
93
+
89
94
  A Client may traverse `Process.parent()` through any number of ancestors in
90
95
  its own Program. The first parent outside that Program is structurally hidden
91
96
  and returned as `null`; no cross-Program Process handle enters the client. If
@@ -110,6 +115,8 @@ through `context.process()`. It is the canonical Process-owned handle, so
110
115
  `context.server === (await context.process()).server`.
111
116
  Endpoint `process()` navigation is asynchronous; contextual ownership is
112
117
  requested only when navigation needs it and then retained by the SDK.
118
+ `context.name()` returns that retained Process's Program-local name, or `null`
119
+ when its launch was unnamed.
113
120
 
114
121
  All domain handles are canonical within this iframe's JavaScript realm. Lookup,
115
122
  navigation, event payloads, and message metadata reuse the same weakly retained
package/dist/context.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Context as CoreContext, ContextCapture as CoreContextCapture, ContextEvents as CoreContextEvents, ContextMessage as CoreContextMessage } from "@phreshos/core";
2
- import { Client, Server, type Endpoint, type Process, type Program, type Window } from "./domain.js";
2
+ import { Server, type Endpoint, type Process, type Program, type Window } from "./domain.js";
3
3
  /** The executing Process's canonical Server handle. */
4
4
  export type ContextServer<Events extends object = {}> = Server<Events>;
5
5
  /** One value addressed to the current Client, with a client-visible sender. */
@@ -9,7 +9,7 @@ export type ContextEvents<Events extends object> = CoreContextEvents<Events, End
9
9
  /** Every event observable through the current Client Context. */
10
10
  export type ContextCapture<Events extends object = {}> = CoreContextCapture<Events, Endpoint | null>;
11
11
  /** Client runtime context: inbound communication, owner hierarchy, and paired Server. */
12
- export interface Context<Events extends object = {}> extends CoreContext<Events, Endpoint | null>, Pick<Client, "service"> {
12
+ export interface Context<Events extends object = {}> extends CoreContext<Events, Endpoint | null> {
13
13
  /** The same Server handle exposed by the executing Process. */
14
14
  readonly server: ContextServer;
15
15
  /** Presentation capability of the executing Client. */
@@ -18,6 +18,8 @@ export interface Context<Events extends object = {}> extends CoreContext<Events,
18
18
  readonly permission: Program["permission"];
19
19
  /** Returns the Process represented by this Client. */
20
20
  process(): Promise<Process>;
21
+ /** Returns the executing Process's Program-local name, or `null` when unnamed. */
22
+ name(): Promise<string | null>;
21
23
  /** Returns the accessible parent Process, or `null` when none exists. */
22
24
  parent(): Promise<Process | null>;
23
25
  /** Returns the Program that owns this Client. */
package/dist/context.js CHANGED
@@ -2,7 +2,6 @@ import Deadline from "./deadline.js";
2
2
  import { Client, Server, ServerTrafficHandle, TrafficHandle, bindEvents, endpointLifecycle, endpointEvents, process, program, visibleEndpoint, window as windowHandle } from "./domain.js";
3
3
  import Events from "./events.js";
4
4
  import wire from "./wire.js";
5
- import { disableCurrentService, enableCurrentService, endpointService } from "./service.js";
6
5
  import { currentProgramPermission } from "./permissions.js";
7
6
  const ServerBase = Server;
8
7
  const ClientBase = Client;
@@ -21,9 +20,9 @@ class ContextServerHandle extends ServerBase {
21
20
  const answer = await wire.request(["exists", "server"]);
22
21
  return answer[0];
23
22
  }
24
- async start() { await wire.request(["start-endpoint", undefined, "server"]); }
23
+ async start(launch = {}) { await wire.request(["start-endpoint", undefined, "server", launch]); }
25
24
  async stop() { await wire.request(["stop-endpoint", undefined, "server"]); }
26
- service() { return endpointService(null, "server"); }
25
+ async isService() { return (await wire.request(["is-service", "server"]))[0]; }
27
26
  async waitReady(timeout) { await wire.request(["wait-ready"], timeout); }
28
27
  async ask(event, payload = undefined) {
29
28
  return this.askWithin(undefined, event, payload);
@@ -75,9 +74,9 @@ class ContextClientHandle extends ClientBase {
75
74
  const answer = await wire.request(["exists", "client"]);
76
75
  return answer[0];
77
76
  }
78
- async start(overrides = {}) { await wire.request(["start-endpoint", undefined, "client", overrides]); }
77
+ async start(launch = {}) { await wire.request(["start-endpoint", undefined, "client", launch]); }
79
78
  async stop() { await wire.request(["stop-endpoint", undefined, "client"]); }
80
- service() { return endpointService(null, "client"); }
79
+ async isService() { return (await wire.request(["is-service", "client"]))[0]; }
81
80
  }
82
81
  contextClient = new ContextClientHandle(owner);
83
82
  class ClientContext extends Events {
@@ -91,6 +90,7 @@ class ClientContext extends Events {
91
90
  }, null, impossible));
92
91
  }
93
92
  process() { return owner(); }
93
+ async name() { return (await owner()).name; }
94
94
  async parent() {
95
95
  const answer = await wire.request(["parent"]);
96
96
  return answer[0] ? process(answer[0]) : null;
@@ -104,10 +104,8 @@ class ClientContext extends Events {
104
104
  return answer[0];
105
105
  }
106
106
  async stop() { await wire.request(["stop-current"]); }
107
- service() { return endpointService(null, "client"); }
107
+ async isService() { return contextClient.isService(); }
108
108
  publish(event, payload = undefined) { wire.send("end-host", "emit", event, payload); }
109
- async enableService(name) { await enableCurrentService(name); }
110
- async disableService() { await disableCurrentService(); }
111
109
  }
112
110
  async function currentAddress() {
113
111
  const identity = await wire.identity();
package/dist/domain.d.ts CHANGED
@@ -6,6 +6,7 @@ export interface HandleAddress {
6
6
  }
7
7
  export interface EndpointDeclarationRecord {
8
8
  start: boolean;
9
+ service: boolean;
9
10
  }
10
11
  export interface ClientDeclarationRecord extends EndpointDeclarationRecord {
11
12
  title: string | null;
@@ -32,8 +33,11 @@ export interface ProcessRecord {
32
33
  program: ProgramRecord;
33
34
  options: Record<string, string>;
34
35
  startedAt: string | Date;
35
- server: Record<string, never> | null;
36
- client: Record<string, never> | null;
36
+ server: EndpointRecord | null;
37
+ client: EndpointRecord | null;
38
+ }
39
+ export interface EndpointRecord {
40
+ service: boolean;
37
41
  }
38
42
  export interface EndpointReference {
39
43
  kind: "server" | "client";
package/dist/domain.js CHANGED
@@ -4,7 +4,6 @@ import Deadline from "./deadline.js";
4
4
  import HandleRegistry from "./handle-registry.js";
5
5
  import { area, sql, store } from "./storage.js";
6
6
  import wire from "./wire.js";
7
- import { endpointService } from "./service.js";
8
7
  import { currentProgramPermission } from "./permissions.js";
9
8
  const handles = new HandleRegistry();
10
9
  const ProgramBase = CoreProgram;
@@ -75,7 +74,8 @@ function programCommandChunk(value) {
75
74
  }
76
75
  function declaration(record) {
77
76
  return Object.freeze({
78
- start: record.start
77
+ start: record.start,
78
+ service: record.service
79
79
  });
80
80
  }
81
81
  function clientDeclaration(record) {
@@ -219,9 +219,9 @@ class ServerHandle extends ServerBase {
219
219
  const answer = await wire.request(["exists", "server", this.owner.address]);
220
220
  return answer[0];
221
221
  }
222
- async start() { await wire.request(["start-endpoint", this.owner.address, "server"]); }
222
+ async start(launch = {}) { await wire.request(["start-endpoint", this.owner.address, "server", launch]); }
223
223
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "server"]); }
224
- service() { return endpointService(this.owner.address, "server"); }
224
+ async isService() { return (await wire.request(["is-service", "server", this.owner.address]))[0]; }
225
225
  async waitReady(timeout) { await wire.request(["wait-ready", this.owner.address], timeout); }
226
226
  async ask(event, payload = undefined) {
227
227
  return this.askWithin(undefined, event, payload);
@@ -264,9 +264,9 @@ class ClientHandle extends ClientBase {
264
264
  const answer = await wire.request(["exists", "client", this.owner.address]);
265
265
  return answer[0];
266
266
  }
267
- async start(overrides = {}) { await wire.request(["start-endpoint", this.owner.address, "client", overrides]); }
267
+ async start(launch = {}) { await wire.request(["start-endpoint", this.owner.address, "client", launch]); }
268
268
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "client"]); }
269
- service() { return endpointService(this.owner.address, "client"); }
269
+ async isService() { return (await wire.request(["is-service", "client", this.owner.address]))[0]; }
270
270
  }
271
271
  class WindowHandle extends Events {
272
272
  target;
package/dist/main.d.ts CHANGED
@@ -2,6 +2,6 @@ export { system, type System } from "./system.js";
2
2
  export { type SystemPointer, type PointerEvents, type PointerPosition } from "./pointer.js";
3
3
  export { type DesktopEvents, type DesktopSize, type SystemDesktop } from "./desktop.js";
4
4
  export { context, type Context, type ContextCapture, type ContextEvents, type ContextMessage, type ContextServer } from "./context.js";
5
- export { ClientService, ServerService, Service, type ServiceKey, type ServiceLifecycle, type ServiceLifecycleEvents } from "@phreshos/core";
5
+ export { ClientService, ServerService, Service, type ServiceKey, } from "@phreshos/core";
6
6
  export { Client, Endpoint, Process, Program, Server, type Window, type ProgramProcess, type AnswerCapture, type AnswerMessage, type AnswerSubscriber, type AskCapture, type AskMessage, type AskSubscriber, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
7
- export type { Askable, Capture, Captures, CaptureSubscriber, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointLifecycle, EndpointLifecycleEvents, EntryStat, EventMessage, EventName, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permission, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, SystemUploads, Upload, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermission, Timeoutable, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, AnimationsPreference, DesktopPreferences, DesktopPreferencesEvents, DesktopPreferencesUpdate, SystemDesktopPreferences, ThemedValue, Theme, ThemePreference, Value, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, VisibilityTransition, Transaction } from "@phreshos/core";
7
+ export type { Askable, Capture, Captures, CaptureSubscriber, ClientLaunch, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointLifecycle, EndpointLifecycleEvents, EntryStat, EventMessage, EventName, EventOptions, EventSubscriber, Exit, FileStat, Launch, ServerLaunch, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permission, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, SystemUploads, Upload, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermission, Timeoutable, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, AnimationsPreference, DesktopPreferences, DesktopPreferencesEvents, DesktopPreferencesUpdate, SystemDesktopPreferences, ThemedValue, Theme, ThemePreference, Value, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, VisibilityTransition, Transaction } from "@phreshos/core";
package/dist/main.js CHANGED
@@ -2,5 +2,5 @@ export { system } from "./system.js";
2
2
  export {} from "./pointer.js";
3
3
  export {} from "./desktop.js";
4
4
  export { context } from "./context.js";
5
- export { ClientService, ServerService, Service } from "@phreshos/core";
5
+ export { ClientService, ServerService, Service, } from "@phreshos/core";
6
6
  export { Client, Endpoint, Process, Program, Server } from "./domain.js";
package/dist/service.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { type ClientService, type ServerService, type Service, type ServiceKey } from "@phreshos/core";
2
- import type { HandleAddress } from "./domain.js";
3
2
  export declare function prepareService<EventsMap extends object = {}>(key: ServiceKey & {
4
3
  endpoint: "server";
5
4
  }): ServerService<EventsMap>;
@@ -7,8 +6,3 @@ export declare function prepareService<EventsMap extends object = {}>(key: Servi
7
6
  endpoint: "client";
8
7
  }): ClientService<EventsMap>;
9
8
  export declare function prepareService(key: ServiceKey): Service;
10
- export declare function enableCurrentService(name: string): Promise<void>;
11
- export declare function disableCurrentService(): Promise<void>;
12
- export declare function endpointService<EventsMap extends object = {}>(target: HandleAddress | null, endpoint: "server"): Promise<ServerService<EventsMap> | null>;
13
- export declare function endpointService<EventsMap extends object = {}>(target: HandleAddress | null, endpoint: "client"): Promise<ClientService<EventsMap> | null>;
14
- export declare function endpointService(target: HandleAddress | null, endpoint: "server" | "client"): Promise<Service | null>;
package/dist/service.js CHANGED
@@ -6,37 +6,43 @@ import wire from "./wire.js";
6
6
  const handles = new HandleRegistry();
7
7
  const ServerServiceBase = CoreServerService;
8
8
  const ClientServiceBase = CoreClientService;
9
- class ServerHandler extends ServerServiceBase {
9
+ class ServiceHandle {
10
10
  key;
11
- name;
12
11
  lifecycle;
13
12
  constructor(key) {
14
- super();
15
13
  this.key = key;
16
- this.name = key.name;
17
14
  this.lifecycle = new Events(...serviceEvents(key, "lifecycle"));
18
- bindEvents(this, new Events(...serviceEvents(key, "events")));
19
15
  }
20
16
  publish(event, payload = undefined) {
21
17
  wire.send("end-host", "service-send", this.key, event, payload);
22
18
  }
23
- async ask(event, payload = undefined) {
24
- return await this.askWithin(new Deadline(), event, payload);
25
- }
26
- timeout(milliseconds) {
27
- return {
28
- ask: (event, payload = undefined) => {
29
- return this.askWithin(new Deadline(milliseconds), event, payload);
30
- }
31
- };
32
- }
33
- async enabled() {
34
- const answer = await wire.request(["service-enabled", this.key]);
19
+ async exists() {
20
+ const answer = await wire.request(["service-exists", this.key]);
35
21
  return answer[0];
36
22
  }
23
+ }
24
+ class ServerHandler extends ServerServiceBase {
25
+ key;
26
+ lifecycle;
27
+ service;
28
+ constructor(key) {
29
+ super();
30
+ this.key = key;
31
+ this.service = new ServiceHandle(key);
32
+ this.lifecycle = this.service.lifecycle;
33
+ bindEvents(this, new Events(...serviceEvents(key, "events")));
34
+ }
35
+ publish(event, payload = undefined) { this.service.publish(event, payload); }
36
+ exists() { return this.service.exists(); }
37
37
  async waitReady(timeout) {
38
38
  await wire.request(["service-wait-ready", this.key, timeout], timeout);
39
39
  }
40
+ async ask(event, payload = undefined) {
41
+ return await this.askWithin(new Deadline(), event, payload);
42
+ }
43
+ timeout(milliseconds) {
44
+ return { ask: (event, payload = undefined) => (this.askWithin(new Deadline(milliseconds), event, payload)) };
45
+ }
40
46
  async askWithin(deadline, event, payload) {
41
47
  const identity = await wire.identity();
42
48
  const address = `client:${identity.process}:${crypto.randomUUID()}`;
@@ -52,44 +58,29 @@ class ServerHandler extends ServerServiceBase {
52
58
  }
53
59
  }
54
60
  class ClientHandler extends ClientServiceBase {
55
- key;
56
- name;
57
61
  lifecycle;
62
+ service;
58
63
  constructor(key) {
59
64
  super();
60
- this.key = key;
61
- this.name = key.name;
62
- this.lifecycle = new Events(...serviceEvents(key, "lifecycle"));
65
+ this.service = new ServiceHandle(key);
66
+ this.lifecycle = this.service.lifecycle;
63
67
  bindEvents(this, new Events(...serviceEvents(key, "events")));
64
68
  }
65
- async enabled() {
66
- const answer = await wire.request(["service-enabled", this.key]);
67
- return answer[0];
68
- }
69
- async waitReady(timeout) {
70
- await wire.request(["service-wait-ready", this.key, timeout], timeout);
71
- }
69
+ publish(event, payload = undefined) { this.service.publish(event, payload); }
70
+ exists() { return this.service.exists(); }
72
71
  }
73
72
  export function prepareService(key) {
74
73
  if (!isServiceKey(key))
75
74
  throw new Error("A complete service key is required");
76
- const normalized = Object.freeze({ program: key.program, endpoint: key.endpoint, name: key.name });
77
- const identity = JSON.stringify([normalized.program, normalized.endpoint, normalized.name]);
78
- return handles.obtain(`service:${identity}`, () => {
79
- return normalized.endpoint === "server"
80
- ? new ServerHandler(normalized)
81
- : new ClientHandler(normalized);
75
+ const normalized = Object.freeze({
76
+ ...(key.program === undefined ? {} : { program: key.program }),
77
+ process: key.process,
78
+ endpoint: key.endpoint
82
79
  });
83
- }
84
- export async function enableCurrentService(name) {
85
- await wire.request(["enable-service", name]);
86
- }
87
- export async function disableCurrentService() {
88
- await wire.request(["disable-service"]);
89
- }
90
- export async function endpointService(target, endpoint) {
91
- const answer = await wire.request(["endpoint-service", target, endpoint]);
92
- return answer[0] ? prepareService(answer[0]) : null;
80
+ const identity = JSON.stringify([key.program ?? null, key.process, key.endpoint]);
81
+ return handles.obtain(`service:${identity}`, () => normalized.endpoint === "server"
82
+ ? new ServerHandler(normalized)
83
+ : new ClientHandler(normalized));
93
84
  }
94
85
  function serviceEvents(key, scope) {
95
86
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/client",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
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.27"
50
+ "@phreshos/core": "^0.1.29"
51
51
  },
52
52
  "dependencies": {
53
53
  "@msgpack/msgpack": "^3.1.3"
54
54
  },
55
55
  "devDependencies": {
56
- "@phreshos/core": "^0.1.27",
56
+ "@phreshos/core": "^0.1.29",
57
57
  "typescript": "^6.0.3"
58
58
  }
59
59
  }