@phreshos/node 0.1.12 → 0.1.14
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 +31 -36
- package/dist/development-client.d.ts +20 -0
- package/dist/development-client.js +259 -0
- package/dist/events.d.ts +4 -4
- package/dist/events.js +18 -29
- package/dist/main.d.ts +2 -2
- package/dist/main.js +2 -2
- package/dist/process-tree.d.ts +10 -0
- package/dist/process-tree.js +67 -0
- package/dist/program-resources.d.ts +6 -6
- package/dist/program-resources.js +12 -26
- package/dist/project.d.ts +5 -3
- package/dist/project.js +53 -13
- package/dist/representation.d.ts +100 -0
- package/dist/representation.js +331 -0
- package/dist/shell.d.ts +3 -0
- package/dist/shell.js +125 -0
- package/dist/storage.d.ts +6 -2
- package/dist/storage.js +27 -8
- package/dist/system.d.ts +26 -20
- package/dist/system.js +273 -300
- package/dist/traffic.d.ts +6 -6
- package/dist/traffic.js +15 -33
- package/dist/transport.d.ts +20 -10
- package/dist/transport.js +106 -110
- package/dist/uploads.d.ts +4 -1
- package/dist/uploads.js +20 -5
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.js +15 -0
- package/package.json +4 -2
package/dist/system.js
CHANGED
|
@@ -1,64 +1,68 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientEndpoint as CoreClientEndpoint, ClientService as CoreClientService, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint, ServerService as CoreServerService, isServiceKey, parseClientPermissions } from "@phreshos/core";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { gatewayAddress } from "./address.js";
|
|
4
4
|
import Events from "./events.js";
|
|
5
5
|
import HandleRegistry from "./handle-registry.js";
|
|
6
6
|
import { resolveHome } from "./home.js";
|
|
7
|
-
import { filesystemStorage } from "./storage.js";
|
|
8
|
-
import {
|
|
7
|
+
import { filesystemStorage, nativeStorage } from "./storage.js";
|
|
8
|
+
import { programPermissions, programSql, programStore } from "./program-resources.js";
|
|
9
9
|
import { EndpointTrafficHandle, ServerTrafficHandle } from "./traffic.js";
|
|
10
|
-
import
|
|
10
|
+
import SystemRepresentation, {} from "./representation.js";
|
|
11
|
+
import { GatewayConnection, openConnection } from "./transport.js";
|
|
11
12
|
import Uploads from "./uploads.js";
|
|
13
|
+
import shell from "./shell.js";
|
|
14
|
+
import websocket from "./websocket.js";
|
|
12
15
|
const systems = new WeakMap();
|
|
16
|
+
const processSnapshots = new WeakMap();
|
|
13
17
|
const ProgramBase = CoreProgram;
|
|
14
18
|
const ProcessBase = CoreProcess;
|
|
15
|
-
const
|
|
16
|
-
const
|
|
19
|
+
const ServerEndpointBase = CoreServerEndpoint;
|
|
20
|
+
const ClientEndpointBase = CoreClientEndpoint;
|
|
17
21
|
/** One connected owner-local implementation of the shared System contract. */
|
|
18
22
|
export class System {
|
|
19
|
-
storage
|
|
23
|
+
storage;
|
|
20
24
|
appearance;
|
|
21
25
|
program;
|
|
22
26
|
process;
|
|
23
27
|
uploads;
|
|
24
|
-
|
|
28
|
+
async fetch(input, init) {
|
|
29
|
+
const request = new Request(input, init);
|
|
30
|
+
return await fetch(request, { signal: connectedSignal(this, request.signal) });
|
|
31
|
+
}
|
|
32
|
+
websocket(url, protocols) {
|
|
33
|
+
return websocket(url, protocols, connectedSignal(this));
|
|
34
|
+
}
|
|
35
|
+
async *shell(command, options = {}) {
|
|
36
|
+
yield* shell(command, { ...options, signal: connectedSignal(this, options.signal) });
|
|
37
|
+
}
|
|
38
|
+
constructor(connection) {
|
|
25
39
|
const lifetime = new AbortController();
|
|
26
40
|
const handles = new HandleRegistry();
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
systems.set(this, { address, connection, handles, lifetime, transport, closed: false });
|
|
33
|
-
this.appearance = new SystemAppearance(transport);
|
|
41
|
+
const representation = new SystemRepresentation(connection);
|
|
42
|
+
systems.set(this, { connection, handles, lifetime, representation, closed: false });
|
|
43
|
+
connection.onDisconnect(() => void closeSystem(this, new Error("This System connection is closed")));
|
|
44
|
+
this.storage = nativeStorage(homedir(), "the native filesystem", () => connectedSignal(this));
|
|
45
|
+
this.appearance = new SystemAppearance(this);
|
|
34
46
|
this.program = new ProgramRegistry(this);
|
|
35
47
|
this.process = new ProcessRegistry(this);
|
|
36
|
-
this.uploads = new Uploads(value =>
|
|
48
|
+
this.uploads = new Uploads(value => uploadRequest(this, value), () => connectedSignal(this));
|
|
49
|
+
representation.activate();
|
|
37
50
|
}
|
|
38
51
|
/** Connect to the System selected by argument, environment, or owner default. */
|
|
39
52
|
static async connect(home) {
|
|
40
53
|
const resolved = resolveHome(home);
|
|
41
54
|
const address = gatewayAddress(resolved);
|
|
42
|
-
return new System(
|
|
55
|
+
return new System(await openConnection(address));
|
|
43
56
|
}
|
|
44
57
|
/** Atomically replace one runtime Program without touching its installed form. */
|
|
45
58
|
async forceCreateProgram(source) {
|
|
46
59
|
requireConnected(this);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return programHandle(this, required(event.program));
|
|
50
|
-
}
|
|
51
|
-
throw new Error("The System did not confirm the created Program");
|
|
60
|
+
const identity = await representation(this).call("/program/force-create-program", source, "");
|
|
61
|
+
return programHandle(this, required(representation(this).programs.get(identity), identity));
|
|
52
62
|
}
|
|
53
63
|
/** Close this owner connection and abort every attached operation it owns. */
|
|
54
64
|
async disconnect() {
|
|
55
|
-
|
|
56
|
-
if (state.closed)
|
|
57
|
-
return;
|
|
58
|
-
state.closed = true;
|
|
59
|
-
state.lifetime.abort(new Error("This System connection is closed"));
|
|
60
|
-
state.connection.destroy();
|
|
61
|
-
state.handles.clear();
|
|
65
|
+
await closeSystem(this, new Error("This System connection is closed"));
|
|
62
66
|
}
|
|
63
67
|
service(key) {
|
|
64
68
|
requireConnected(this);
|
|
@@ -81,6 +85,16 @@ function systemState(system) {
|
|
|
81
85
|
throw new Error("Unknown System connection");
|
|
82
86
|
return state;
|
|
83
87
|
}
|
|
88
|
+
function closeSystem(system, reason) {
|
|
89
|
+
const state = systemState(system);
|
|
90
|
+
if (state.closed)
|
|
91
|
+
return Promise.resolve();
|
|
92
|
+
state.closed = true;
|
|
93
|
+
state.lifetime.abort(reason);
|
|
94
|
+
state.representation.close();
|
|
95
|
+
state.handles.clear();
|
|
96
|
+
return state.connection.disconnect();
|
|
97
|
+
}
|
|
84
98
|
function requireConnected(system) {
|
|
85
99
|
if (systemState(system).closed)
|
|
86
100
|
throw new Error("This System connection is closed");
|
|
@@ -90,9 +104,9 @@ function connectedSignal(system, signal) {
|
|
|
90
104
|
requireConnected(system);
|
|
91
105
|
return signal ? AbortSignal.any([signal, lifetime]) : lifetime;
|
|
92
106
|
}
|
|
93
|
-
function
|
|
107
|
+
function representation(system) {
|
|
94
108
|
requireConnected(system);
|
|
95
|
-
return systemState(system).
|
|
109
|
+
return systemState(system).representation;
|
|
96
110
|
}
|
|
97
111
|
function programHandle(system, snapshot) {
|
|
98
112
|
const handle = systemState(system).handles.obtain(`program:${snapshot.reference}`, () => new ProgramHandle(system, snapshot));
|
|
@@ -103,65 +117,45 @@ function processHandle(system, snapshot) {
|
|
|
103
117
|
return systemState(system).handles.obtain(`process:${snapshot.reference}`, () => new ProcessHandle(system, snapshot));
|
|
104
118
|
}
|
|
105
119
|
class SystemAppearance extends Events {
|
|
106
|
-
|
|
107
|
-
constructor(
|
|
108
|
-
super(["change"], (_event,
|
|
109
|
-
this.
|
|
120
|
+
system;
|
|
121
|
+
constructor(system) {
|
|
122
|
+
super(["change"], (_event, subscriber) => representation(system).on("appearance", subscriber));
|
|
123
|
+
this.system = system;
|
|
110
124
|
}
|
|
111
125
|
async snapshot() {
|
|
112
|
-
return
|
|
126
|
+
return representation(this.system).appearance;
|
|
113
127
|
}
|
|
114
128
|
async update(appearance) {
|
|
115
|
-
await this.
|
|
129
|
+
await representation(this.system).call("/appearance/update", appearance);
|
|
116
130
|
}
|
|
117
131
|
}
|
|
118
132
|
class ProgramRegistry extends Events {
|
|
119
133
|
system;
|
|
120
134
|
constructor(system) {
|
|
121
|
-
super(["create", "forget", "install", "uninstall"], (event,
|
|
122
|
-
|
|
135
|
+
super(["create", "forget", "install", "uninstall"], (event, subscriber) => {
|
|
136
|
+
if (event === null)
|
|
137
|
+
throw new Error("System Program events are named");
|
|
138
|
+
return representation(system).on(`program:${event}`, (...values) => subscriber(this.event(event, values)));
|
|
139
|
+
});
|
|
123
140
|
this.system = system;
|
|
124
141
|
}
|
|
125
142
|
async list(onlyInstalled = false) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
capability: "program",
|
|
131
|
-
operation: "list",
|
|
132
|
-
input: { installedOnly: onlyInstalled, limit: 100, offset }
|
|
133
|
-
});
|
|
134
|
-
programs.push(...page.data.map(snapshot => programHandle(this.system, snapshot)));
|
|
135
|
-
offset += page.data.length;
|
|
136
|
-
if (!page.truncated || !page.data.length)
|
|
137
|
-
return programs;
|
|
138
|
-
}
|
|
143
|
+
return [...representation(this.system).programs.values()]
|
|
144
|
+
.filter(program => !onlyInstalled || program.installed)
|
|
145
|
+
.sort((left, right) => left.identity.localeCompare(right.identity))
|
|
146
|
+
.map(program => programHandle(this.system, program));
|
|
139
147
|
}
|
|
140
148
|
async find(identity) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return programHandle(this.system, snapshot);
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
if (unknown(error, "Program"))
|
|
147
|
-
return null;
|
|
148
|
-
throw error;
|
|
149
|
-
}
|
|
149
|
+
const program = representation(this.system).programs.get(identity);
|
|
150
|
+
return program ? programHandle(this.system, program) : null;
|
|
150
151
|
}
|
|
151
152
|
async create(source) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return programHandle(this.system, required(event.program));
|
|
155
|
-
}
|
|
156
|
-
throw new Error("The System did not confirm the created Program");
|
|
153
|
+
const identity = await representation(this.system).call("/program/create-program", source);
|
|
154
|
+
return programHandle(this.system, required(representation(this.system).programs.get(identity), identity));
|
|
157
155
|
}
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
const payload = waited.payload;
|
|
162
|
-
return { program: programHandle(this.system, required(payload.program)), everything: payload.everything === true };
|
|
163
|
-
}
|
|
164
|
-
return programHandle(this.system, required(waited.payload));
|
|
156
|
+
event(event, values) {
|
|
157
|
+
const program = programHandle(this.system, required(values[0]));
|
|
158
|
+
return event === "uninstall" ? { program, everything: values[1] === true } : program;
|
|
165
159
|
}
|
|
166
160
|
}
|
|
167
161
|
class ProgramHandle extends ProgramBase {
|
|
@@ -173,9 +167,9 @@ class ProgramHandle extends ProgramBase {
|
|
|
173
167
|
store;
|
|
174
168
|
logs;
|
|
175
169
|
database;
|
|
176
|
-
permission;
|
|
177
170
|
process;
|
|
178
171
|
startup;
|
|
172
|
+
permissions;
|
|
179
173
|
snapshot;
|
|
180
174
|
constructor(system, snapshot) {
|
|
181
175
|
super();
|
|
@@ -184,20 +178,24 @@ class ProgramHandle extends ProgramBase {
|
|
|
184
178
|
this.reference = snapshot.reference;
|
|
185
179
|
this.identity = snapshot.identity;
|
|
186
180
|
const address = this.address();
|
|
187
|
-
bindEvents(this, new Events(["forget", "uninstall"], (event,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.
|
|
195
|
-
this.
|
|
196
|
-
this.
|
|
181
|
+
bindEvents(this, new Events(["forget", "uninstall"], (event, subscriber) => {
|
|
182
|
+
if (event === null)
|
|
183
|
+
throw new Error("Program events are named");
|
|
184
|
+
return representation(system).on(`program:${this.reference}:${event}`, (...values) => subscriber(values[0]));
|
|
185
|
+
}));
|
|
186
|
+
representation(system).on(`program:${this.reference}:change`, value => this.update(value));
|
|
187
|
+
const call = (event, ...values) => representation(system).call(event, ...values);
|
|
188
|
+
this.data = filesystemStorage(() => programStoragePath(system, address, "data"), `Program "${this.identity}" data`, () => connectedSignal(system));
|
|
189
|
+
this.cache = filesystemStorage(() => programStoragePath(system, address, "cache"), `Program "${this.identity}" cache`, () => connectedSignal(system));
|
|
190
|
+
this.store = programStore(call, address);
|
|
191
|
+
this.logs = programSql(call, address, "logs");
|
|
192
|
+
this.database = programSql(call, address, "database");
|
|
197
193
|
this.process = new ProgramProcesses(system, this);
|
|
198
194
|
this.startup = new ProgramStartup(system, this);
|
|
195
|
+
this.permissions = programPermissions(call, address);
|
|
199
196
|
}
|
|
200
197
|
get name() { return this.snapshot.name; }
|
|
198
|
+
get assetId() { return this.snapshot.assetId; }
|
|
201
199
|
get version() { return this.snapshot.version; }
|
|
202
200
|
get description() { return this.snapshot.description; }
|
|
203
201
|
get hasAgent() { return this.snapshot.hasAgent; }
|
|
@@ -215,7 +213,8 @@ class ProgramHandle extends ProgramBase {
|
|
|
215
213
|
size: this.snapshot.client.size,
|
|
216
214
|
position: this.snapshot.client.position,
|
|
217
215
|
layer: this.snapshot.client.layer,
|
|
218
|
-
minimize: this.snapshot.client.minimize
|
|
216
|
+
minimize: this.snapshot.client.minimize,
|
|
217
|
+
permissions: parseClientPermissions(this.snapshot.client.permissions)
|
|
219
218
|
}) : null;
|
|
220
219
|
}
|
|
221
220
|
update(snapshot) {
|
|
@@ -224,7 +223,7 @@ class ProgramHandle extends ProgramBase {
|
|
|
224
223
|
this.snapshot = snapshot;
|
|
225
224
|
}
|
|
226
225
|
async icon(size = "medium") {
|
|
227
|
-
const value = await
|
|
226
|
+
const value = await representation(this.system).call("/program/icon", this.address(), size);
|
|
228
227
|
if (!Array.isArray(value) || value.some(byte => typeof byte !== "number"))
|
|
229
228
|
throw new Error("The System returned an invalid Program icon");
|
|
230
229
|
return new Blob([Uint8Array.from(value)], { type: "image/png" });
|
|
@@ -232,27 +231,20 @@ class ProgramHandle extends ProgramBase {
|
|
|
232
231
|
async agent() {
|
|
233
232
|
if (!this.hasAgent)
|
|
234
233
|
return null;
|
|
235
|
-
const value = await
|
|
234
|
+
const value = await representation(this.system).call("/program/agent", this.address());
|
|
236
235
|
return typeof value === "string" ? value : null;
|
|
237
236
|
}
|
|
238
237
|
async installed() {
|
|
239
|
-
|
|
240
|
-
if (event.event === "installedState")
|
|
241
|
-
return event.installed === true;
|
|
242
|
-
}
|
|
243
|
-
throw new Error("The System returned no Program installation state");
|
|
238
|
+
return this.snapshot.installed;
|
|
244
239
|
}
|
|
245
|
-
install() { return command(this.system,
|
|
246
|
-
uninstall(everything = false) { return command(this.system,
|
|
240
|
+
install() { return command(this.system, "install", this.address()); }
|
|
241
|
+
uninstall(everything = false) { return command(this.system, "uninstall", this.address(), everything); }
|
|
247
242
|
async fork(identity) {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return programHandle(this.system, required(event.program));
|
|
251
|
-
}
|
|
252
|
-
throw new Error("The System did not confirm the forked Program");
|
|
243
|
+
const created = await representation(this.system).call("/program/fork-program", this.address(), identity);
|
|
244
|
+
return programHandle(this.system, required(representation(this.system).programs.get(created), created));
|
|
253
245
|
}
|
|
254
246
|
async forget() {
|
|
255
|
-
|
|
247
|
+
await representation(this.system).call("/program/forget-program", this.address(), "");
|
|
256
248
|
}
|
|
257
249
|
address() { return Object.freeze({ identity: this.identity, reference: this.reference }); }
|
|
258
250
|
}
|
|
@@ -264,13 +256,7 @@ class ProgramStartup {
|
|
|
264
256
|
this.program = program;
|
|
265
257
|
}
|
|
266
258
|
async get() {
|
|
267
|
-
|
|
268
|
-
word: "startup", handle: this.program.address(), operation: "get"
|
|
269
|
-
})) {
|
|
270
|
-
if (event.event === "startup")
|
|
271
|
-
return event.launch;
|
|
272
|
-
}
|
|
273
|
-
throw new Error("The System returned no Program startup state");
|
|
259
|
+
return await representation(this.system).call("/program/startup", this.program.address(), "get");
|
|
274
260
|
}
|
|
275
261
|
async enable(launch = {}) {
|
|
276
262
|
await this.change("enable", launch);
|
|
@@ -279,30 +265,25 @@ class ProgramStartup {
|
|
|
279
265
|
await this.change("disable");
|
|
280
266
|
}
|
|
281
267
|
async change(operation, launch) {
|
|
282
|
-
|
|
283
|
-
word: "startup", handle: this.program.address(), operation, launch
|
|
284
|
-
})) {
|
|
285
|
-
if (event.event === "startup")
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
throw new Error("The System did not confirm the Program startup change");
|
|
268
|
+
await representation(this.system).call("/program/startup", this.program.address(), operation, launch);
|
|
289
269
|
}
|
|
290
270
|
}
|
|
291
271
|
class ProgramProcesses extends Events {
|
|
292
272
|
system;
|
|
293
273
|
program;
|
|
294
274
|
constructor(system, program) {
|
|
295
|
-
super(["create", "exit"], (event,
|
|
296
|
-
|
|
297
|
-
|
|
275
|
+
super(["create", "exit"], (event, subscriber) => {
|
|
276
|
+
if (event === null)
|
|
277
|
+
throw new Error("Program Process events are named");
|
|
278
|
+
return representation(system).on(`program:${program.identity}:process:${event}`, (...values) => (subscriber(programProcessEvent(system, event, values))));
|
|
279
|
+
});
|
|
298
280
|
this.system = system;
|
|
299
281
|
this.program = program;
|
|
300
282
|
}
|
|
301
283
|
async list() {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
return value.map(snapshot => processHandle(this.system, snapshot));
|
|
284
|
+
return [...representation(this.system).processes.values()]
|
|
285
|
+
.filter(process => process.program === this.program.identity)
|
|
286
|
+
.map(process => processHandle(this.system, process));
|
|
306
287
|
}
|
|
307
288
|
async first() { return (await this.list()).sort(chronological)[0] ?? null; }
|
|
308
289
|
async last() { return (await this.list()).sort(chronological).at(-1) ?? null; }
|
|
@@ -313,13 +294,12 @@ class ProgramProcesses extends Events {
|
|
|
313
294
|
create(launch = {}) { return this.createExact("create-process", launch); }
|
|
314
295
|
async *run(launch = {}, options = {}) {
|
|
315
296
|
let process = null;
|
|
316
|
-
for await (const event of
|
|
317
|
-
word: "run-process",
|
|
318
|
-
handle: this.program.address(),
|
|
319
|
-
launch
|
|
320
|
-
}, options.signal)) {
|
|
297
|
+
for await (const event of representation(this.system).command("run", this.program.address(), launch, options.signal)) {
|
|
321
298
|
if (event.event === "started") {
|
|
322
|
-
|
|
299
|
+
const identity = event.process?.identity;
|
|
300
|
+
if (typeof identity !== "string")
|
|
301
|
+
throw new Error("The System returned an invalid started Process");
|
|
302
|
+
process = processHandle(this.system, required(representation(this.system).processes.get(identity), identity));
|
|
323
303
|
yield Object.freeze({ event: "started", process });
|
|
324
304
|
}
|
|
325
305
|
else if (event.event === "output") {
|
|
@@ -355,42 +335,32 @@ class ProgramProcesses extends Events {
|
|
|
355
335
|
}
|
|
356
336
|
findOrCreate(launch) { return this.createExact("find-or-create-process", launch); }
|
|
357
337
|
async exitAll() {
|
|
358
|
-
|
|
359
|
-
await Promise.all(processes.map(process => process.exit()));
|
|
360
|
-
return processes.map(process => process.identity);
|
|
338
|
+
return await representation(this.system).call("/process/exit-all", this.program.identity, "");
|
|
361
339
|
}
|
|
362
340
|
async createExact(word, launch) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
throw new Error("The System did not confirm the created Process");
|
|
341
|
+
const route = word === "create-process" ? "/program/create-process" : "/program/find-or-create-process";
|
|
342
|
+
const identity = await representation(this.system).call(route, this.program.address(), launch, null);
|
|
343
|
+
return processHandle(this.system, required(representation(this.system).processes.get(identity), identity));
|
|
368
344
|
}
|
|
369
345
|
}
|
|
370
346
|
class ProcessRegistry extends Events {
|
|
371
347
|
system;
|
|
372
348
|
constructor(system) {
|
|
373
|
-
super(["create", "exit"], (event,
|
|
374
|
-
|
|
375
|
-
|
|
349
|
+
super(["create", "exit"], (event, subscriber) => {
|
|
350
|
+
if (event === null)
|
|
351
|
+
throw new Error("System Process events are named");
|
|
352
|
+
return representation(system).on(`process:${event}`, (...values) => subscriber(processEvent(system, event, values)));
|
|
353
|
+
});
|
|
376
354
|
this.system = system;
|
|
377
355
|
}
|
|
378
356
|
list() { return listProcesses(this.system); }
|
|
379
357
|
async find(identity) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
return processHandle(this.system, snapshot);
|
|
383
|
-
}
|
|
384
|
-
catch (error) {
|
|
385
|
-
if (unknown(error, "Process"))
|
|
386
|
-
return null;
|
|
387
|
-
throw error;
|
|
388
|
-
}
|
|
358
|
+
const process = representation(this.system).processes.get(identity);
|
|
359
|
+
return process ? processHandle(this.system, process) : null;
|
|
389
360
|
}
|
|
390
361
|
}
|
|
391
362
|
class ProcessHandle extends ProcessBase {
|
|
392
363
|
system;
|
|
393
|
-
snapshot;
|
|
394
364
|
identity;
|
|
395
365
|
name;
|
|
396
366
|
startedAt;
|
|
@@ -399,30 +369,32 @@ class ProcessHandle extends ProcessBase {
|
|
|
399
369
|
constructor(system, snapshot) {
|
|
400
370
|
super();
|
|
401
371
|
this.system = system;
|
|
402
|
-
this
|
|
403
|
-
bindEvents(this, new Events(["exit"], (
|
|
404
|
-
capability: "process", operation: "wait", input: { process: snapshot.identity, event, timeout }
|
|
405
|
-
}, signal).then(exactProcessEvent)));
|
|
372
|
+
processSnapshots.set(this, snapshot);
|
|
373
|
+
bindEvents(this, new Events(["exit"], (_event, subscriber) => (representation(system).on(`process:${snapshot.reference}:exit`, value => subscriber(value)))));
|
|
406
374
|
this.identity = snapshot.identity;
|
|
407
375
|
this.name = snapshot.name;
|
|
408
376
|
this.startedAt = new Date(snapshot.startedAt);
|
|
409
|
-
this.server = new
|
|
410
|
-
this.client = new
|
|
377
|
+
this.server = new ServerEndpointHandle(system, this);
|
|
378
|
+
this.client = new ClientEndpointHandle(system, this);
|
|
379
|
+
}
|
|
380
|
+
program() {
|
|
381
|
+
const snapshot = processState(this.system, this);
|
|
382
|
+
return programHandle(this.system, required(representation(this.system).programs.get(snapshot.program), snapshot.program));
|
|
411
383
|
}
|
|
412
|
-
program() { return programHandle(this.system, required(this.snapshot.programSnapshot, this.snapshot.program)); }
|
|
413
384
|
async parent() {
|
|
414
385
|
if (!await this.exists())
|
|
415
386
|
throw new Error(`Process "${this.identity}" no longer exists`);
|
|
416
|
-
|
|
387
|
+
const snapshot = processState(this.system, this);
|
|
388
|
+
if (snapshot.parent === null)
|
|
417
389
|
return null;
|
|
418
|
-
const parent = await this.system.process.find(
|
|
390
|
+
const parent = await this.system.process.find(snapshot.parent.identity);
|
|
419
391
|
if (!parent)
|
|
420
392
|
throw new Error("The parent Process no longer exists");
|
|
421
393
|
return parent;
|
|
422
394
|
}
|
|
423
|
-
async option(name) { return this.
|
|
395
|
+
async option(name) { return processState(this.system, this).options[name]; }
|
|
424
396
|
async exit() {
|
|
425
|
-
await
|
|
397
|
+
await representation(this.system).call("/process/exit", this.identity);
|
|
426
398
|
}
|
|
427
399
|
async exited() { return await this.system.process.find(this.identity) === null; }
|
|
428
400
|
async exists() { return !await this.exited(); }
|
|
@@ -433,52 +405,37 @@ class EndpointOperations extends Events {
|
|
|
433
405
|
endpoint;
|
|
434
406
|
lifecycle;
|
|
435
407
|
constructor(system, owner, endpoint) {
|
|
436
|
-
super([], (event,
|
|
437
|
-
|
|
438
|
-
:
|
|
439
|
-
|
|
440
|
-
|
|
408
|
+
super([], (event, subscriber, impossible) => representation(system).follow({
|
|
409
|
+
scope: "endpoint",
|
|
410
|
+
process: owner.identity,
|
|
411
|
+
endpoint,
|
|
412
|
+
event
|
|
413
|
+
}, (_received, payload) => subscriber(payload), impossible));
|
|
441
414
|
this.system = system;
|
|
442
415
|
this.owner = owner;
|
|
443
416
|
this.endpoint = endpoint;
|
|
444
|
-
this.lifecycle = new Events(["start", "stop"], (event,
|
|
445
|
-
return waitEndpointLifecycle(system, owner, endpoint, event, signal, timeout);
|
|
446
|
-
});
|
|
417
|
+
this.lifecycle = new Events(["start", "stop"], (event, subscriber, impossible) => (endpointLifecycle(system, owner, endpoint, event, subscriber, impossible)));
|
|
447
418
|
}
|
|
448
419
|
process() { return Promise.resolve(this.owner); }
|
|
449
420
|
async exists() {
|
|
450
|
-
|
|
451
|
-
return value.running;
|
|
421
|
+
return endpointState(this.system, this.owner, this.endpoint) !== null;
|
|
452
422
|
}
|
|
453
423
|
async start(launch = {}) { await this.operation("start", launch); }
|
|
454
424
|
async stop() { await this.operation("stop"); }
|
|
455
425
|
async waitReady(timeout) {
|
|
456
|
-
await
|
|
457
|
-
process: this.owner.identity, endpoint: this.endpoint, timeout
|
|
458
|
-
} });
|
|
426
|
+
await waitEndpointReady(this.system, this.owner, this.endpoint, timeout);
|
|
459
427
|
}
|
|
460
428
|
async isService() {
|
|
461
|
-
return
|
|
462
|
-
capability: "endpoint", operation: "isService", process: this.owner.identity, endpoint: this.endpoint
|
|
463
|
-
});
|
|
429
|
+
return endpointState(this.system, this.owner, this.endpoint)?.service === true;
|
|
464
430
|
}
|
|
465
431
|
publish(event, payload) {
|
|
466
|
-
void
|
|
467
|
-
process: this.owner.identity, endpoint: this.endpoint, event, payload
|
|
468
|
-
} });
|
|
469
|
-
}
|
|
470
|
-
inspect() {
|
|
471
|
-
return transport(this.system).control({ capability: "endpoint", operation: "inspect", input: {
|
|
472
|
-
process: this.owner.identity, endpoint: this.endpoint
|
|
473
|
-
} });
|
|
432
|
+
void representation(this.system).call("/process/endpoint/publish", this.owner.identity, this.endpoint, event, payload);
|
|
474
433
|
}
|
|
475
434
|
async operation(operation, launch) {
|
|
476
|
-
await
|
|
477
|
-
process: this.owner.identity, endpoint: this.endpoint, ...(launch ? { launch } : {})
|
|
478
|
-
} });
|
|
435
|
+
await representation(this.system).call(`/process/endpoint/${operation}`, this.owner.identity, this.endpoint, launch);
|
|
479
436
|
}
|
|
480
437
|
}
|
|
481
|
-
class
|
|
438
|
+
class ServerEndpointHandle extends ServerEndpointBase {
|
|
482
439
|
system;
|
|
483
440
|
owner;
|
|
484
441
|
endpoint = "server";
|
|
@@ -490,7 +447,7 @@ class ServerEndpoint extends ServerBase {
|
|
|
490
447
|
this.system = system;
|
|
491
448
|
this.owner = owner;
|
|
492
449
|
this.base = new EndpointOperations(system, owner, "server");
|
|
493
|
-
this.traffic = new ServerTrafficHandle((
|
|
450
|
+
this.traffic = new ServerTrafficHandle(representation(system), owner.identity, "server", value => endpointFromReference(system, value));
|
|
494
451
|
this.lifecycle = this.base.lifecycle;
|
|
495
452
|
bindEvents(this, this.base);
|
|
496
453
|
}
|
|
@@ -502,17 +459,16 @@ class ServerEndpoint extends ServerBase {
|
|
|
502
459
|
stop() { return this.base.stop(); }
|
|
503
460
|
publish(event, payload) { return this.base.publish(event, payload); }
|
|
504
461
|
async ask(event, payload) {
|
|
505
|
-
return await
|
|
506
|
-
process: this.owner.identity, endpoint: "server", event, payload
|
|
507
|
-
} });
|
|
462
|
+
return await this.askWithin(event, payload, 10_000);
|
|
508
463
|
}
|
|
509
464
|
timeout(milliseconds) {
|
|
510
|
-
return { ask: (event, payload) =>
|
|
511
|
-
|
|
512
|
-
|
|
465
|
+
return { ask: (event, payload) => this.askWithin(event, payload, milliseconds) };
|
|
466
|
+
}
|
|
467
|
+
askWithin(event, payload, timeout) {
|
|
468
|
+
return representation(this.system).call("/process/endpoint/ask", this.owner.identity, event, payload, timeout);
|
|
513
469
|
}
|
|
514
470
|
}
|
|
515
|
-
class
|
|
471
|
+
class ClientEndpointHandle extends ClientEndpointBase {
|
|
516
472
|
endpoint = "client";
|
|
517
473
|
traffic;
|
|
518
474
|
lifecycle;
|
|
@@ -521,7 +477,7 @@ class ClientEndpoint extends ClientBase {
|
|
|
521
477
|
constructor(system, owner) {
|
|
522
478
|
super();
|
|
523
479
|
this.base = new EndpointOperations(system, owner, "client");
|
|
524
|
-
this.traffic = new EndpointTrafficHandle((
|
|
480
|
+
this.traffic = new EndpointTrafficHandle(representation(system), owner.identity, "client", value => endpointFromReference(system, value));
|
|
525
481
|
this.lifecycle = this.base.lifecycle;
|
|
526
482
|
bindEvents(this, this.base);
|
|
527
483
|
this.window = new SystemWindow(system, owner);
|
|
@@ -538,9 +494,11 @@ class SystemWindow extends Events {
|
|
|
538
494
|
system;
|
|
539
495
|
process;
|
|
540
496
|
constructor(system, process) {
|
|
541
|
-
super(["move", "resize", "geometry", "minimize", "changeTitle", "front"], (event,
|
|
542
|
-
|
|
543
|
-
|
|
497
|
+
super(["move", "resize", "geometry", "minimize", "changeTitle", "front"], (event, subscriber) => {
|
|
498
|
+
if (event === null)
|
|
499
|
+
throw new Error("Window events are named");
|
|
500
|
+
return representation(system).on(`window:${process.identity}:${event}`, subscriber);
|
|
501
|
+
});
|
|
544
502
|
this.system = system;
|
|
545
503
|
this.process = process;
|
|
546
504
|
}
|
|
@@ -548,20 +506,23 @@ class SystemWindow extends Events {
|
|
|
548
506
|
async position() { return (await this.snapshot()).position; }
|
|
549
507
|
async size() { return (await this.snapshot()).size; }
|
|
550
508
|
async minimized() { return (await this.snapshot()).minimized; }
|
|
551
|
-
async front() { return (
|
|
509
|
+
async front() { return frontWindow(this.system, this.process); }
|
|
552
510
|
async layer() { return (await this.snapshot()).layer; }
|
|
553
511
|
async location() { return (await this.snapshot()).location; }
|
|
554
|
-
async move(position) { await this.change("move",
|
|
555
|
-
async resize(size) { await this.change("resize",
|
|
556
|
-
async setGeometry(geometry) { await this.change("
|
|
557
|
-
async minimize(minimized = true) { await this.change("minimize",
|
|
558
|
-
async changeTitle(title) { await this.change("
|
|
559
|
-
async raise() { await this.change("raise"
|
|
512
|
+
async move(position) { await this.change("move", position); }
|
|
513
|
+
async resize(size) { await this.change("resize", size); }
|
|
514
|
+
async setGeometry(geometry) { await this.change("geometry", geometry); }
|
|
515
|
+
async minimize(minimized = true) { await this.change("minimize", minimized); }
|
|
516
|
+
async changeTitle(title) { await this.change("change-title", title); }
|
|
517
|
+
async raise() { await this.change("raise"); }
|
|
560
518
|
snapshot() {
|
|
561
|
-
|
|
519
|
+
const window = processState(this.system, this.process).client?.window;
|
|
520
|
+
if (!window)
|
|
521
|
+
throw new Error(`Process "${this.process.identity}" has no live Client Endpoint`);
|
|
522
|
+
return Promise.resolve(window);
|
|
562
523
|
}
|
|
563
524
|
async change(operation, input) {
|
|
564
|
-
await
|
|
525
|
+
await representation(this.system).call(`/process/${operation}`, this.process.identity, input);
|
|
565
526
|
}
|
|
566
527
|
}
|
|
567
528
|
class ServiceBase {
|
|
@@ -571,19 +532,19 @@ class ServiceBase {
|
|
|
571
532
|
constructor(system, key) {
|
|
572
533
|
this.system = system;
|
|
573
534
|
this.key = key;
|
|
574
|
-
this.lifecycle = new Events(["start", "stop"], (event,
|
|
575
|
-
|
|
576
|
-
},
|
|
535
|
+
this.lifecycle = new Events(["start", "stop"], (event, subscriber, impossible) => representation(system).follow({
|
|
536
|
+
scope: "service", key, kind: "lifecycle", event
|
|
537
|
+
}, (_received, payload) => subscriber(payload), impossible));
|
|
577
538
|
}
|
|
578
|
-
async exists() { return
|
|
539
|
+
async exists() { return serviceState(this.system, this.key) !== null; }
|
|
579
540
|
async waitReady(timeout) {
|
|
580
|
-
await
|
|
541
|
+
await representation(this.system).call("/process/service/wait-ready", this.key, timeout);
|
|
581
542
|
}
|
|
582
543
|
publish(event, payload) {
|
|
583
|
-
void
|
|
544
|
+
void representation(this.system).call("/process/service/publish", this.key, event, payload);
|
|
584
545
|
}
|
|
585
546
|
}
|
|
586
|
-
/** Node
|
|
547
|
+
/** Node SDK handle for a Service provided by a Server Endpoint. */
|
|
587
548
|
export class ServerService extends CoreServerService {
|
|
588
549
|
constructor() { super(); }
|
|
589
550
|
}
|
|
@@ -598,23 +559,21 @@ class ServerServiceHandle extends ServerService {
|
|
|
598
559
|
this.key = key;
|
|
599
560
|
this.base = new ServiceBase(system, key);
|
|
600
561
|
this.lifecycle = this.base.lifecycle;
|
|
601
|
-
bindEvents(this, new Events([], (event,
|
|
602
|
-
|
|
603
|
-
},
|
|
562
|
+
bindEvents(this, new Events([], (event, subscriber, impossible) => representation(system).follow({
|
|
563
|
+
scope: "service", key, kind: "events", event
|
|
564
|
+
}, (_received, payload) => subscriber(payload), impossible)));
|
|
604
565
|
}
|
|
605
566
|
exists() { return this.base.exists(); }
|
|
606
567
|
waitReady(timeout) { return this.base.waitReady(timeout); }
|
|
607
568
|
publish = (event, payload) => this.base.publish(event, payload);
|
|
608
569
|
async ask(event, payload) {
|
|
609
|
-
return await
|
|
570
|
+
return await representation(this.system).call("/process/service/ask", this.key, event, payload, 10_000);
|
|
610
571
|
}
|
|
611
572
|
timeout(milliseconds) {
|
|
612
|
-
return { ask: (event, payload) =>
|
|
613
|
-
capability: "service", operation: "ask", key: this.key, event, payload, timeout: milliseconds
|
|
614
|
-
}) };
|
|
573
|
+
return { ask: (event, payload) => representation(this.system).call("/process/service/ask", this.key, event, payload, milliseconds) };
|
|
615
574
|
}
|
|
616
575
|
}
|
|
617
|
-
/** Node
|
|
576
|
+
/** Node SDK handle for a Service provided by a Client Endpoint. */
|
|
618
577
|
export class ClientService extends CoreClientService {
|
|
619
578
|
constructor() { super(); }
|
|
620
579
|
}
|
|
@@ -625,27 +584,21 @@ class ClientServiceHandle extends ClientService {
|
|
|
625
584
|
super();
|
|
626
585
|
this.base = new ServiceBase(system, key);
|
|
627
586
|
this.lifecycle = this.base.lifecycle;
|
|
628
|
-
bindEvents(this, new Events([], (event,
|
|
629
|
-
|
|
630
|
-
},
|
|
587
|
+
bindEvents(this, new Events([], (event, subscriber, impossible) => representation(system).follow({
|
|
588
|
+
scope: "service", key, kind: "events", event
|
|
589
|
+
}, (_received, payload) => subscriber(payload), impossible)));
|
|
631
590
|
}
|
|
632
591
|
exists() { return this.base.exists(); }
|
|
633
592
|
waitReady(timeout) { return this.base.waitReady(timeout); }
|
|
634
593
|
publish = (event, payload) => this.base.publish(event, payload);
|
|
635
594
|
}
|
|
636
595
|
async function listProcesses(system) {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
if (!page.truncated || !page.data.length)
|
|
644
|
-
return processes;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
async function* command(system, request) {
|
|
648
|
-
for await (const event of transport(system).lifecycle(request)) {
|
|
596
|
+
return [...representation(system).processes.values()]
|
|
597
|
+
.sort((left, right) => right.startedAt.getTime() - left.startedAt.getTime())
|
|
598
|
+
.map(process => processHandle(system, process));
|
|
599
|
+
}
|
|
600
|
+
async function* command(system, operation, subject, value) {
|
|
601
|
+
for await (const event of representation(system).command(operation, subject, value)) {
|
|
649
602
|
if (event.event === "output")
|
|
650
603
|
yield {
|
|
651
604
|
stream: event.stream === "stderr" ? "stderr" : "stdout",
|
|
@@ -653,49 +606,21 @@ async function* command(system, request) {
|
|
|
653
606
|
};
|
|
654
607
|
}
|
|
655
608
|
}
|
|
656
|
-
|
|
609
|
+
function endpointLifecycle(system, owner, endpoint, event, subscriber, impossible) {
|
|
657
610
|
if (event !== "start" && event !== "stop")
|
|
658
611
|
throw new Error(`An Endpoint lifecycle has no "${event}" event`);
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}, signal);
|
|
664
|
-
}
|
|
665
|
-
function processEvent(system, value) {
|
|
666
|
-
const waited = value;
|
|
667
|
-
const payload = waited.payload;
|
|
668
|
-
if (waited.event === "exit" && payload)
|
|
669
|
-
return {
|
|
670
|
-
process: processHandle(system, required(payload.processSnapshot, String(payload.process ?? ""))),
|
|
671
|
-
status: payload.status,
|
|
672
|
-
code: payload.code,
|
|
673
|
-
signal: payload.signal
|
|
674
|
-
};
|
|
675
|
-
if (payload && typeof payload.identity === "string")
|
|
676
|
-
return processHandle(system, payload);
|
|
677
|
-
return payload;
|
|
678
|
-
}
|
|
679
|
-
function programProcessEvent(system, event, value) {
|
|
680
|
-
if (event === "create")
|
|
681
|
-
return processHandle(system, value);
|
|
682
|
-
const exit = value;
|
|
683
|
-
return {
|
|
684
|
-
process: processHandle(system, required(exit.process)),
|
|
685
|
-
status: exit.status,
|
|
686
|
-
code: exit.code,
|
|
687
|
-
signal: exit.signal
|
|
688
|
-
};
|
|
612
|
+
const model = representation(system);
|
|
613
|
+
const stopEvent = model.on(`endpoint:${processReference(owner)}:${endpoint}:${event}`, () => subscriber(undefined));
|
|
614
|
+
const stopExit = model.on(`process:${processReference(owner)}:exit`, () => impossible?.(new Error(`Process "${owner.identity}" exited`)));
|
|
615
|
+
return () => { stopEvent(); stopExit(); };
|
|
689
616
|
}
|
|
690
|
-
function
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
signal: payload.signal
|
|
698
|
-
};
|
|
617
|
+
function processEvent(system, event, values) {
|
|
618
|
+
const process = processHandle(system, required(values[0]));
|
|
619
|
+
return event === "exit" ? { process, ...values[1] } : process;
|
|
620
|
+
}
|
|
621
|
+
function programProcessEvent(system, event, values) {
|
|
622
|
+
const process = processHandle(system, required(values[0]));
|
|
623
|
+
return event === "exit" ? { process, ...values[1] } : process;
|
|
699
624
|
}
|
|
700
625
|
function bindEvents(target, events) {
|
|
701
626
|
Object.assign(target, eventsOf(events));
|
|
@@ -709,36 +634,84 @@ function eventsOf(events) {
|
|
|
709
634
|
}
|
|
710
635
|
function chronological(left, right) { return left.startedAt.getTime() - right.startedAt.getTime(); }
|
|
711
636
|
async function programStoragePath(system, handle, area) {
|
|
712
|
-
const value = await
|
|
637
|
+
const value = await representation(system).call("/program/area", handle, area, "path", []);
|
|
713
638
|
if (typeof value !== "string")
|
|
714
639
|
throw new Error("The System returned an invalid Program storage path");
|
|
715
640
|
return value;
|
|
716
641
|
}
|
|
717
642
|
function endpointFromReference(system, value) {
|
|
718
643
|
const reference = value;
|
|
719
|
-
if (!reference || (reference.kind !== "server" && reference.kind !== "client"))
|
|
644
|
+
if (!reference || (reference.kind !== "server" && reference.kind !== "client") || typeof reference.process?.identity !== "string") {
|
|
720
645
|
throw new Error("The System returned an invalid Endpoint reference");
|
|
721
|
-
|
|
646
|
+
}
|
|
647
|
+
const owner = processHandle(system, required(representation(system).processes.get(reference.process.identity), reference.process.identity));
|
|
722
648
|
return reference.kind === "server" ? owner.server : owner.client;
|
|
723
649
|
}
|
|
724
|
-
function
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
650
|
+
function processState(system, process) {
|
|
651
|
+
const original = required(processSnapshots.get(process));
|
|
652
|
+
const current = representation(system).processes.get(process.identity);
|
|
653
|
+
if (!current || current.reference !== original.reference)
|
|
654
|
+
throw new Error(`Process "${process.identity}" no longer exists`);
|
|
655
|
+
return current;
|
|
656
|
+
}
|
|
657
|
+
function processReference(process) { return required(processSnapshots.get(process)).reference; }
|
|
658
|
+
function endpointState(system, process, endpoint) {
|
|
659
|
+
const current = representation(system).processes.get(process.identity);
|
|
660
|
+
const original = required(processSnapshots.get(process));
|
|
661
|
+
return current?.reference === original.reference ? current[endpoint] : null;
|
|
662
|
+
}
|
|
663
|
+
function waitEndpointReady(system, process, endpoint, timeout = 10_000) {
|
|
664
|
+
if (endpointReady(endpointState(system, process, endpoint), endpoint))
|
|
665
|
+
return Promise.resolve();
|
|
666
|
+
return new Promise((resolve, reject) => {
|
|
667
|
+
const model = representation(system);
|
|
668
|
+
const reference = processReference(process);
|
|
669
|
+
const finish = (work) => {
|
|
670
|
+
clearTimeout(timer);
|
|
671
|
+
stopStart();
|
|
672
|
+
stopReady();
|
|
673
|
+
stopExit();
|
|
674
|
+
work();
|
|
675
|
+
};
|
|
676
|
+
const inspect = () => {
|
|
677
|
+
if (endpointReady(endpointState(system, process, endpoint), endpoint))
|
|
678
|
+
finish(resolve);
|
|
679
|
+
};
|
|
680
|
+
const stopStart = model.on(`endpoint:${reference}:${endpoint}:start`, inspect);
|
|
681
|
+
const stopReady = model.on(`endpoint:${reference}:${endpoint}:ready`, inspect);
|
|
682
|
+
const stopExit = model.on(`process:${reference}:exit`, () => finish(() => reject(new Error(`Process "${process.identity}" exited`))));
|
|
683
|
+
const timer = setTimeout(() => finish(() => reject(new Error("The Endpoint did not become ready before the timeout"))), timeout);
|
|
684
|
+
inspect();
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
function endpointReady(state, endpoint) {
|
|
688
|
+
return state !== null && (endpoint === "client" || "ready" in state && state.ready);
|
|
689
|
+
}
|
|
690
|
+
function serviceState(system, key) {
|
|
691
|
+
const model = representation(system);
|
|
692
|
+
const process = key.program === undefined
|
|
693
|
+
? model.processes.get(key.process)
|
|
694
|
+
: [...model.processes.values()].find(candidate => candidate.program === key.program && (candidate.identity === key.process || candidate.name === key.process));
|
|
695
|
+
const endpoint = process?.[key.endpoint];
|
|
696
|
+
return endpoint?.service === true ? endpoint : null;
|
|
697
|
+
}
|
|
698
|
+
function frontWindow(system, process) {
|
|
699
|
+
const window = processState(system, process).client?.window;
|
|
700
|
+
if (!window || window.minimized)
|
|
701
|
+
return false;
|
|
702
|
+
return ![...representation(system).processes.values()].some(candidate => {
|
|
703
|
+
const other = candidate.client?.window;
|
|
704
|
+
return other && !other.minimized && other.layer === window.layer && other.depth > window.depth;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
async function uploadRequest(system, value) {
|
|
708
|
+
const request = value;
|
|
709
|
+
if (request.operation === "access")
|
|
710
|
+
return representation(system).call("/uploads/access");
|
|
711
|
+
if (request.operation === "stat")
|
|
712
|
+
return representation(system).call("/uploads/stat", request.file);
|
|
713
|
+
throw new Error(`The Uploads API does not know "${String(request.operation)}"`);
|
|
740
714
|
}
|
|
741
|
-
function unknown(error, entity) { return error instanceof Error && error.message.startsWith(`Unknown ${entity}`); }
|
|
742
715
|
function required(value, identity = "") {
|
|
743
716
|
if (value !== undefined)
|
|
744
717
|
return value;
|
|
@@ -747,5 +720,5 @@ function required(value, identity = "") {
|
|
|
747
720
|
export const Program = CoreProgram;
|
|
748
721
|
export const Process = CoreProcess;
|
|
749
722
|
export const Endpoint = CoreEndpoint;
|
|
750
|
-
export const
|
|
751
|
-
export const
|
|
723
|
+
export const ServerEndpoint = CoreServerEndpoint;
|
|
724
|
+
export const ClientEndpoint = CoreClientEndpoint;
|