@phreshos/server 0.1.0 → 0.1.2

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/domain.js CHANGED
@@ -1,142 +1,176 @@
1
- import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, Window as CoreWindow } from "@phreshos/core";
1
+ import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer } from "@phreshos/core";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import Events from "./events.js";
4
4
  import Deadline from "./deadline.js";
5
+ import HandleRegistry from "./handle-registry.js";
5
6
  import { area, sql, store } from "./storage.js";
7
+ import startup, {} from "./startup.js";
8
+ import permissions from "./permissions.js";
6
9
  import wire from "./wire.js";
10
+ const handles = new HandleRegistry();
7
11
  const ProgramBase = CoreProgram;
8
12
  const ProcessBase = CoreProcess;
9
13
  const ServerBase = CoreServer;
10
14
  const ClientBase = CoreClient;
11
- const WindowBase = CoreWindow;
12
15
  class ProgramHandle extends ProgramBase {
13
16
  identity;
14
- name;
15
- version;
16
- description;
17
- server;
18
- client;
17
+ reference;
19
18
  data;
20
19
  cache;
21
20
  store;
22
21
  logs;
23
22
  database;
23
+ startup;
24
+ permissions;
25
+ record;
24
26
  constructor(record) {
25
27
  super();
26
28
  this.identity = record.identity;
27
- this.name = record.name;
28
- this.version = record.version;
29
- this.description = record.description;
30
- this.server = record.server;
31
- this.client = record.client;
32
- this.data = area(record.identity, "data");
33
- this.cache = area(record.identity, "cache");
34
- this.store = store(record.identity);
35
- this.logs = sql("logs", record.identity);
36
- this.database = sql("database", record.identity);
37
- bindEvents(this, scoped("host-end", record.identity, programEvent));
29
+ this.reference = record.reference;
30
+ this.record = record;
31
+ this.data = area(this.address, "data");
32
+ this.cache = area(this.address, "cache");
33
+ this.store = store(this.address);
34
+ this.logs = sql("logs", this.address);
35
+ this.database = sql("database", this.address);
36
+ this.startup = startup(this.address);
37
+ this.permissions = permissions(this.address);
38
+ bindEvents(this, scoped("host-end", record.reference, programEvent));
39
+ }
40
+ get name() { return this.record.name; }
41
+ get version() { return this.record.version; }
42
+ get description() { return this.record.description; }
43
+ get server() { return this.record.server; }
44
+ get client() { return this.record.client; }
45
+ get address() { return { identity: this.identity, reference: this.reference }; }
46
+ update(record) {
47
+ if (record.reference !== this.reference)
48
+ throw new Error("A Program handle cannot become another Program");
49
+ this.record = record;
38
50
  }
39
51
  async processes() {
40
- const answer = await wire.request(["processes", this.identity]);
41
- return answer[0].map(process);
52
+ const answer = await wire.request(["processes", this.address]);
53
+ return answer[0].map(record => process(record));
54
+ }
55
+ async firstProcess() {
56
+ return chronological(await this.processes())[0] ?? null;
57
+ }
58
+ async lastProcess() {
59
+ return chronological(await this.processes()).at(-1) ?? null;
42
60
  }
43
61
  async getProcess(identityOrName) {
44
- const answer = await wire.request(["program-process", this.identity, identityOrName]);
62
+ const answer = await wire.request(["program-process", this.address, identityOrName]);
45
63
  return answer[0] ? process(answer[0]) : null;
46
64
  }
47
65
  async createProcess(launch = {}) {
48
- const answer = await wire.request(["create-process", this.identity, launch]);
66
+ const answer = await wire.request(["create-process", this.address, launch]);
49
67
  return process(answer[0]);
50
68
  }
51
69
  async apiDocs() {
52
- const answer = await wire.request(["api-docs", this.identity]);
70
+ const answer = await wire.request(["api-docs", this.address]);
53
71
  return answer[0];
54
72
  }
73
+ async icon(size = "medium") {
74
+ const answer = await wire.request(["icon", this.address, size]);
75
+ return new Blob([Uint8Array.from(answer[0])], { type: "image/png" });
76
+ }
55
77
  async installed() {
56
- const answer = await wire.request(["installed", this.identity]);
78
+ const answer = await wire.request(["installed", this.address]);
57
79
  return answer[0];
58
80
  }
59
81
  async install() {
60
- await wire.request(["install", this.identity]);
82
+ await wire.request(["install", this.address]);
61
83
  return this;
62
84
  }
63
85
  async fork(identity) {
64
- const answer = await wire.request(["fork", this.identity, identity]);
86
+ const answer = await wire.request(["fork", this.address, identity]);
65
87
  return program(answer[0]);
66
88
  }
67
89
  async uninstall(everything = false) {
68
- await wire.request(["uninstall", this.identity, everything]);
90
+ await wire.request(["uninstall", this.address, everything]);
69
91
  }
70
92
  async forget() {
71
- await wire.request(["forget", this.identity]);
93
+ await wire.request(["forget", this.address]);
72
94
  }
73
95
  async exitAll() {
74
- const answer = await wire.request(["exit-all", this.identity]);
96
+ const answer = await wire.request(["exit-all", this.address]);
75
97
  return answer[0];
76
98
  }
77
99
  }
100
+ /** Internal transport address for a Program handle created by this SDK. */
101
+ export function programAddress(value) {
102
+ if (!(value instanceof ProgramHandle))
103
+ throw new Error("A Program handle is required");
104
+ return value.address;
105
+ }
106
+ function chronological(processes) {
107
+ return processes.sort((left, right) => left.startedAt.getTime() - right.startedAt.getTime());
108
+ }
78
109
  class ProcessHandle extends ProcessBase {
79
110
  identity;
111
+ reference;
80
112
  name;
81
113
  startedAt;
82
114
  server;
83
115
  client;
84
116
  ownerProgram;
85
117
  options;
86
- constructor(record) {
118
+ constructor(record, endpoints = {}) {
87
119
  super();
88
120
  this.identity = record.identity;
121
+ this.reference = record.reference;
89
122
  this.name = record.name;
90
123
  this.startedAt = new Date(record.startedAt);
91
124
  this.ownerProgram = program(record.program);
92
125
  this.options = record.options;
93
- this.server = new ServerHandle(this);
94
- this.client = new ClientHandle(this);
95
- bindEvents(this, scoped("process-host", record.identity, processEvent));
126
+ this.server = endpointHandle(this, "server", endpoints.server);
127
+ this.client = endpointHandle(this, "client", endpoints.client);
128
+ bindEvents(this, scoped("process-host", record.reference, processEvent));
96
129
  }
97
130
  program() { return this.ownerProgram; }
131
+ get address() { return { identity: this.identity, reference: this.reference }; }
98
132
  async parent() {
99
- const answer = await wire.request(["parent", this.identity]);
133
+ const answer = await wire.request(["parent", this.address]);
100
134
  return answer[0] ? process(answer[0]) : null;
101
135
  }
102
136
  async option(name) {
103
137
  if (name in this.options)
104
138
  return this.options[name];
105
- const answer = await wire.request(["option", this.identity, name]);
139
+ const answer = await wire.request(["option", this.address, name]);
106
140
  return answer[0];
107
141
  }
108
142
  async exit() {
109
- await wire.request(["exit", this.identity]);
143
+ await wire.request(["exit", this.address]);
110
144
  }
111
145
  async exited() {
112
- const answer = await wire.request(["exited", this.identity]);
146
+ const answer = await wire.request(["exited", this.address]);
113
147
  return answer[0];
114
148
  }
115
149
  }
116
- class TrafficHandle extends Events {
117
- owner;
150
+ export class TrafficHandle extends Events {
151
+ target;
118
152
  kind;
119
- constructor(owner, kind) {
120
- super((event, listener, impossible) => wire.observe(owner.identity, kind, "publish", event, value => {
153
+ constructor(target, kind) {
154
+ super((event, listener, impossible) => wire.observe(target, kind, "publish", event, value => {
121
155
  listener(trafficMessage(value));
122
- }, impossible), observer => wire.observe(owner.identity, kind, "publish", null, (event, value) => {
156
+ }, impossible), observer => wire.observe(target, kind, "publish", null, (event, value) => {
123
157
  if (typeof event === "string")
124
158
  observer(event, trafficMessage(value));
125
159
  }));
126
- this.owner = owner;
160
+ this.target = target;
127
161
  this.kind = kind;
128
162
  }
129
163
  observeAsks(observer) {
130
- return wire.observe(this.owner.identity, this.kind, "ask", null, (event, questionId, message) => {
164
+ return wire.observe(this.target, this.kind, "ask", null, (event, questionId, message) => {
131
165
  if (typeof event !== "string" || typeof questionId !== "string")
132
166
  return;
133
167
  observer({ event, questionId, message: trafficMessage(message) });
134
168
  });
135
169
  }
136
170
  }
137
- class ServerTrafficHandle extends TrafficHandle {
171
+ export class ServerTrafficHandle extends TrafficHandle {
138
172
  observeAnswers(observer) {
139
- return wire.observe(this.owner.identity, "server", "answer", null, (event, questionId, message) => {
173
+ return wire.observe(this.target, "server", "answer", null, (event, questionId, message) => {
140
174
  if (typeof event !== "string" || typeof questionId !== "string")
141
175
  return;
142
176
  const raw = message;
@@ -154,35 +188,36 @@ class ServerHandle extends ServerBase {
154
188
  constructor(owner) {
155
189
  super();
156
190
  this.owner = owner;
157
- this.traffic = new ServerTrafficHandle(owner, "server");
191
+ this.traffic = new ServerTrafficHandle(owner.address, "server");
192
+ bindEvents(this, endpointEvents(owner.address, "server"));
158
193
  }
159
- process() { return this.owner; }
160
- publish(event, payload) {
161
- wire.send("end-host", "send", this.owner.identity, "server", event, payload);
194
+ async process() { return this.owner; }
195
+ publish(event, payload = undefined) {
196
+ wire.send("end-host", "send", this.owner.address, "server", event, payload);
162
197
  }
163
198
  async exists() {
164
- const answer = await wire.request(["exists", "server", this.owner.identity]);
199
+ const answer = await wire.request(["exists", "server", this.owner.address]);
165
200
  return answer[0];
166
201
  }
167
- async start() { await wire.request(["start-endpoint", this.owner.identity, "server"]); }
168
- async stop() { await wire.request(["stop-endpoint", this.owner.identity, "server"]); }
202
+ async start() { await wire.request(["start-endpoint", this.owner.address, "server"]); }
203
+ async stop() { await wire.request(["stop-endpoint", this.owner.address, "server"]); }
169
204
  async waitReady(timeout) {
170
- await wire.request(["wait-ready", this.owner.identity], timeout);
205
+ await wire.request(["wait-ready", this.owner.address], timeout);
171
206
  }
172
- async ask(event, payload) {
207
+ async ask(event, payload = undefined) {
173
208
  return this.askWithin(undefined, event, payload);
174
209
  }
175
210
  timeout(milliseconds) {
176
- return { ask: (event, payload) => this.askWithin(milliseconds, event, payload) };
211
+ return { ask: (event, payload = undefined) => this.askWithin(milliseconds, event, payload) };
177
212
  }
178
213
  async askWithin(timeout, event, payload) {
179
214
  const deadline = new Deadline(timeout);
180
- await wire.requestWithin(["wait-ready", this.owner.identity, true], deadline);
181
- const identity = await wire.identity;
215
+ await wire.requestWithin(["wait-ready", this.owner.address, true], deadline);
216
+ const identity = await wire.identity();
182
217
  const address = `server:${identity.process}:${randomUUID()}`;
183
218
  const questionId = randomUUID();
184
219
  const waiting = wire.expectWithin(address, deadline);
185
- wire.send("end-host", "ask", this.owner.identity, "server", address, questionId, event, payload);
220
+ wire.send("end-host", "ask", this.owner.address, "server", address, questionId, event, payload);
186
221
  try {
187
222
  return await waiting;
188
223
  }
@@ -194,36 +229,35 @@ class ServerHandle extends ServerBase {
194
229
  class ClientHandle extends ClientBase {
195
230
  owner;
196
231
  traffic;
232
+ window;
197
233
  constructor(owner) {
198
234
  super();
199
235
  this.owner = owner;
200
- this.traffic = new TrafficHandle(owner, "client");
236
+ this.traffic = new TrafficHandle(owner.address, "client");
237
+ this.window = window(async () => owner.address);
238
+ bindEvents(this, endpointEvents(owner.address, "client"));
201
239
  }
202
- process() { return this.owner; }
203
- publish(event, payload) { wire.send("end-host", "send", this.owner.identity, "client", event, payload); }
240
+ async process() { return this.owner; }
241
+ publish(event, payload = undefined) { wire.send("end-host", "send", this.owner.address, "client", event, payload); }
204
242
  async exists() {
205
- const answer = await wire.request(["exists", "client", this.owner.identity]);
243
+ const answer = await wire.request(["exists", "client", this.owner.address]);
206
244
  return answer[0];
207
245
  }
208
246
  async start(overrides = {}) {
209
- await wire.request(["start-endpoint", this.owner.identity, "client", overrides]);
210
- }
211
- async stop() { await wire.request(["stop-endpoint", this.owner.identity, "client"]); }
212
- async window() {
213
- await wire.request(["window", this.owner.identity]);
214
- return new WindowHandle(this);
247
+ await wire.request(["start-endpoint", this.owner.address, "client", overrides]);
215
248
  }
249
+ async stop() { await wire.request(["stop-endpoint", this.owner.address, "client"]); }
216
250
  }
217
- class WindowHandle extends WindowBase {
218
- owner;
219
- constructor(owner) {
220
- super();
221
- this.owner = owner;
222
- bindEvents(this, scoped("host-end", owner.process().identity, (_event, values) => values[0]));
251
+ class WindowHandle extends Events {
252
+ target;
253
+ surface;
254
+ constructor(target) {
255
+ super(...deferredScoped("host-end", target, (_event, values) => values[0]));
256
+ this.target = target;
257
+ this.surface = new WindowSurfaceHandle(target);
223
258
  }
224
- client() { return this.owner; }
225
259
  async state() {
226
- const answer = await wire.request(["window", this.owner.process().identity]);
260
+ const answer = await wire.request(["window", await this.target()]);
227
261
  return answer[0];
228
262
  }
229
263
  async title() { return (await this.state()).title; }
@@ -233,11 +267,62 @@ class WindowHandle extends WindowBase {
233
267
  async front() { return (await this.state()).front; }
234
268
  async layer() { return (await this.state()).layer; }
235
269
  async location() { return (await this.state()).location; }
236
- async move(position) { await wire.request(["move", this.owner.process().identity, position]); }
237
- async resize(size) { await wire.request(["resize", this.owner.process().identity, size]); }
238
- async minimize(minimized = true) { await wire.request(["minimize", this.owner.process().identity, minimized]); }
239
- async changeTitle(title) { await wire.request(["changeTitle", this.owner.process().identity, title]); }
240
- async raise() { await wire.request(["raise", this.owner.process().identity]); }
270
+ async move(position) { await wire.request(["move", await this.target(), position]); }
271
+ async resize(size) { await wire.request(["resize", await this.target(), size]); }
272
+ async minimize(minimized = true) { await wire.request(["minimize", await this.target(), minimized]); }
273
+ async changeTitle(title) { await wire.request(["changeTitle", await this.target(), title]); }
274
+ async raise() { await wire.request(["raise", await this.target()]); }
275
+ }
276
+ class WindowSurfaceHandle {
277
+ target;
278
+ constructor(target) {
279
+ this.target = target;
280
+ }
281
+ async set(settings = {}) { await wire.request(["surfaceSet", await this.target(), settings]); }
282
+ async remove() { await wire.request(["surfaceRemove", await this.target()]); }
283
+ }
284
+ function deferredScoped(route, target, convert) {
285
+ return [
286
+ (event, listener, impossible) => {
287
+ if (!windowEvent(event)) {
288
+ impossible?.(new Error(`A Window has no "${event}" event`));
289
+ return () => undefined;
290
+ }
291
+ return deferred(target, subject => wire.on(route, event, (...values) => {
292
+ const message = unscoped(subject, values);
293
+ if (message)
294
+ listener(convert(event, message));
295
+ }, subject, impossible), impossible);
296
+ },
297
+ observer => deferred(target, subject => wire.onAll(route, (event, ...values) => {
298
+ if (typeof event !== "string" || !windowEvent(event))
299
+ return;
300
+ const message = unscoped(subject, values);
301
+ if (message)
302
+ observer(event, convert(event, message));
303
+ }, subject))
304
+ ];
305
+ }
306
+ function windowEvent(event) {
307
+ return event === "move" || event === "resize" || event === "minimize" || event === "changeTitle" || event === "front";
308
+ }
309
+ function deferred(target, register, impossible) {
310
+ let active = true;
311
+ let stop = () => undefined;
312
+ void target().then(address => {
313
+ if (active)
314
+ stop = register(address.reference);
315
+ }, error => {
316
+ const failure = error instanceof Error ? error : new Error(String(error));
317
+ if (active && impossible)
318
+ impossible(failure);
319
+ else if (active)
320
+ queueMicrotask(() => { throw failure; });
321
+ });
322
+ return () => {
323
+ active = false;
324
+ stop();
325
+ };
241
326
  }
242
327
  export function scoped(route, subject, convert) {
243
328
  return new Events((event, listener, impossible) => wire.on(route, event, (...values) => {
@@ -252,16 +337,23 @@ export function scoped(route, subject, convert) {
252
337
  observer(event, convert(event, message));
253
338
  }, subject));
254
339
  }
340
+ /** Destinationless events originating from one Endpoint handle. */
341
+ export function endpointEvents(target, half) {
342
+ return new Events((event, listener, impossible) => wire.follow(target, half, event, listener, impossible), observer => wire.follow(target, half, null, (event, payload) => {
343
+ if (typeof event === "string")
344
+ observer(event, payload);
345
+ }));
346
+ }
255
347
  function unscoped(subject, values) {
256
348
  if (subject === null)
257
349
  return values;
258
350
  return values[0] === subject ? values.slice(1) : null;
259
351
  }
260
352
  function programEvent(event, values) {
261
- if (event === "serverStart" || event === "clientStart" || event === "clientStop" || event === "processCreate")
353
+ if (event === "endpointStart" || event === "endpointStop")
354
+ return lifecycleEndpoint(values[0], values[1]);
355
+ if (event === "processCreate")
262
356
  return process(values[0]);
263
- if (event === "serverStop")
264
- return { process: process(values[0]), code: numberOrNull(values[1]), signal: stringOrNull(values[2]) };
265
357
  if (event === "processExit")
266
358
  return { process: process(values[0]), ...exit(values[1], values[2]) };
267
359
  if (event === "uninstall")
@@ -269,12 +361,20 @@ function programEvent(event, values) {
269
361
  return undefined;
270
362
  }
271
363
  function processEvent(event, values) {
272
- if (event === "serverStop")
273
- return { code: numberOrNull(values[0]), signal: stringOrNull(values[1]) };
364
+ if (event === "endpointStart" || event === "endpointStop")
365
+ return lifecycleEndpoint(values[0], values[1]);
274
366
  if (event === "exit")
275
367
  return exit(values[0], values[1]);
276
368
  return undefined;
277
369
  }
370
+ export function lifecycleEndpoint(record, kind) {
371
+ const owner = process(record);
372
+ if (kind === "server")
373
+ return owner.server;
374
+ if (kind === "client")
375
+ return owner.client;
376
+ throw new Error("The host returned an invalid Endpoint lifecycle event");
377
+ }
278
378
  export function exit(code, signal) {
279
379
  const namedSignal = stringOrNull(signal);
280
380
  return { status: namedSignal === null ? "exited" : "signaled", code: numberOrNull(code), signal: namedSignal };
@@ -294,20 +394,35 @@ export function bindEvents(target, events) {
294
394
  });
295
395
  }
296
396
  export function program(record) {
297
- return new ProgramHandle(record);
397
+ const handle = handles.obtain(`program:${record.reference}`, () => new ProgramHandle(record));
398
+ handle.update(record);
399
+ return handle;
298
400
  }
299
- export function process(record) {
300
- return new ProcessHandle(record);
401
+ export function process(record, endpoints = {}) {
402
+ return handles.obtain(`process:${record.reference}`, () => new ProcessHandle(record, endpoints));
301
403
  }
302
404
  export function endpoint(reference) {
303
405
  if (!reference)
304
406
  throw new Error("The boundary returned an invalid Endpoint reference");
305
- const owner = new ProcessHandle(reference.process);
407
+ const owner = process(reference.process);
306
408
  return reference.kind === "server" ? owner.server : owner.client;
307
409
  }
410
+ export function claimEndpoint(reference, kind, endpoint) {
411
+ return handles.adopt(`endpoint:${reference}:${kind}`, endpoint);
412
+ }
413
+ function endpointHandle(owner, kind, preferred) {
414
+ return handles.obtain(`endpoint:${owner.reference}:${kind}`, () => preferred ?? (kind === "server" ? new ServerHandle(owner) : new ClientHandle(owner)));
415
+ }
416
+ export function window(target) {
417
+ return new WindowHandle(target);
418
+ }
419
+ /** Runtime constructor used to identify and type Server-visible Program handles. */
308
420
  export const Program = CoreProgram;
421
+ /** Runtime constructor used to identify and type Server-visible Process handles. */
309
422
  export const Process = CoreProcess;
423
+ /** Runtime constructor used to identify and type Server-visible Endpoint handles. */
310
424
  export const Endpoint = CoreEndpoint;
425
+ /** Runtime constructor used to identify and type Server-visible Server handles. */
311
426
  export const Server = CoreServer;
427
+ /** Runtime constructor used to identify and type Server-visible Client handles. */
312
428
  export const Client = CoreClient;
313
- export const Window = CoreWindow;
@@ -0,0 +1,7 @@
1
+ /** Weak canonical references for system-backed domain handles in this SDK realm. */
2
+ export default class HandleRegistry {
3
+ private readonly handles;
4
+ private readonly released;
5
+ obtain<Value extends object>(key: string, create: () => Value): Value;
6
+ adopt<Value extends object>(key: string, value: Value): Value;
7
+ }
@@ -0,0 +1,24 @@
1
+ /** Weak canonical references for system-backed domain handles in this SDK realm. */
2
+ export default class HandleRegistry {
3
+ handles = new Map();
4
+ released = new FinalizationRegistry(({ key, reference }) => {
5
+ if (this.handles.get(key) === reference)
6
+ this.handles.delete(key);
7
+ });
8
+ obtain(key, create) {
9
+ const existing = this.handles.get(key)?.deref();
10
+ if (existing)
11
+ return existing;
12
+ const value = create();
13
+ const reference = new WeakRef(value);
14
+ this.handles.set(key, reference);
15
+ this.released.register(value, { key, reference });
16
+ return value;
17
+ }
18
+ adopt(key, value) {
19
+ const existing = this.handles.get(key)?.deref();
20
+ if (existing && existing !== value)
21
+ throw new Error(`The canonical handle for "${key}" already exists`);
22
+ return existing ?? this.obtain(key, () => value);
23
+ }
24
+ }
package/dist/host.d.ts CHANGED
@@ -1,65 +1,113 @@
1
- import type { Exit, Layer, Position, ServedFile, Size, Subscribable } from "@phreshos/core";
2
- import { type Process, type Program } from "./domain.js";
1
+ import type { DesktopWallpaper, Exit, FileWallpaper, Layer, Position, ServedFile, Size, Subscribable, ThemeProperties, WritableTheme } from "@phreshos/core";
2
+ import { type Client, type Process, type Program, type Server } from "./domain.js";
3
+ /** Resolved production description for a Program's Server. */
3
4
  export type ServerDescription = Readonly<{
5
+ /** Absolute directory containing the production Server files. */
4
6
  location: string;
7
+ /** Whether newly created Processes start this Server by default. */
5
8
  start?: boolean;
9
+ /** Command used to install the Server's production dependencies. */
6
10
  installCommand?: string;
11
+ /** Command used to start the Server from its production directory. */
7
12
  startCommand: string;
8
13
  }>;
14
+ /** Resolved production description for a Program's Client and initial Window. */
9
15
  export type ClientDescription = Readonly<{
16
+ /** Absolute directory containing the production Client files. */
10
17
  location: string;
18
+ /** Whether newly created Processes start this Client by default. */
11
19
  start?: boolean;
20
+ /** Default Window title. */
12
21
  title?: string;
22
+ /** Default Window size. */
13
23
  size?: Size;
24
+ /** Default Window position. */
14
25
  position?: Position;
26
+ /** Default Window layer. */
15
27
  layer?: Layer;
28
+ /** Whether the Window starts minimized. */
16
29
  minimize?: boolean;
17
30
  }>;
18
31
  type Description = Readonly<{
32
+ /** Stable identity assigned to the Program. */
19
33
  identity: string;
34
+ /** Human-readable Program name. */
20
35
  name?: string;
36
+ /** Declared Program version. */
21
37
  version?: string;
38
+ /** Short human-readable Program description. */
22
39
  description?: string;
40
+ /** Path to the Program-authored API entry document. */
23
41
  apiDocs?: string;
24
- icons?: string;
42
+ /** Absolute validated PNG source used to derive the Program's hosted icon sizes. */
43
+ icon?: string;
44
+ /** Absolute directory used for the Program's persistent storage. */
25
45
  storage: string;
26
46
  }>;
47
+ /** Complete runtime description used to create a Program. */
27
48
  export type ProgramDescription = Description & (Readonly<{
49
+ /** Required Server description when no Client is described. */
28
50
  server: ServerDescription;
51
+ /** Optional Client description. */
29
52
  client?: ClientDescription;
30
53
  }> | Readonly<{
54
+ /** Optional Server description. */
31
55
  server?: ServerDescription;
56
+ /** Required Client description when no Server is described. */
32
57
  client: ClientDescription;
33
58
  }>);
34
- export type HostServerStop = Omit<Exit, "status"> & Readonly<{
35
- process: Process;
36
- }>;
59
+ /** An uninstall reported with the affected Program and removal scope. */
37
60
  export type HostProgramUninstall = Readonly<{
61
+ /** Program that left the installed state. */
38
62
  program: Program;
63
+ /** Whether all installed resources, including storage, were removed. */
39
64
  everythingRemoved: boolean;
40
65
  }>;
66
+ /** A Process exit reported with the Process that ended. */
41
67
  export type HostProcessExit = Exit & Readonly<{
68
+ /** Process that ended. */
42
69
  process: Process;
43
70
  }>;
71
+ /** Authoritative lifecycle events visible to the Server host. */
44
72
  export type HostEvents = {
45
- serverStart: Process;
46
- serverStop: HostServerStop;
47
- clientStart: Process;
48
- clientStop: Process;
73
+ /** One Process Endpoint entered a new live incarnation. */
74
+ endpointStart: Server | Client;
75
+ /** One Process Endpoint incarnation ended. */
76
+ endpointStop: Server | Client;
77
+ /** A Program entered the runtime registry. */
49
78
  programCreate: Program;
79
+ /** A Program left the runtime registry. */
50
80
  programForget: Program;
81
+ /** A Program entered the installed state. */
51
82
  programInstall: Program;
83
+ /** A Program left the installed state. */
52
84
  programUninstall: HostProgramUninstall;
85
+ /** A Process entered the runtime set. */
53
86
  processCreate: Process;
87
+ /** A Process left the runtime set. */
54
88
  processExit: HostProcessExit;
55
89
  };
90
+ /** Authoritative system capabilities available to a Server endpoint. */
56
91
  export interface Host<Events extends object = {}> extends Subscribable<HostEvents & Events, never> {
92
+ /** Observable system Theme authority. */
93
+ readonly theme: WritableTheme<ThemeProperties>;
94
+ /** Authoritative wallpaper visible before authentication. */
95
+ readonly signInWallpaper: FileWallpaper;
96
+ /** Authoritative wallpaper visible within authenticated desktops. */
97
+ readonly desktopWallpaper: DesktopWallpaper;
98
+ /** Publishes a value through the host and returns its public file metadata. */
57
99
  serve(value: unknown): Promise<ServedFile>;
100
+ /** Returns all known Programs, optionally restricted to installed Programs. */
58
101
  programs(onlyInstalled?: boolean): Promise<Program[]>;
102
+ /** Returns the Program with the given stable identity. */
59
103
  getProgram(identity: string): Promise<Program>;
104
+ /** Creates a runtime Program from a description or description file path. */
60
105
  createProgram(source: ProgramDescription | string): Promise<Program>;
106
+ /** Returns every live Process known to the authoritative host. */
61
107
  processes(): Promise<Process[]>;
108
+ /** Returns the live Process with the given runtime identity. */
62
109
  getProcess(identity: string): Promise<Process>;
63
110
  }
111
+ /** Authoritative system capabilities for the currently executing Server. */
64
112
  export declare const host: Host;
65
113
  export {};