@phreshos/server 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 { endpoint } 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 ServerChannel 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 ServerChannel 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
  answer(event, answerer) {
15
18
  return wire.answer("end-end", event, value => answerer(message(value)));
16
19
  }
package/dist/current.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import type { Channel as CoreChannel } from "@phreshos/core";
2
2
  import { type Answerer } from "./channel.js";
3
- import { Client, type Process, type Program } from "./domain.js";
3
+ import { Client, type Process, type Program, type Server } from "./domain.js";
4
4
  /** The current Process's canonical Client handle. */
5
5
  export type CurrentClient<Events extends object = {}> = Client<Events>;
6
6
  /** Current Server context: its inbound Channel, owner hierarchy, and paired Client. */
7
- export interface Current<Events extends object = {}> extends CoreChannel<Events> {
7
+ export interface Current<Events extends object = {}> extends CoreChannel<Events>, Pick<Server, "service"> {
8
8
  /** The same Client handle exposed by the current Process. */
9
9
  readonly client: CurrentClient;
10
10
  /** Registers one answerer; omitting its return produces `undefined`. */
package/dist/current.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { channel } from "./channel.js";
2
2
  import { Client, TrafficHandle, bindEvents, endpointEvents, process, program, window } from "./domain.js";
3
3
  import wire from "./wire.js";
4
+ import { endpointService } from "./service.js";
4
5
  const ClientBase = Client;
5
6
  class CurrentClientHandle extends ClientBase {
6
7
  owner;
@@ -24,6 +25,7 @@ class CurrentClientHandle extends ClientBase {
24
25
  await wire.request(["start-endpoint", undefined, "client", overrides]);
25
26
  }
26
27
  async stop() { await wire.request(["stop-endpoint", undefined, "client"]); }
28
+ service() { return endpointService(null, "client"); }
27
29
  }
28
30
  let ownerPromise = null;
29
31
  let currentClient;
@@ -64,6 +66,7 @@ class ServerCurrent {
64
66
  return answer[0];
65
67
  }
66
68
  async stop() { await wire.request(["stop-current"]); }
69
+ service() { return endpointService(null, "server"); }
67
70
  }
68
71
  function bindChannel(target, source) {
69
72
  Object.assign(target, {
@@ -71,7 +74,9 @@ function bindChannel(target, source) {
71
74
  subscribe: source.subscribe.bind(source),
72
75
  waitFor: source.waitFor.bind(source),
73
76
  events: source.events.bind(source),
74
- observe: source.observe.bind(source)
77
+ observe: source.observe.bind(source),
78
+ enableService: source.enableService.bind(source),
79
+ disableService: source.disableService.bind(source)
75
80
  });
76
81
  }
77
82
  /** Inbound events, owner hierarchy, and paired Client for the current Server. */
package/dist/domain.js CHANGED
@@ -7,6 +7,7 @@ import { area, sql, store } from "./storage.js";
7
7
  import startup, {} from "./startup.js";
8
8
  import permissions from "./permissions.js";
9
9
  import wire from "./wire.js";
10
+ import { endpointService } from "./service.js";
10
11
  const handles = new HandleRegistry();
11
12
  const ProgramBase = CoreProgram;
12
13
  const ProcessBase = CoreProcess;
@@ -201,6 +202,7 @@ class ServerHandle extends ServerBase {
201
202
  }
202
203
  async start() { await wire.request(["start-endpoint", this.owner.address, "server"]); }
203
204
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "server"]); }
205
+ service() { return endpointService(this.owner.address, "server"); }
204
206
  async waitReady(timeout) {
205
207
  await wire.request(["wait-ready", this.owner.address], timeout);
206
208
  }
@@ -247,6 +249,7 @@ class ClientHandle extends ClientBase {
247
249
  await wire.request(["start-endpoint", this.owner.address, "client", overrides]);
248
250
  }
249
251
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "client"]); }
252
+ service() { return endpointService(this.owner.address, "client"); }
250
253
  }
251
254
  class WindowHandle extends Events {
252
255
  target;
package/dist/host.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { DesktopWallpaper, Exit, FileWallpaper, Layer, Position, ServedFile, Size, Subscribable, ThemeProperties, WritableTheme } from "@phreshos/core";
1
+ import type { DesktopWallpaper, Exit, FileWallpaper, Layer, Position, ServedFile, ClientServiceHandler, ServerServiceHandler, Size, Subscribable, ThemeProperties, WritableTheme } from "@phreshos/core";
2
2
  import { type Client, type Process, type Program, type Server } from "./domain.js";
3
3
  /** Resolved production description for a Program's Server. */
4
4
  export type ServerDescription = Readonly<{
@@ -107,6 +107,18 @@ export interface Host<Events extends object = {}> extends Subscribable<HostEvent
107
107
  processes(): Promise<Process[]>;
108
108
  /** Returns the live Process with the given runtime identity. */
109
109
  getProcess(identity: string): Promise<Process>;
110
+ /** Returns a stable handle for one exact Server service identity. */
111
+ service<ServiceEvents extends object = {}>(key: {
112
+ program: string;
113
+ endpoint: "server";
114
+ name: string;
115
+ }): ServerServiceHandler<ServiceEvents>;
116
+ /** Returns a stable handle for one exact Client service identity. */
117
+ service<ServiceEvents extends object = {}>(key: {
118
+ program: string;
119
+ endpoint: "client";
120
+ name: string;
121
+ }): ClientServiceHandler<ServiceEvents>;
110
122
  }
111
123
  /** Authoritative system capabilities for the currently executing Server. */
112
124
  export declare const host: Host;
package/dist/host.js CHANGED
@@ -4,6 +4,7 @@ import serve from "./served.js";
4
4
  import ServerTheme from "./theme.js";
5
5
  import { ServerDesktopWallpaper, ServerSignInWallpaper } from "./wallpaper.js";
6
6
  import wire from "./wire.js";
7
+ import { service as serviceHandle } from "./service.js";
7
8
  class ServerHost extends Events {
8
9
  theme = new ServerTheme();
9
10
  signInWallpaper = new ServerSignInWallpaper();
@@ -35,6 +36,9 @@ class ServerHost extends Events {
35
36
  const answer = await wire.request(["process", identity]);
36
37
  return process(answer[0]);
37
38
  }
39
+ service(key) {
40
+ return serviceHandle(key);
41
+ }
38
42
  }
39
43
  function hostEvent(event, values) {
40
44
  if (event === "endpointStart" || event === "endpointStop")
package/dist/main.d.ts CHANGED
@@ -2,5 +2,6 @@ export { host, type Host, type ClientDescription, type HostEvents, type HostProc
2
2
  export { current, type Current, type CurrentClient } from "./current.js";
3
3
  export { type Answerer, type Channel } from "./channel.js";
4
4
  export { type ProgramStartup } from "./startup.js";
5
+ export { ClientServiceHandler, ServerServiceHandler, ServiceHandler, type ClientServiceChannel, type ServerServiceChannel, type ServiceChannel, type ServiceKey, type ServiceLifecycleEvents } from "@phreshos/core";
5
6
  export { Client, Endpoint, Process, Program, Server, type Window, type ProgramArea } from "./domain.js";
6
7
  export type { AnswerCapture, AnswerMessage, AnswerObserver, Askable, AskCapture, AskMessage, AskObserver, Capture, Captures, ChannelCapture, ChannelEvents, ChannelMessage, ClientTraffic, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointTraffic, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, Position, ProgramEvents, ProgramPermissions, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, ServerTraffic, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TrafficCapture, TrafficEvents, TrafficMessage, Value, Theme, ThemeEvents, ThemeProperties, WallpaperLaunch, FileWallpaper, DesktopWallpaper, WritableTheme, WindowEvents, WindowLayer, WindowState, WindowSurface, WindowSurfaceEasing, WindowSurfaceSettings, WindowSurfaceTransaction } from "@phreshos/core";
package/dist/main.js CHANGED
@@ -2,4 +2,5 @@ export { host } from "./host.js";
2
2
  export { current } from "./current.js";
3
3
  export {} from "./channel.js";
4
4
  export {} from "./startup.js";
5
+ export { ClientServiceHandler, ServerServiceHandler, ServiceHandler } from "@phreshos/core";
5
6
  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,123 @@
1
+ import { ClientServiceHandler as CoreClientServiceHandler, ServerServiceHandler as CoreServerServiceHandler, isServiceKey } from "@phreshos/core";
2
+ import { randomUUID } from "node:crypto";
3
+ import Deadline from "./deadline.js";
4
+ import Events from "./events.js";
5
+ import HandleRegistry from "./handle-registry.js";
6
+ import wire from "./wire.js";
7
+ const handles = new HandleRegistry();
8
+ const ServerServiceBase = CoreServerServiceHandler;
9
+ const ClientServiceBase = CoreClientServiceHandler;
10
+ class ServerChannelHandle extends Events {
11
+ key;
12
+ constructor(key) {
13
+ super(...serviceEvents(key, "channel"));
14
+ this.key = key;
15
+ }
16
+ publish(event, payload = undefined) {
17
+ wire.send("end-host", "service-send", this.key, event, payload);
18
+ }
19
+ async ask(event, payload = undefined) {
20
+ return await this.askWithin(new Deadline(), event, payload);
21
+ }
22
+ timeout(milliseconds) {
23
+ return {
24
+ ask: (event, payload = undefined) => {
25
+ return this.askWithin(new Deadline(milliseconds), event, payload);
26
+ }
27
+ };
28
+ }
29
+ async askWithin(deadline, event, payload) {
30
+ const identity = await wire.identity();
31
+ const address = `server:${identity.process}:${randomUUID()}`;
32
+ const question = randomUUID();
33
+ const waiting = wire.expectWithin(address, deadline);
34
+ wire.send("end-host", "service-ask", this.key, address, question, event, payload);
35
+ try {
36
+ return await waiting;
37
+ }
38
+ finally {
39
+ wire.forget(address);
40
+ }
41
+ }
42
+ }
43
+ class ClientChannelHandle extends Events {
44
+ constructor(key) {
45
+ super(...serviceEvents(key, "channel"));
46
+ }
47
+ }
48
+ class ServerHandler extends ServerServiceBase {
49
+ key;
50
+ program;
51
+ endpoint = "server";
52
+ name;
53
+ channel;
54
+ constructor(key) {
55
+ super();
56
+ this.key = key;
57
+ this.program = key.program;
58
+ this.name = key.name;
59
+ this.channel = new ServerChannelHandle(key);
60
+ bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
61
+ }
62
+ async disabled() {
63
+ const answer = await wire.request(["service-disabled", this.key]);
64
+ return answer[0];
65
+ }
66
+ }
67
+ class ClientHandler extends ClientServiceBase {
68
+ key;
69
+ program;
70
+ endpoint = "client";
71
+ name;
72
+ channel;
73
+ constructor(key) {
74
+ super();
75
+ this.key = key;
76
+ this.program = key.program;
77
+ this.name = key.name;
78
+ this.channel = new ClientChannelHandle(key);
79
+ bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
80
+ }
81
+ async disabled() {
82
+ const answer = await wire.request(["service-disabled", this.key]);
83
+ return answer[0];
84
+ }
85
+ }
86
+ export function service(key) {
87
+ if (!isServiceKey(key))
88
+ throw new Error("A complete service key is required");
89
+ const normalized = Object.freeze({ program: key.program, endpoint: key.endpoint, name: key.name });
90
+ const identity = JSON.stringify([normalized.program, normalized.endpoint, normalized.name]);
91
+ return handles.obtain(`service:${identity}`, () => {
92
+ return normalized.endpoint === "server"
93
+ ? new ServerHandler(normalized)
94
+ : new ClientHandler(normalized);
95
+ });
96
+ }
97
+ export async function enableCurrentService(name) {
98
+ await wire.request(["enable-service", name]);
99
+ }
100
+ export async function disableCurrentService() {
101
+ await wire.request(["disable-service"]);
102
+ }
103
+ export async function endpointService(target, endpoint) {
104
+ const answer = await wire.request(["endpoint-service", target, endpoint]);
105
+ return answer[0] ? service(answer[0]) : null;
106
+ }
107
+ function serviceEvents(key, scope) {
108
+ return [
109
+ (event, listener) => wire.followService(key, scope, event, listener),
110
+ (observer) => wire.followService(key, scope, null, (event, payload) => {
111
+ if (typeof event === "string")
112
+ observer(event, payload);
113
+ })
114
+ ];
115
+ }
116
+ function bindEvents(target, events) {
117
+ Object.assign(target, {
118
+ subscribe: events.subscribe.bind(events),
119
+ waitFor: events.waitFor.bind(events),
120
+ events: events.events.bind(events),
121
+ observe: events.observe.bind(events)
122
+ });
123
+ }
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;
@@ -30,6 +30,8 @@ declare class Wire {
30
30
  observe(target: HandleAddress | null, half: "server" | "client", kind: TrafficKind, event: string | null, handler: Handler, impossible?: Failure): Cleanup;
31
31
  /** Follow destinationless events emitted by one Endpoint. */
32
32
  follow(target: HandleAddress | null, half: "server" | "client", event: string | null, handler: Handler, impossible?: Failure): Cleanup;
33
+ /** Follow one exact service lifecycle or application event route. */
34
+ followService(key: ServiceKey, scope: "lifecycle" | "channel", event: string | null, handler: Handler): Cleanup;
33
35
  private register;
34
36
  private unregister;
35
37
  private deliver;
package/dist/wire.js CHANGED
@@ -156,6 +156,16 @@ class Wire {
156
156
  this.send("end-host", "unfollow", subscription);
157
157
  });
158
158
  }
159
+ /** Follow one exact service lifecycle or application event route. */
160
+ followService(key, scope, event, handler) {
161
+ const subscription = randomUUID();
162
+ const stop = this.on("service-event", subscription, handler);
163
+ this.send("end-host", "service-follow", subscription, key, scope, event);
164
+ return once(() => {
165
+ stop();
166
+ this.send("end-host", "service-unfollow", subscription);
167
+ });
168
+ }
159
169
  register(kind, route, event, subject, impossible) {
160
170
  const subscription = randomUUID();
161
171
  if (impossible)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/server",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "The SDK used by a Program's server 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
  "@types/node": "^26.2.0",
54
54
  "typescript": "^6.0.3"
55
55
  }