@phreshos/server 0.1.10 → 0.1.12
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 +40 -2
- package/dist/channel.d.ts +2 -2
- package/dist/channel.js +1 -1
- package/dist/current.js +2 -2
- package/dist/domain.d.ts +34 -14
- package/dist/domain.js +71 -28
- package/dist/host.d.ts +40 -35
- package/dist/host.js +38 -25
- package/dist/main.d.ts +3 -3
- package/dist/permissions.d.ts +2 -2
- package/dist/permissions.js +5 -5
- package/dist/service.d.ts +5 -5
- package/dist/service.js +13 -17
- package/dist/theme.js +4 -18
- package/dist/wire.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -113,9 +113,47 @@ waiter.
|
|
|
113
113
|
Server Program handles add `install()` and `fork()`. Their filesystem areas
|
|
114
114
|
also expose `path()` and traversal-safe `resolve()`, because filesystem work is
|
|
115
115
|
performed locally in the Server SDK after the Host supplies only the area root.
|
|
116
|
-
Object descriptions passed to `host.
|
|
116
|
+
Object descriptions passed to `host.program.create()` therefore require an
|
|
117
117
|
explicit absolute storage root as well as at least one declared Endpoint.
|
|
118
118
|
|
|
119
|
+
Host registries are separated by owner. Reads, commands, and the complete
|
|
120
|
+
subscription contract live on their relevant capability rather than directly
|
|
121
|
+
on `host`:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const programs = await host.program.list()
|
|
125
|
+
const program = await host.program.find("counter")
|
|
126
|
+
const processes = await host.process.list()
|
|
127
|
+
|
|
128
|
+
const stopPrograms = host.program.subscribe("create", value => undefined)
|
|
129
|
+
const stopProcesses = host.process.subscribe("exit", value => undefined)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
A Program owns its scoped Process capability:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const processes = await program.process.list()
|
|
136
|
+
const process = await program.process.find("worker")
|
|
137
|
+
const created = await program.process.create({ name: "worker" })
|
|
138
|
+
const shared = await program.process.findOrCreate({
|
|
139
|
+
name: "shared-server",
|
|
140
|
+
server: true,
|
|
141
|
+
client: false
|
|
142
|
+
})
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`findOrCreate()` is atomic at the authoritative Core. Concurrent equivalent
|
|
146
|
+
launches converge on one named Process; a different launch for that name
|
|
147
|
+
rejects instead of reconfiguring the existing Process.
|
|
148
|
+
|
|
149
|
+
`host.service(key)` creates the exact opaque service handle. The
|
|
150
|
+
handler exposes its authored `name`, `enabled()`, `waitReady()`, lifecycle
|
|
151
|
+
subscriptions, and channel; it does not expose its Program or Endpoint as
|
|
152
|
+
separate fields. Services are not a second public registry: inspect Programs
|
|
153
|
+
and their Endpoint declarations. `program.server?.hasService()` identifies a
|
|
154
|
+
declared Service capability, while `await program.server?.docs()` reads its
|
|
155
|
+
installed policy and API documentation before the Endpoint starts.
|
|
156
|
+
|
|
119
157
|
`program.icon()` requests a guaranteed PNG `Blob` in `small`, `medium`, or
|
|
120
158
|
`large` form without exposing the Program's source path or the system's private
|
|
121
159
|
asset-hosting address. Omitting the size selects `medium`.
|
|
@@ -137,6 +175,6 @@ await program.startup.disable()
|
|
|
137
175
|
```
|
|
138
176
|
|
|
139
177
|
The system validates this through the same launch contract as
|
|
140
|
-
`
|
|
178
|
+
`program.process.create()`. Setting startup does not create a Process immediately.
|
|
141
179
|
`uninstall(false)` preserves the configuration but makes it inactive until the
|
|
142
180
|
Program is installed again; removing everything deletes it.
|
package/dist/channel.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { Channel as CoreChannel, ChannelMessage, Cleanup
|
|
1
|
+
import type { Channel as CoreChannel, ChannelMessage, Cleanup } from "@phreshos/core";
|
|
2
2
|
import { type Endpoint } from "./domain.js";
|
|
3
3
|
/** Handles one question addressed to the current Server. */
|
|
4
4
|
export type Answerer<Payload = unknown, Result = undefined> = (message: ChannelMessage<Payload>) => Result | Promise<Result>;
|
|
5
5
|
/** Events and questions explicitly accepted by the current Server. */
|
|
6
|
-
export interface Channel<Events extends object = {}> extends CoreChannel<Events, Endpoint
|
|
6
|
+
export interface Channel<Events extends object = {}> extends CoreChannel<Events, Endpoint> {
|
|
7
7
|
/** Registers one answerer; omitting its return produces `undefined`. */
|
|
8
8
|
answer<Payload = unknown, Result = undefined>(event: string, answerer: Answerer<Payload, Result>): Cleanup;
|
|
9
9
|
}
|
package/dist/channel.js
CHANGED
|
@@ -12,7 +12,7 @@ class ServerChannel extends Events {
|
|
|
12
12
|
publish(event, payload = undefined) {
|
|
13
13
|
wire.send("end-host", "emit", event, payload);
|
|
14
14
|
}
|
|
15
|
-
async enableService(
|
|
15
|
+
async enableService(name) { await enableCurrentService(name); }
|
|
16
16
|
async disableService() { await disableCurrentService(); }
|
|
17
17
|
answer(event, answerer) {
|
|
18
18
|
return wire.answer("end-end", event, value => answerer(message(value)));
|
package/dist/current.js
CHANGED
|
@@ -31,7 +31,7 @@ let ownerPromise = null;
|
|
|
31
31
|
let currentClient;
|
|
32
32
|
function owner() {
|
|
33
33
|
if (!ownerPromise) {
|
|
34
|
-
const resolving = wire.request(["process"]).then(answer => {
|
|
34
|
+
const resolving = wire.request(["current-process"]).then(answer => {
|
|
35
35
|
return process(answer[0], { client: currentClient });
|
|
36
36
|
});
|
|
37
37
|
const retained = resolving.catch(error => {
|
|
@@ -58,7 +58,7 @@ class ServerCurrent {
|
|
|
58
58
|
return answer[0] ? process(answer[0]) : null;
|
|
59
59
|
}
|
|
60
60
|
async program() {
|
|
61
|
-
const answer = await wire.request(["program"]);
|
|
61
|
+
const answer = await wire.request(["current-program"]);
|
|
62
62
|
return program(answer[0]);
|
|
63
63
|
}
|
|
64
64
|
async option(name) {
|
package/dist/domain.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture, type AskCapture, type Cleanup, type ClientDeclaration, type
|
|
1
|
+
import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture, type AskCapture, type Cleanup, type ClientDeclaration, type Exit, type Launch, type Position, type ProgramPermission, type ProgramProcess as CoreProgramProcess, type ProgramArea as CoreProgramArea, type Size, type TrafficMessage, type Window as CoreWindow, type WindowState } from "@phreshos/core";
|
|
2
2
|
import Events from "./events.js";
|
|
3
3
|
import { type ProgramStartup } from "./startup.js";
|
|
4
4
|
export interface HandleAddress {
|
|
@@ -13,6 +13,17 @@ export interface ProgramArea extends CoreProgramArea {
|
|
|
13
13
|
resolve(...path: string[]): Promise<string>;
|
|
14
14
|
}
|
|
15
15
|
/** Client-safe Program data transported by the authoritative host. */
|
|
16
|
+
export interface EndpointDeclarationRecord {
|
|
17
|
+
start: boolean;
|
|
18
|
+
hasService: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface ClientDeclarationRecord extends EndpointDeclarationRecord {
|
|
21
|
+
title: string | null;
|
|
22
|
+
size: Size | null;
|
|
23
|
+
position: Position | null;
|
|
24
|
+
layer: ClientDeclaration["layer"];
|
|
25
|
+
minimize: boolean | null;
|
|
26
|
+
}
|
|
16
27
|
export interface ProgramRecord {
|
|
17
28
|
reference: string;
|
|
18
29
|
identity: string;
|
|
@@ -20,8 +31,8 @@ export interface ProgramRecord {
|
|
|
20
31
|
name: string;
|
|
21
32
|
version: string | null;
|
|
22
33
|
description: string | null;
|
|
23
|
-
server:
|
|
24
|
-
client:
|
|
34
|
+
server: EndpointDeclarationRecord | null;
|
|
35
|
+
client: ClientDeclarationRecord | null;
|
|
25
36
|
}
|
|
26
37
|
/** Process data transported with every Endpoint reference. */
|
|
27
38
|
export interface ProcessRecord {
|
|
@@ -40,7 +51,7 @@ export interface EndpointReference {
|
|
|
40
51
|
}
|
|
41
52
|
export type WindowRecord = WindowState;
|
|
42
53
|
/** Server-visible Program handle and privileged Program operations. */
|
|
43
|
-
export interface Program<Events extends object = {}> extends CoreProgram<Events> {
|
|
54
|
+
export interface Program<Events extends object = {}> extends Omit<CoreProgram<Events>, "process"> {
|
|
44
55
|
/** Persistent filesystem data shared by every Process of this Program. */
|
|
45
56
|
readonly data: ProgramArea;
|
|
46
57
|
/** Disposable filesystem data shared by every Process of this Program. */
|
|
@@ -48,21 +59,30 @@ export interface Program<Events extends object = {}> extends CoreProgram<Events>
|
|
|
48
59
|
/** Persistent Process launch used when the system starts. */
|
|
49
60
|
readonly startup: ProgramStartup;
|
|
50
61
|
/** Persistent permission decisions owned by this Program. */
|
|
51
|
-
readonly
|
|
62
|
+
readonly permission: ProgramPermission;
|
|
63
|
+
/** Operations and lifecycle observation for this Program's Processes. */
|
|
64
|
+
readonly process: ProgramProcess;
|
|
65
|
+
/** Installs this Program and returns the same handle. */
|
|
66
|
+
install(): Promise<this>;
|
|
67
|
+
/** Creates a new runtime Program with the supplied stable identity. */
|
|
68
|
+
fork(identity: string): Promise<Program>;
|
|
69
|
+
}
|
|
70
|
+
/** Server-visible Process operations scoped to one Program. */
|
|
71
|
+
export interface ProgramProcess extends Omit<CoreProgramProcess, "list" | "first" | "last" | "find" | "create" | "findOrCreate"> {
|
|
52
72
|
/** Returns every live Process of this Program. */
|
|
53
|
-
|
|
73
|
+
list(): Promise<Process[]>;
|
|
54
74
|
/** Returns the earliest-started live Process, or `null` when none exist. */
|
|
55
|
-
|
|
75
|
+
first(): Promise<Process | null>;
|
|
56
76
|
/** Returns the latest-started live Process, or `null` when none exist. */
|
|
57
|
-
|
|
77
|
+
last(): Promise<Process | null>;
|
|
58
78
|
/** Finds a live Process by runtime identity or Program-local name. */
|
|
59
|
-
|
|
79
|
+
find(identityOrName: string): Promise<Process | null>;
|
|
60
80
|
/** Creates one Process of this Program. */
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
create(launch?: Launch): Promise<Process>;
|
|
82
|
+
/** Finds the named Process or atomically creates it with the same resolved launch. */
|
|
83
|
+
findOrCreate(launch: Launch & Readonly<{
|
|
84
|
+
name: string;
|
|
85
|
+
}>): Promise<Process>;
|
|
66
86
|
}
|
|
67
87
|
/** Server-visible Process handle. */
|
|
68
88
|
export interface Process<Events extends object = {}> extends CoreProcess<Events> {
|
package/dist/domain.js
CHANGED
|
@@ -5,7 +5,7 @@ import Deadline from "./deadline.js";
|
|
|
5
5
|
import HandleRegistry from "./handle-registry.js";
|
|
6
6
|
import { area, sql, store } from "./storage.js";
|
|
7
7
|
import startup, {} from "./startup.js";
|
|
8
|
-
import
|
|
8
|
+
import permission from "./permissions.js";
|
|
9
9
|
import wire from "./wire.js";
|
|
10
10
|
import { endpointService } from "./service.js";
|
|
11
11
|
const handles = new HandleRegistry();
|
|
@@ -22,7 +22,8 @@ class ProgramHandle extends ProgramBase {
|
|
|
22
22
|
logs;
|
|
23
23
|
database;
|
|
24
24
|
startup;
|
|
25
|
-
|
|
25
|
+
permission;
|
|
26
|
+
process;
|
|
26
27
|
record;
|
|
27
28
|
constructor(record) {
|
|
28
29
|
super();
|
|
@@ -35,38 +36,25 @@ class ProgramHandle extends ProgramBase {
|
|
|
35
36
|
this.logs = sql("logs", this.address);
|
|
36
37
|
this.database = sql("database", this.address);
|
|
37
38
|
this.startup = startup(this.address);
|
|
38
|
-
this.
|
|
39
|
-
|
|
39
|
+
this.permission = permission(this.address);
|
|
40
|
+
this.process = new ProgramProcessHandle(this.address, record.reference);
|
|
41
|
+
bindEvents(this, scoped("program-host", record.reference, programEvent));
|
|
40
42
|
}
|
|
41
43
|
get name() { return this.record.name; }
|
|
42
44
|
get version() { return this.record.version; }
|
|
43
45
|
get description() { return this.record.description; }
|
|
44
|
-
get server() {
|
|
45
|
-
|
|
46
|
+
get server() {
|
|
47
|
+
return this.record.server ? declaration(this.address, "server", this.record.server) : null;
|
|
48
|
+
}
|
|
49
|
+
get client() {
|
|
50
|
+
return this.record.client ? clientDeclaration(this.address, this.record.client) : null;
|
|
51
|
+
}
|
|
46
52
|
get address() { return { identity: this.identity, reference: this.reference }; }
|
|
47
53
|
update(record) {
|
|
48
54
|
if (record.reference !== this.reference)
|
|
49
55
|
throw new Error("A Program handle cannot become another Program");
|
|
50
56
|
this.record = record;
|
|
51
57
|
}
|
|
52
|
-
async processes() {
|
|
53
|
-
const answer = await wire.request(["processes", this.address]);
|
|
54
|
-
return answer[0].map(record => process(record));
|
|
55
|
-
}
|
|
56
|
-
async firstProcess() {
|
|
57
|
-
return chronological(await this.processes())[0] ?? null;
|
|
58
|
-
}
|
|
59
|
-
async lastProcess() {
|
|
60
|
-
return chronological(await this.processes()).at(-1) ?? null;
|
|
61
|
-
}
|
|
62
|
-
async getProcess(identityOrName) {
|
|
63
|
-
const answer = await wire.request(["program-process", this.address, identityOrName]);
|
|
64
|
-
return answer[0] ? process(answer[0]) : null;
|
|
65
|
-
}
|
|
66
|
-
async createProcess(launch = {}) {
|
|
67
|
-
const answer = await wire.request(["create-process", this.address, launch]);
|
|
68
|
-
return process(answer[0]);
|
|
69
|
-
}
|
|
70
58
|
async icon(size = "medium") {
|
|
71
59
|
const answer = await wire.request(["icon", this.address, size]);
|
|
72
60
|
return new Blob([Uint8Array.from(answer[0])], { type: "image/png" });
|
|
@@ -89,8 +77,60 @@ class ProgramHandle extends ProgramBase {
|
|
|
89
77
|
async forget() {
|
|
90
78
|
await wire.request(["forget", this.address]);
|
|
91
79
|
}
|
|
80
|
+
}
|
|
81
|
+
function declaration(address, endpoint, record) {
|
|
82
|
+
return Object.freeze({
|
|
83
|
+
start: record.start,
|
|
84
|
+
hasService: () => record.hasService,
|
|
85
|
+
docs: () => endpointDocs(address, endpoint, record.hasService)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function clientDeclaration(address, record) {
|
|
89
|
+
return Object.freeze({
|
|
90
|
+
...declaration(address, "client", record),
|
|
91
|
+
title: record.title,
|
|
92
|
+
size: record.size,
|
|
93
|
+
position: record.position,
|
|
94
|
+
layer: record.layer,
|
|
95
|
+
minimize: record.minimize
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function endpointDocs(address, endpoint, declared) {
|
|
99
|
+
if (!declared)
|
|
100
|
+
return null;
|
|
101
|
+
const answer = await wire.request(["endpoint-docs", address, endpoint]);
|
|
102
|
+
return answer[0];
|
|
103
|
+
}
|
|
104
|
+
class ProgramProcessHandle {
|
|
105
|
+
address;
|
|
106
|
+
constructor(address, reference) {
|
|
107
|
+
this.address = address;
|
|
108
|
+
bindEvents(this, scoped("program-process", reference, programProcessEvent));
|
|
109
|
+
}
|
|
110
|
+
async list() {
|
|
111
|
+
const answer = await wire.request(["program-process-list", this.address]);
|
|
112
|
+
return answer[0].map(record => process(record));
|
|
113
|
+
}
|
|
114
|
+
async first() {
|
|
115
|
+
return chronological(await this.list())[0] ?? null;
|
|
116
|
+
}
|
|
117
|
+
async last() {
|
|
118
|
+
return chronological(await this.list()).at(-1) ?? null;
|
|
119
|
+
}
|
|
120
|
+
async find(identityOrName) {
|
|
121
|
+
const answer = await wire.request(["program-process-find", this.address, identityOrName]);
|
|
122
|
+
return answer[0] ? process(answer[0]) : null;
|
|
123
|
+
}
|
|
124
|
+
async create(launch = {}) {
|
|
125
|
+
const answer = await wire.request(["program-process-create", this.address, launch]);
|
|
126
|
+
return process(answer[0]);
|
|
127
|
+
}
|
|
128
|
+
async findOrCreate(launch) {
|
|
129
|
+
const answer = await wire.request(["program-process-find-or-create", this.address, launch]);
|
|
130
|
+
return process(answer[0]);
|
|
131
|
+
}
|
|
92
132
|
async exitAll() {
|
|
93
|
-
const answer = await wire.request(["exit-all", this.address]);
|
|
133
|
+
const answer = await wire.request(["program-process-exit-all", this.address]);
|
|
94
134
|
return answer[0];
|
|
95
135
|
}
|
|
96
136
|
}
|
|
@@ -339,13 +379,16 @@ function unscoped(subject, values) {
|
|
|
339
379
|
return values;
|
|
340
380
|
return values[0] === subject ? values.slice(1) : null;
|
|
341
381
|
}
|
|
342
|
-
function
|
|
382
|
+
function programProcessEvent(event, values) {
|
|
343
383
|
if (event === "endpointStart" || event === "endpointStop")
|
|
344
384
|
return lifecycleEndpoint(values[0], values[1]);
|
|
345
|
-
if (event === "
|
|
385
|
+
if (event === "create")
|
|
346
386
|
return process(values[0]);
|
|
347
|
-
if (event === "
|
|
387
|
+
if (event === "exit")
|
|
348
388
|
return { process: process(values[0]), ...exit(values[1], values[2]) };
|
|
389
|
+
return undefined;
|
|
390
|
+
}
|
|
391
|
+
function programEvent(event, values) {
|
|
349
392
|
if (event === "uninstall")
|
|
350
393
|
return { everythingRemoved: values[0] === true };
|
|
351
394
|
return undefined;
|
package/dist/host.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DesktopWallpaper, Exit, FileWallpaper, Layer, Position, ServedFile, ClientServiceHandler, ServerServiceHandler, Size, Subscribable, ThemeProperties, WritableTheme } from "@phreshos/core";
|
|
1
|
+
import type { DesktopWallpaper, Exit, FileWallpaper, Layer, Position, ServedFile, ClientServiceHandler, ServerServiceHandler, ServiceKey, 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<{
|
|
@@ -6,6 +6,8 @@ export type ServerDescription = Readonly<{
|
|
|
6
6
|
location: string;
|
|
7
7
|
/** Whether newly created Processes start this Server by default. */
|
|
8
8
|
start?: boolean;
|
|
9
|
+
/** Absolute Markdown file documenting the Service this Server may expose. */
|
|
10
|
+
serviceDocs?: string;
|
|
9
11
|
/** Command used to install the Server's production dependencies. */
|
|
10
12
|
installCommand?: string;
|
|
11
13
|
/** Command used to start the Server from its production directory. */
|
|
@@ -17,6 +19,8 @@ export type ClientDescription = Readonly<{
|
|
|
17
19
|
location: string;
|
|
18
20
|
/** Whether newly created Processes start this Client by default. */
|
|
19
21
|
start?: boolean;
|
|
22
|
+
/** Absolute Markdown file documenting the Service this Client may expose. */
|
|
23
|
+
serviceDocs?: string;
|
|
20
24
|
/** Default Window title. */
|
|
21
25
|
title?: string;
|
|
22
26
|
/** Default Window size. */
|
|
@@ -55,68 +59,69 @@ export type ProgramDescription = Description & (Readonly<{
|
|
|
55
59
|
client: ClientDescription;
|
|
56
60
|
}>);
|
|
57
61
|
/** An uninstall reported with the affected Program and removal scope. */
|
|
58
|
-
export type
|
|
62
|
+
export type ProgramHostUninstall = Readonly<{
|
|
59
63
|
/** Program that left the installed state. */
|
|
60
64
|
program: Program;
|
|
61
65
|
/** Whether all installed resources, including storage, were removed. */
|
|
62
66
|
everythingRemoved: boolean;
|
|
63
67
|
}>;
|
|
64
68
|
/** A Process exit reported with the Process that ended. */
|
|
65
|
-
export type
|
|
69
|
+
export type ProcessHostExit = Exit & Readonly<{
|
|
66
70
|
/** Process that ended. */
|
|
67
71
|
process: Process;
|
|
68
72
|
}>;
|
|
69
73
|
/** Authoritative lifecycle events visible to the Server host. */
|
|
70
|
-
export type
|
|
71
|
-
/** One Process Endpoint entered a new live incarnation. */
|
|
72
|
-
endpointStart: Server | Client;
|
|
73
|
-
/** One Process Endpoint incarnation ended. */
|
|
74
|
-
endpointStop: Server | Client;
|
|
74
|
+
export type ProgramHostEvents = {
|
|
75
75
|
/** A Program entered the runtime registry. */
|
|
76
|
-
|
|
76
|
+
create: Program;
|
|
77
77
|
/** A Program left the runtime registry. */
|
|
78
|
-
|
|
78
|
+
forget: Program;
|
|
79
79
|
/** A Program entered the installed state. */
|
|
80
|
-
|
|
80
|
+
install: Program;
|
|
81
81
|
/** A Program left the installed state. */
|
|
82
|
-
|
|
82
|
+
uninstall: ProgramHostUninstall;
|
|
83
|
+
};
|
|
84
|
+
/** Authoritative Process lifecycle events visible to the Server host. */
|
|
85
|
+
export type ProcessHostEvents = {
|
|
86
|
+
/** One Process Endpoint entered a new live incarnation. */
|
|
87
|
+
endpointStart: Server | Client;
|
|
88
|
+
/** One Process Endpoint incarnation ended. */
|
|
89
|
+
endpointStop: Server | Client;
|
|
83
90
|
/** A Process entered the runtime set. */
|
|
84
|
-
|
|
91
|
+
create: Process;
|
|
85
92
|
/** A Process left the runtime set. */
|
|
86
|
-
|
|
93
|
+
exit: ProcessHostExit;
|
|
87
94
|
};
|
|
95
|
+
/** Authoritative Program registry available to a Server endpoint. */
|
|
96
|
+
export interface HostProgram extends Subscribable<ProgramHostEvents, never> {
|
|
97
|
+
list(onlyInstalled?: boolean): Promise<Program[]>;
|
|
98
|
+
find(identity: string): Promise<Program | null>;
|
|
99
|
+
create(source: ProgramDescription | string): Promise<Program>;
|
|
100
|
+
}
|
|
101
|
+
/** Authoritative Process registry available to a Server endpoint. */
|
|
102
|
+
export interface HostProcess extends Subscribable<ProcessHostEvents, never> {
|
|
103
|
+
list(): Promise<Process[]>;
|
|
104
|
+
find(identity: string): Promise<Process | null>;
|
|
105
|
+
}
|
|
88
106
|
/** Authoritative system capabilities available to a Server endpoint. */
|
|
89
|
-
export interface Host
|
|
107
|
+
export interface Host {
|
|
90
108
|
/** Observable system Theme authority. */
|
|
91
109
|
readonly theme: WritableTheme<ThemeProperties>;
|
|
92
110
|
/** Authoritative wallpaper visible before authentication. */
|
|
93
111
|
readonly signInWallpaper: FileWallpaper;
|
|
94
112
|
/** Authoritative wallpaper visible within authenticated desktops. */
|
|
95
113
|
readonly desktopWallpaper: DesktopWallpaper;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
/** Returns
|
|
99
|
-
|
|
100
|
-
/** Returns the Program with the given stable identity. */
|
|
101
|
-
getProgram(identity: string): Promise<Program>;
|
|
102
|
-
/** Creates a runtime Program from a description or description file path. */
|
|
103
|
-
createProgram(source: ProgramDescription | string): Promise<Program>;
|
|
104
|
-
/** Returns every live Process known to the authoritative host. */
|
|
105
|
-
processes(): Promise<Process[]>;
|
|
106
|
-
/** Returns the live Process with the given runtime identity. */
|
|
107
|
-
getProcess(identity: string): Promise<Process>;
|
|
108
|
-
/** Returns a stable handle for one exact Server service identity. */
|
|
109
|
-
service<ServiceEvents extends object = {}>(key: {
|
|
110
|
-
program: string;
|
|
114
|
+
readonly program: HostProgram;
|
|
115
|
+
readonly process: HostProcess;
|
|
116
|
+
/** Returns a stable handle for one exact Service identity. */
|
|
117
|
+
service<ServiceEvents extends object = {}>(key: ServiceKey & {
|
|
111
118
|
endpoint: "server";
|
|
112
|
-
name: string;
|
|
113
119
|
}): ServerServiceHandler<ServiceEvents>;
|
|
114
|
-
|
|
115
|
-
service<ServiceEvents extends object = {}>(key: {
|
|
116
|
-
program: string;
|
|
120
|
+
service<ServiceEvents extends object = {}>(key: ServiceKey & {
|
|
117
121
|
endpoint: "client";
|
|
118
|
-
name: string;
|
|
119
122
|
}): ClientServiceHandler<ServiceEvents>;
|
|
123
|
+
/** Publishes a value through the host and returns its public file metadata. */
|
|
124
|
+
serve(value: unknown): Promise<ServedFile>;
|
|
120
125
|
}
|
|
121
126
|
/** Authoritative system capabilities for the currently executing Server. */
|
|
122
127
|
export declare const host: Host;
|
package/dist/host.js
CHANGED
|
@@ -4,55 +4,68 @@ 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 {
|
|
8
|
-
class ServerHost
|
|
7
|
+
import { prepareService } from "./service.js";
|
|
8
|
+
class ServerHost {
|
|
9
9
|
theme = new ServerTheme();
|
|
10
10
|
signInWallpaper = new ServerSignInWallpaper();
|
|
11
11
|
desktopWallpaper = new ServerDesktopWallpaper();
|
|
12
|
+
program = new ServerHostProgram();
|
|
13
|
+
process = new ServerHostProcess();
|
|
14
|
+
service(key) { return prepareService(key); }
|
|
15
|
+
serve(value) { return serve(value); }
|
|
16
|
+
}
|
|
17
|
+
class ServerHostProgram extends Events {
|
|
12
18
|
constructor() {
|
|
13
|
-
super((event, listener, impossible) => wire.on("host-
|
|
19
|
+
super((event, listener, impossible) => wire.on("host-program", event, (...values) => listener(hostProgramEvent(event, values)), null, impossible), observer => wire.onAll("host-program", (event, ...values) => {
|
|
14
20
|
if (typeof event === "string")
|
|
15
|
-
observer(event,
|
|
21
|
+
observer(event, hostProgramEvent(event, values));
|
|
16
22
|
}));
|
|
17
23
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const answer = await wire.request(["programs", onlyInstalled]);
|
|
24
|
+
async list(onlyInstalled = false) {
|
|
25
|
+
const answer = await wire.request(["host-program-list", onlyInstalled]);
|
|
21
26
|
return answer[0].map(program);
|
|
22
27
|
}
|
|
23
|
-
async
|
|
24
|
-
const answer = await wire.request(["program", identity]);
|
|
25
|
-
return program(answer[0]);
|
|
28
|
+
async find(identity) {
|
|
29
|
+
const answer = await wire.request(["host-program-find", identity]);
|
|
30
|
+
return answer[0] ? program(answer[0]) : null;
|
|
26
31
|
}
|
|
27
|
-
async
|
|
28
|
-
const answer = await wire.request(["
|
|
32
|
+
async create(source) {
|
|
33
|
+
const answer = await wire.request(["host-program-create", source]);
|
|
29
34
|
return program(answer[0]);
|
|
30
35
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
36
|
+
}
|
|
37
|
+
class ServerHostProcess extends Events {
|
|
38
|
+
constructor() {
|
|
39
|
+
super((event, listener, impossible) => wire.on("host-process", event, (...values) => listener(hostProcessEvent(event, values)), null, impossible), observer => wire.onAll("host-process", (event, ...values) => {
|
|
40
|
+
if (typeof event === "string")
|
|
41
|
+
observer(event, hostProcessEvent(event, values));
|
|
42
|
+
}));
|
|
34
43
|
}
|
|
35
|
-
async
|
|
36
|
-
const answer = await wire.request(["process"
|
|
37
|
-
return
|
|
44
|
+
async list() {
|
|
45
|
+
const answer = await wire.request(["host-process-list"]);
|
|
46
|
+
return answer[0].map(record => process(record));
|
|
38
47
|
}
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
async find(identity) {
|
|
49
|
+
const answer = await wire.request(["host-process-find", identity]);
|
|
50
|
+
return answer[0] ? process(answer[0]) : null;
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
|
-
function
|
|
53
|
+
function hostProcessEvent(event, values) {
|
|
44
54
|
if (event === "endpointStart" || event === "endpointStop")
|
|
45
55
|
return lifecycleEndpoint(values[1], values[2]);
|
|
46
|
-
if (event === "
|
|
56
|
+
if (event === "create") {
|
|
47
57
|
return process(values[1]);
|
|
48
58
|
}
|
|
49
|
-
if (event === "
|
|
59
|
+
if (event === "exit") {
|
|
50
60
|
return { process: process(values[1]), ...exit(values[2], values[3]) };
|
|
51
61
|
}
|
|
52
|
-
|
|
62
|
+
return values[0];
|
|
63
|
+
}
|
|
64
|
+
function hostProgramEvent(event, values) {
|
|
65
|
+
if (event === "create" || event === "forget" || event === "install") {
|
|
53
66
|
return program(values[1]);
|
|
54
67
|
}
|
|
55
|
-
if (event === "
|
|
68
|
+
if (event === "uninstall") {
|
|
56
69
|
return { program: program(values[1]), everythingRemoved: values[2] === true };
|
|
57
70
|
}
|
|
58
71
|
return values[0];
|
package/dist/main.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { host, type Host, type ClientDescription, type
|
|
1
|
+
export { host, type Host, type HostProcess, type HostProgram, type ClientDescription, type ProcessHostEvents, type ProcessHostExit, type ProgramHostEvents, type ProgramHostUninstall, type ProgramDescription, type ServerDescription } from "./host.js";
|
|
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
5
|
export { ClientServiceHandler, ServerServiceHandler, ServiceHandler, type ClientServiceChannel, type ServerServiceChannel, type ServiceChannel, type ServiceKey, type ServiceLifecycleEvents } from "@phreshos/core";
|
|
6
|
-
export { Client, Endpoint, Process, Program, Server, type Window, type ProgramArea } from "./domain.js";
|
|
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,
|
|
6
|
+
export { Client, Endpoint, Process, Program, Server, type Window, type ProgramArea, type ProgramProcess } from "./domain.js";
|
|
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, ProgramPermission, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, ServerTraffic, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TrafficCapture, TrafficEvents, TrafficMessage, Value, Theme, ThemeEvents, ThemeProperties, WallpaperLaunch, FileWallpaper, DesktopWallpaper, WritableTheme, WindowEvents, WindowGeometry, WindowLayer, WindowState } from "@phreshos/core";
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ProgramPermission } from "@phreshos/core";
|
|
2
2
|
/** Creates the persistent permission manager for one Program handle. */
|
|
3
|
-
export default function
|
|
3
|
+
export default function permission(program: unknown): ProgramPermission;
|
package/dist/permissions.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import wire from "./wire.js";
|
|
2
2
|
/** Creates the persistent permission manager for one Program handle. */
|
|
3
|
-
export default function
|
|
3
|
+
export default function permission(program) {
|
|
4
4
|
return {
|
|
5
5
|
async get(name) {
|
|
6
|
-
const answer = await wire.request(["
|
|
6
|
+
const answer = await wire.request(["program-permission", program, "get", name]);
|
|
7
7
|
return answer[0];
|
|
8
8
|
},
|
|
9
9
|
async getAll() {
|
|
10
|
-
const answer = await wire.request(["
|
|
10
|
+
const answer = await wire.request(["program-permission", program, "getAll"]);
|
|
11
11
|
return answer[0];
|
|
12
12
|
},
|
|
13
13
|
async set(name, value) {
|
|
14
|
-
await wire.request(["
|
|
14
|
+
await wire.request(["program-permission", program, "set", name, value]);
|
|
15
15
|
},
|
|
16
16
|
async delete(name) {
|
|
17
|
-
await wire.request(["
|
|
17
|
+
await wire.request(["program-permission", program, "delete", name]);
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
}
|
package/dist/service.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { type ClientServiceHandler, type
|
|
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
|
|
3
|
+
export declare function prepareService<EventsMap extends object = {}>(key: ServiceKey & {
|
|
4
4
|
endpoint: "server";
|
|
5
5
|
}): ServerServiceHandler<EventsMap>;
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function prepareService<EventsMap extends object = {}>(key: ServiceKey & {
|
|
7
7
|
endpoint: "client";
|
|
8
8
|
}): ClientServiceHandler<EventsMap>;
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function enableCurrentService(
|
|
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
|
@@ -47,47 +47,43 @@ class ClientChannelHandle extends Events {
|
|
|
47
47
|
}
|
|
48
48
|
class ServerHandler extends ServerServiceBase {
|
|
49
49
|
key;
|
|
50
|
-
program;
|
|
51
|
-
endpoint = "server";
|
|
52
50
|
name;
|
|
53
51
|
channel;
|
|
54
52
|
constructor(key) {
|
|
55
53
|
super();
|
|
56
54
|
this.key = key;
|
|
57
|
-
this.program = key.program;
|
|
58
55
|
this.name = key.name;
|
|
59
56
|
this.channel = new ServerChannelHandle(key);
|
|
60
57
|
bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
|
|
61
58
|
}
|
|
62
|
-
async
|
|
63
|
-
const answer = await wire.request(["service-
|
|
59
|
+
async enabled() {
|
|
60
|
+
const answer = await wire.request(["service-enabled", this.key]);
|
|
64
61
|
return answer[0];
|
|
65
62
|
}
|
|
66
|
-
async
|
|
67
|
-
|
|
68
|
-
return answer[0];
|
|
63
|
+
async waitReady(timeout) {
|
|
64
|
+
await wire.request(["service-wait-ready", this.key, timeout], timeout);
|
|
69
65
|
}
|
|
70
66
|
}
|
|
71
67
|
class ClientHandler extends ClientServiceBase {
|
|
72
68
|
key;
|
|
73
|
-
program;
|
|
74
|
-
endpoint = "client";
|
|
75
69
|
name;
|
|
76
70
|
channel;
|
|
77
71
|
constructor(key) {
|
|
78
72
|
super();
|
|
79
73
|
this.key = key;
|
|
80
|
-
this.program = key.program;
|
|
81
74
|
this.name = key.name;
|
|
82
75
|
this.channel = new ClientChannelHandle(key);
|
|
83
76
|
bindEvents(this, new Events(...serviceEvents(key, "lifecycle")));
|
|
84
77
|
}
|
|
85
|
-
async
|
|
86
|
-
const answer = await wire.request(["service-
|
|
78
|
+
async enabled() {
|
|
79
|
+
const answer = await wire.request(["service-enabled", this.key]);
|
|
87
80
|
return answer[0];
|
|
88
81
|
}
|
|
82
|
+
async waitReady(timeout) {
|
|
83
|
+
await wire.request(["service-wait-ready", this.key, timeout], timeout);
|
|
84
|
+
}
|
|
89
85
|
}
|
|
90
|
-
export function
|
|
86
|
+
export function prepareService(key) {
|
|
91
87
|
if (!isServiceKey(key))
|
|
92
88
|
throw new Error("A complete service key is required");
|
|
93
89
|
const normalized = Object.freeze({ program: key.program, endpoint: key.endpoint, name: key.name });
|
|
@@ -98,15 +94,15 @@ export function service(key) {
|
|
|
98
94
|
: new ClientHandler(normalized);
|
|
99
95
|
});
|
|
100
96
|
}
|
|
101
|
-
export async function enableCurrentService(
|
|
102
|
-
await wire.request(["enable-service",
|
|
97
|
+
export async function enableCurrentService(name) {
|
|
98
|
+
await wire.request(["enable-service", name]);
|
|
103
99
|
}
|
|
104
100
|
export async function disableCurrentService() {
|
|
105
101
|
await wire.request(["disable-service"]);
|
|
106
102
|
}
|
|
107
103
|
export async function endpointService(target, endpoint) {
|
|
108
104
|
const answer = await wire.request(["endpoint-service", target, endpoint]);
|
|
109
|
-
return answer[0] ?
|
|
105
|
+
return answer[0] ? prepareService(answer[0]) : null;
|
|
110
106
|
}
|
|
111
107
|
function serviceEvents(key, scope) {
|
|
112
108
|
return [
|
package/dist/theme.js
CHANGED
|
@@ -5,31 +5,17 @@ import wire from "./wire.js";
|
|
|
5
5
|
export default class ServerTheme extends Events {
|
|
6
6
|
constructor() {
|
|
7
7
|
super((event, listener, impossible) => wire.on("host-theme", event, value => {
|
|
8
|
-
|
|
9
|
-
listener(createThemeSnapshot(value));
|
|
8
|
+
listener(createThemeSnapshot(value));
|
|
10
9
|
}, null, impossible), observer => wire.onAll("host-theme", (event, value) => {
|
|
11
|
-
if (typeof event === "string"
|
|
10
|
+
if (typeof event === "string")
|
|
12
11
|
observer(event, createThemeSnapshot(value));
|
|
13
12
|
}));
|
|
14
13
|
}
|
|
15
14
|
async snapshot() {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
throw new Error("The host 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
|
update = async (theme) => {
|
|
22
19
|
await wire.request(["update-theme", theme]);
|
|
23
20
|
};
|
|
24
21
|
}
|
|
25
|
-
function isObject(value) {
|
|
26
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
27
|
-
return false;
|
|
28
|
-
const theme = value;
|
|
29
|
-
const surface = theme.surface;
|
|
30
|
-
return typeof theme.background === "string" && typeof theme.foreground === "string" && typeof theme.accent === "string"
|
|
31
|
-
&& typeof theme.spacing === "number" && typeof theme.radius === "number"
|
|
32
|
-
&& typeof surface === "object" && surface !== null
|
|
33
|
-
&& typeof surface.grain === "number" && typeof surface.animation === "number"
|
|
34
|
-
&& typeof surface.backdrop === "number" && typeof surface.opacity === "number";
|
|
35
|
-
}
|
package/dist/wire.js
CHANGED
|
@@ -69,7 +69,7 @@ class Wire {
|
|
|
69
69
|
/** Resolves this endpoint's Process address only for operations that need it. */
|
|
70
70
|
identity() {
|
|
71
71
|
if (!this.identityPromise) {
|
|
72
|
-
const resolving = this.request(["process"]).then(value => {
|
|
72
|
+
const resolving = this.request(["current-process"]).then(value => {
|
|
73
73
|
const [record] = value;
|
|
74
74
|
if (typeof record?.identity !== "string" || typeof record.reference !== "string") {
|
|
75
75
|
throw new Error("The host returned an invalid Process identity");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "The SDK used by a Program's server endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"prepack": "node --run build"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@phreshos/core": "^0.1.
|
|
49
|
+
"@phreshos/core": "^0.1.11"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@msgpack/msgpack": "^3.1.3"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@phreshos/core": "^0.1.
|
|
55
|
+
"@phreshos/core": "^0.1.11",
|
|
56
56
|
"@types/node": "^26.2.0",
|
|
57
57
|
"typescript": "^6.0.3"
|
|
58
58
|
}
|