@phreshos/client 0.1.2 → 0.1.3

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/dist/channel.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { visibleEndpoint } from "./domain.js";
2
2
  import Events from "./events.js";
3
3
  import wire from "./wire.js";
4
+ import { disableCurrentService, enableCurrentService } from "./service.js";
4
5
  class ClientChannel extends Events {
5
6
  constructor() {
6
7
  super((event, listener, impossible) => wire.on("end-end", event, value => listener(message(value)), null, impossible), observer => wire.onAll("end-end", (event, value) => {
@@ -11,6 +12,8 @@ class ClientChannel extends Events {
11
12
  publish(event, payload = undefined) {
12
13
  wire.send("end-host", "emit", event, payload);
13
14
  }
15
+ async enableService(name) { await enableCurrentService(name); }
16
+ async disableService() { await disableCurrentService(); }
14
17
  }
15
18
  function message(value) {
16
19
  const raw = value;
package/dist/current.d.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import type { Channel as CoreChannel } from "@phreshos/core";
2
- import { Server, type Endpoint, type Process, type Program, type Window } from "./domain.js";
2
+ import { Client, Server, type Endpoint, type Process, type Program, type Window } from "./domain.js";
3
3
  /** The current Process's canonical Server handle. */
4
4
  export type CurrentServer<Events extends object = {}> = Server<Events>;
5
5
  /** Current Client context: its inbound Channel, owner hierarchy, and paired Server. */
6
- export interface Current<Events extends object = {}> extends CoreChannel<Events, Endpoint | null> {
6
+ export interface Current<Events extends object = {}> extends CoreChannel<Events, Endpoint | null>, Pick<Client, "service"> {
7
7
  /** The same Server handle exposed by the current Process. */
8
8
  readonly server: CurrentServer;
9
9
  /** Presentation capability of this current Client. */
package/dist/current.js CHANGED
@@ -2,6 +2,7 @@ import { channel } from "./channel.js";
2
2
  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
+ import { endpointService } from "./service.js";
5
6
  const ServerBase = Server;
6
7
  const ClientBase = Client;
7
8
  class CurrentServerHandle extends ServerBase {
@@ -20,6 +21,7 @@ class CurrentServerHandle extends ServerBase {
20
21
  }
21
22
  async start() { await wire.request(["start-endpoint", undefined, "server"]); }
22
23
  async stop() { await wire.request(["stop-endpoint", undefined, "server"]); }
24
+ service() { return endpointService(null, "server"); }
23
25
  async waitReady(timeout) { await wire.request(["wait-ready"], timeout); }
24
26
  async ask(event, payload = undefined) {
25
27
  return this.askWithin(undefined, event, payload);
@@ -75,6 +77,7 @@ class CurrentClientHandle extends ClientBase {
75
77
  }
76
78
  async start(overrides = {}) { await wire.request(["start-endpoint", undefined, "client", overrides]); }
77
79
  async stop() { await wire.request(["stop-endpoint", undefined, "client"]); }
80
+ service() { return endpointService(null, "client"); }
78
81
  }
79
82
  currentClient = new CurrentClientHandle(owner);
80
83
  class ClientCurrent {
@@ -97,6 +100,7 @@ class ClientCurrent {
97
100
  return answer[0];
98
101
  }
99
102
  async stop() { await wire.request(["stop-current"]); }
103
+ service() { return endpointService(null, "client"); }
100
104
  }
101
105
  function bindChannel(target, source) {
102
106
  Object.assign(target, {
@@ -104,7 +108,9 @@ function bindChannel(target, source) {
104
108
  subscribe: source.subscribe.bind(source),
105
109
  waitFor: source.waitFor.bind(source),
106
110
  events: source.events.bind(source),
107
- observe: source.observe.bind(source)
111
+ observe: source.observe.bind(source),
112
+ enableService: source.enableService.bind(source),
113
+ disableService: source.disableService.bind(source)
108
114
  });
109
115
  }
110
116
  /** Inbound events, owner hierarchy, and paired Server for the current Client. */
package/dist/domain.js CHANGED
@@ -4,6 +4,7 @@ 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";
7
8
  const handles = new HandleRegistry();
8
9
  const ProgramBase = CoreProgram;
9
10
  const ProcessBase = CoreProcess;
@@ -160,6 +161,7 @@ class ServerHandle extends ServerBase {
160
161
  }
161
162
  async start() { await wire.request(["start-endpoint", this.owner.address, "server"]); }
162
163
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "server"]); }
164
+ service() { return endpointService(this.owner.address, "server"); }
163
165
  async waitReady(timeout) { await wire.request(["wait-ready", this.owner.address], timeout); }
164
166
  async ask(event, payload = undefined) {
165
167
  return this.askWithin(undefined, event, payload);
@@ -202,6 +204,7 @@ class ClientHandle extends ClientBase {
202
204
  }
203
205
  async start(overrides = {}) { await wire.request(["start-endpoint", this.owner.address, "client", overrides]); }
204
206
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "client"]); }
207
+ service() { return endpointService(this.owner.address, "client"); }
205
208
  }
206
209
  class WindowHandle extends Events {
207
210
  target;
package/dist/host.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Permissions, ServedFile, Theme, ThemeProperties } from "@phreshos/core";
1
+ import type { ClientServiceHandler, Permissions, ServedFile, ServerServiceHandler, Theme, ThemeProperties } from "@phreshos/core";
2
2
  import { type HostPointer } from "./pointer.js";
3
3
  import { type HostSurface } from "./surface.js";
4
4
  /** Desktop capabilities structurally available to a Client endpoint. */
@@ -15,6 +15,18 @@ export interface Host {
15
15
  serve(value: unknown): Promise<ServedFile>;
16
16
  /** Performs an unrestricted server-side fetch on behalf of this Client. */
17
17
  fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
18
+ /** Returns a stable handle for one exact Server service identity. */
19
+ service<ServiceEvents extends object = {}>(key: {
20
+ program: string;
21
+ endpoint: "server";
22
+ name: string;
23
+ }): ServerServiceHandler<ServiceEvents>;
24
+ /** Returns a stable handle for one exact Client service identity. */
25
+ service<ServiceEvents extends object = {}>(key: {
26
+ program: string;
27
+ endpoint: "client";
28
+ name: string;
29
+ }): ClientServiceHandler<ServiceEvents>;
18
30
  }
19
31
  /** Desktop capabilities available to this Client. */
20
32
  export declare const host: Host;
package/dist/host.js CHANGED
@@ -4,6 +4,7 @@ import wire from "./wire.js";
4
4
  import ClientPermissions from "./permissions.js";
5
5
  import ClientPointer, {} from "./pointer.js";
6
6
  import ClientSurface, {} from "./surface.js";
7
+ import { service as serviceHandle } from "./service.js";
7
8
  class ClientHost {
8
9
  theme = new ClientTheme();
9
10
  surface = new ClientSurface();
@@ -79,6 +80,9 @@ class ClientHost {
79
80
  });
80
81
  return response;
81
82
  }
83
+ service(key) {
84
+ return serviceHandle(key);
85
+ }
82
86
  }
83
87
  function requestHeaders(normalized, supplied) {
84
88
  if (!supplied)
package/dist/main.d.ts CHANGED
@@ -3,5 +3,6 @@ export { type HostPointer, type PointerEvents, type PointerPosition } from "./po
3
3
  export { type HostSurface, type Surface, type SurfaceEvents } from "./surface.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
+ export { ClientServiceHandler, ServerServiceHandler, ServiceHandler, type ClientServiceChannel, type ServerServiceChannel, type ServiceChannel, type ServiceKey, type ServiceLifecycleEvents } from "@phreshos/core";
6
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";
7
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, WindowLayer, WindowState, WindowSurface, WindowSurfaceEasing, WindowSurfaceSettings, WindowSurfaceTransaction } from "@phreshos/core";
package/dist/main.js CHANGED
@@ -3,4 +3,5 @@ export {} from "./pointer.js";
3
3
  export {} from "./surface.js";
4
4
  export { current } from "./current.js";
5
5
  export {} from "./channel.js";
6
+ export { ClientServiceHandler, ServerServiceHandler, ServiceHandler } from "@phreshos/core";
6
7
  export { Client, Endpoint, Process, Program, Server } from "./domain.js";
@@ -0,0 +1,14 @@
1
+ import { type ClientServiceHandler, type ServerServiceHandler, type ServiceHandler, type ServiceKey } from "@phreshos/core";
2
+ import type { HandleAddress } from "./domain.js";
3
+ export declare function service<EventsMap extends object = {}>(key: ServiceKey & {
4
+ endpoint: "server";
5
+ }): ServerServiceHandler<EventsMap>;
6
+ export declare function service<EventsMap extends object = {}>(key: ServiceKey & {
7
+ endpoint: "client";
8
+ }): ClientServiceHandler<EventsMap>;
9
+ export declare function service(key: ServiceKey): ServiceHandler;
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<ServerServiceHandler<EventsMap> | null>;
13
+ export declare function endpointService<EventsMap extends object = {}>(target: HandleAddress | null, endpoint: "client"): Promise<ClientServiceHandler<EventsMap> | null>;
14
+ export declare function endpointService(target: HandleAddress | null, endpoint: "server" | "client"): Promise<ServiceHandler | null>;
@@ -0,0 +1,122 @@
1
+ import { ClientServiceHandler as CoreClientServiceHandler, ServerServiceHandler as CoreServerServiceHandler, isServiceKey } from "@phreshos/core";
2
+ import Deadline from "./deadline.js";
3
+ import Events from "./events.js";
4
+ import HandleRegistry from "./handle-registry.js";
5
+ import wire from "./wire.js";
6
+ const handles = new HandleRegistry();
7
+ const ServerServiceBase = CoreServerServiceHandler;
8
+ const ClientServiceBase = CoreClientServiceHandler;
9
+ class ServerChannelHandle extends Events {
10
+ key;
11
+ constructor(key) {
12
+ super(...serviceEvents(key, "channel"));
13
+ this.key = key;
14
+ }
15
+ publish(event, payload = undefined) {
16
+ wire.send("end-host", "service-send", this.key, event, payload);
17
+ }
18
+ async ask(event, payload = undefined) {
19
+ return await this.askWithin(new Deadline(), event, payload);
20
+ }
21
+ timeout(milliseconds) {
22
+ return {
23
+ ask: (event, payload = undefined) => {
24
+ return this.askWithin(new Deadline(milliseconds), event, payload);
25
+ }
26
+ };
27
+ }
28
+ async askWithin(deadline, event, payload) {
29
+ const identity = await wire.identity();
30
+ const address = `client:${identity.process}:${crypto.randomUUID()}`;
31
+ const question = crypto.randomUUID();
32
+ const waiting = wire.expectWithin(address, deadline);
33
+ wire.send("end-host", "service-ask", this.key, address, question, event, payload);
34
+ try {
35
+ return await waiting;
36
+ }
37
+ finally {
38
+ wire.forget(address);
39
+ }
40
+ }
41
+ }
42
+ class ClientChannelHandle extends Events {
43
+ constructor(key) {
44
+ super(...serviceEvents(key, "channel"));
45
+ }
46
+ }
47
+ class ServerHandler extends ServerServiceBase {
48
+ key;
49
+ program;
50
+ endpoint = "server";
51
+ name;
52
+ channel;
53
+ constructor(key) {
54
+ super();
55
+ this.key = key;
56
+ this.program = key.program;
57
+ this.name = key.name;
58
+ this.channel = new ServerChannelHandle(key);
59
+ bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
60
+ }
61
+ async disabled() {
62
+ const answer = await wire.request(["service-disabled", this.key]);
63
+ return answer[0];
64
+ }
65
+ }
66
+ class ClientHandler extends ClientServiceBase {
67
+ key;
68
+ program;
69
+ endpoint = "client";
70
+ name;
71
+ channel;
72
+ constructor(key) {
73
+ super();
74
+ this.key = key;
75
+ this.program = key.program;
76
+ this.name = key.name;
77
+ this.channel = new ClientChannelHandle(key);
78
+ bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
79
+ }
80
+ async disabled() {
81
+ const answer = await wire.request(["service-disabled", this.key]);
82
+ return answer[0];
83
+ }
84
+ }
85
+ export function service(key) {
86
+ if (!isServiceKey(key))
87
+ throw new Error("A complete service key is required");
88
+ const normalized = Object.freeze({ program: key.program, endpoint: key.endpoint, name: key.name });
89
+ const identity = JSON.stringify([normalized.program, normalized.endpoint, normalized.name]);
90
+ return handles.obtain(`service:${identity}`, () => {
91
+ return normalized.endpoint === "server"
92
+ ? new ServerHandler(normalized)
93
+ : new ClientHandler(normalized);
94
+ });
95
+ }
96
+ export async function enableCurrentService(name) {
97
+ await wire.request(["enable-service", name]);
98
+ }
99
+ export async function disableCurrentService() {
100
+ await wire.request(["disable-service"]);
101
+ }
102
+ export async function endpointService(target, endpoint) {
103
+ const answer = await wire.request(["endpoint-service", target, endpoint]);
104
+ return answer[0] ? service(answer[0]) : null;
105
+ }
106
+ function serviceEvents(key, scope) {
107
+ return [
108
+ (event, listener) => wire.followService(key, scope, event, listener),
109
+ (observer) => wire.followService(key, scope, null, (event, payload) => {
110
+ if (typeof event === "string")
111
+ observer(event, payload);
112
+ })
113
+ ];
114
+ }
115
+ function bindEvents(target, events) {
116
+ Object.assign(target, {
117
+ subscribe: events.subscribe.bind(events),
118
+ waitFor: events.waitFor.bind(events),
119
+ events: events.events.bind(events),
120
+ observe: events.observe.bind(events)
121
+ });
122
+ }
package/dist/wire.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Cleanup } from "@phreshos/core";
1
+ import type { Cleanup, ServiceKey } from "@phreshos/core";
2
2
  import Deadline from "./deadline.js";
3
3
  import type { HandleAddress } from "./domain.js";
4
4
  type Handler = (...values: unknown[]) => unknown;
@@ -32,6 +32,8 @@ declare class Wire {
32
32
  observe(target: HandleAddress | null, half: "server" | "client", kind: TrafficKind, event: string | null, handler: Handler, impossible?: Failure): Cleanup;
33
33
  /** Follow destinationless events emitted by one Endpoint. */
34
34
  follow(target: HandleAddress | null, half: "server" | "client", event: string | null, handler: Handler, impossible?: Failure): Cleanup;
35
+ /** Follow one exact service lifecycle or application event route. */
36
+ followService(key: ServiceKey, scope: "lifecycle" | "channel", event: string | null, handler: Handler): Cleanup;
35
37
  samplePointer(movementX: number, movementY: number): void;
36
38
  private register;
37
39
  private unregister;
package/dist/wire.js CHANGED
@@ -194,6 +194,16 @@ class Wire {
194
194
  this.send("end-host", "unfollow", subscription);
195
195
  });
196
196
  }
197
+ /** Follow one exact service lifecycle or application event route. */
198
+ followService(key, scope, event, handler) {
199
+ const subscription = crypto.randomUUID();
200
+ const stop = this.on("service-event", subscription, handler);
201
+ this.send("end-host", "service-follow", subscription, key, scope, event);
202
+ return once(() => {
203
+ stop();
204
+ this.send("end-host", "service-unfollow", subscription);
205
+ });
206
+ }
197
207
  samplePointer(movementX, movementY) {
198
208
  this.send("end-host", "pointerSample", movementX, movementY);
199
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/client",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "The SDK used by a Program's client endpoint.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -46,10 +46,10 @@
46
46
  "prepack": "node --run build"
47
47
  },
48
48
  "peerDependencies": {
49
- "@phreshos/core": "^0.1.2"
49
+ "@phreshos/core": "^0.1.3"
50
50
  },
51
51
  "devDependencies": {
52
- "@phreshos/core": "^0.1.2",
52
+ "@phreshos/core": "^0.1.3",
53
53
  "typescript": "^6.0.3"
54
54
  }
55
55
  }