@phreshos/client 0.1.0 → 0.1.1
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/LICENSE +19 -0
- package/README.md +101 -13
- package/dist/channel.d.ts +4 -0
- package/dist/channel.js +3 -0
- package/dist/current.d.ts +13 -10
- package/dist/current.js +67 -14
- package/dist/domain.d.ts +83 -30
- package/dist/domain.js +202 -77
- package/dist/handle-registry.d.ts +7 -0
- package/dist/handle-registry.js +24 -0
- package/dist/host.d.ts +15 -20
- package/dist/host.js +10 -35
- package/dist/main.d.ts +5 -3
- package/dist/main.js +3 -1
- package/dist/permissions.d.ts +8 -0
- package/dist/permissions.js +21 -0
- package/dist/pointer.d.ts +35 -0
- package/dist/pointer.js +54 -0
- package/dist/surface.d.ts +29 -0
- package/dist/surface.js +34 -0
- package/dist/theme.d.ts +13 -0
- package/dist/theme.js +29 -0
- package/dist/wire.d.ts +12 -4
- package/dist/wire.js +52 -29
- package/package.json +29 -4
package/dist/domain.js
CHANGED
|
@@ -1,38 +1,49 @@
|
|
|
1
|
-
import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer
|
|
1
|
+
import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer } from "@phreshos/core";
|
|
2
2
|
import Events from "./events.js";
|
|
3
3
|
import Deadline from "./deadline.js";
|
|
4
|
+
import HandleRegistry from "./handle-registry.js";
|
|
4
5
|
import { area, sql, store } from "./storage.js";
|
|
5
6
|
import wire from "./wire.js";
|
|
7
|
+
const handles = new HandleRegistry();
|
|
6
8
|
const ProgramBase = CoreProgram;
|
|
7
9
|
const ProcessBase = CoreProcess;
|
|
8
10
|
const ServerBase = CoreServer;
|
|
9
11
|
const ClientBase = CoreClient;
|
|
10
|
-
const WindowBase = CoreWindow;
|
|
11
12
|
class ProgramHandle extends ProgramBase {
|
|
12
13
|
identity;
|
|
13
|
-
|
|
14
|
-
version;
|
|
15
|
-
description;
|
|
16
|
-
server;
|
|
17
|
-
client;
|
|
14
|
+
reference;
|
|
18
15
|
data = area("data");
|
|
19
16
|
cache = area("cache");
|
|
20
17
|
store = store();
|
|
21
18
|
logs = sql("logs");
|
|
22
19
|
database = sql("database");
|
|
20
|
+
record;
|
|
23
21
|
constructor(record) {
|
|
24
22
|
super();
|
|
25
23
|
this.identity = record.identity;
|
|
26
|
-
this.
|
|
27
|
-
this.
|
|
28
|
-
this
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
this.reference = record.reference;
|
|
25
|
+
this.record = record;
|
|
26
|
+
bindEvents(this, scoped("host-end", record.reference, programEvent));
|
|
27
|
+
}
|
|
28
|
+
get name() { return this.record.name; }
|
|
29
|
+
get version() { return this.record.version; }
|
|
30
|
+
get description() { return this.record.description; }
|
|
31
|
+
get server() { return this.record.server; }
|
|
32
|
+
get client() { return this.record.client; }
|
|
33
|
+
update(record) {
|
|
34
|
+
if (record.reference !== this.reference)
|
|
35
|
+
throw new Error("A Program handle cannot become another Program");
|
|
36
|
+
this.record = record;
|
|
32
37
|
}
|
|
33
38
|
async processes() {
|
|
34
39
|
const answer = await wire.request(["processes"]);
|
|
35
|
-
return answer[0].map(process);
|
|
40
|
+
return answer[0].map(record => process(record));
|
|
41
|
+
}
|
|
42
|
+
async firstProcess() {
|
|
43
|
+
return chronological(await this.processes())[0] ?? null;
|
|
44
|
+
}
|
|
45
|
+
async lastProcess() {
|
|
46
|
+
return chronological(await this.processes()).at(-1) ?? null;
|
|
36
47
|
}
|
|
37
48
|
async getProcess(identityOrName) {
|
|
38
49
|
const answer = await wire.request(["program-process", undefined, identityOrName]);
|
|
@@ -46,6 +57,10 @@ class ProgramHandle extends ProgramBase {
|
|
|
46
57
|
const answer = await wire.request(["api-docs"]);
|
|
47
58
|
return answer[0];
|
|
48
59
|
}
|
|
60
|
+
async icon(size = "medium") {
|
|
61
|
+
const answer = await wire.request(["icon", size]);
|
|
62
|
+
return new Blob([Uint8Array.from(answer[0])], { type: "image/png" });
|
|
63
|
+
}
|
|
49
64
|
async installed() {
|
|
50
65
|
const answer = await wire.request(["installed"]);
|
|
51
66
|
return answer[0];
|
|
@@ -57,64 +72,70 @@ class ProgramHandle extends ProgramBase {
|
|
|
57
72
|
return answer[0];
|
|
58
73
|
}
|
|
59
74
|
}
|
|
75
|
+
function chronological(processes) {
|
|
76
|
+
return processes.sort((left, right) => left.startedAt.getTime() - right.startedAt.getTime());
|
|
77
|
+
}
|
|
60
78
|
class ProcessHandle extends ProcessBase {
|
|
61
79
|
identity;
|
|
80
|
+
reference;
|
|
62
81
|
name;
|
|
63
82
|
startedAt;
|
|
64
83
|
server;
|
|
65
84
|
client;
|
|
66
85
|
ownerProgram;
|
|
67
86
|
options;
|
|
68
|
-
constructor(record) {
|
|
87
|
+
constructor(record, endpoints = {}) {
|
|
69
88
|
super();
|
|
70
89
|
this.identity = record.identity;
|
|
90
|
+
this.reference = record.reference;
|
|
71
91
|
this.name = record.name;
|
|
72
92
|
this.startedAt = new Date(record.startedAt);
|
|
73
93
|
this.ownerProgram = program(record.program);
|
|
74
94
|
this.options = record.options;
|
|
75
|
-
this.server =
|
|
76
|
-
this.client =
|
|
77
|
-
bindEvents(this, scoped("process-host", record.
|
|
95
|
+
this.server = endpointHandle(this, "server", endpoints.server);
|
|
96
|
+
this.client = endpointHandle(this, "client", endpoints.client);
|
|
97
|
+
bindEvents(this, scoped("process-host", record.reference, processEvent));
|
|
78
98
|
}
|
|
79
99
|
program() { return this.ownerProgram; }
|
|
100
|
+
get address() { return { identity: this.identity, reference: this.reference }; }
|
|
80
101
|
async parent() {
|
|
81
|
-
const answer = await wire.request(["parent", this.
|
|
102
|
+
const answer = await wire.request(["parent", this.address]);
|
|
82
103
|
return answer[0] ? process(answer[0]) : null;
|
|
83
104
|
}
|
|
84
105
|
async option(name) {
|
|
85
106
|
if (name in this.options)
|
|
86
107
|
return this.options[name];
|
|
87
|
-
const answer = await wire.request(["option", this.
|
|
108
|
+
const answer = await wire.request(["option", this.address, name]);
|
|
88
109
|
return answer[0];
|
|
89
110
|
}
|
|
90
|
-
async exit() { await wire.request(["exit", this.
|
|
111
|
+
async exit() { await wire.request(["exit", this.address]); }
|
|
91
112
|
async exited() {
|
|
92
|
-
const answer = await wire.request(["exited", this.
|
|
113
|
+
const answer = await wire.request(["exited", this.address]);
|
|
93
114
|
return answer[0];
|
|
94
115
|
}
|
|
95
116
|
}
|
|
96
|
-
class TrafficHandle extends Events {
|
|
97
|
-
|
|
117
|
+
export class TrafficHandle extends Events {
|
|
118
|
+
target;
|
|
98
119
|
kind;
|
|
99
|
-
constructor(
|
|
100
|
-
super((event, listener, impossible) => wire.observe(
|
|
120
|
+
constructor(target, kind) {
|
|
121
|
+
super((event, listener, impossible) => wire.observe(target, kind, "publish", event, value => listener(trafficMessage(value)), impossible), observer => wire.observe(target, kind, "publish", null, (event, value) => {
|
|
101
122
|
if (typeof event === "string")
|
|
102
123
|
observer(event, trafficMessage(value));
|
|
103
124
|
}));
|
|
104
|
-
this.
|
|
125
|
+
this.target = target;
|
|
105
126
|
this.kind = kind;
|
|
106
127
|
}
|
|
107
128
|
observeAsks(observer) {
|
|
108
|
-
return wire.observe(this.
|
|
129
|
+
return wire.observe(this.target, this.kind, "ask", null, (event, questionId, value) => {
|
|
109
130
|
if (typeof event !== "string" || typeof questionId !== "string")
|
|
110
131
|
return;
|
|
111
132
|
observer({ event, questionId, message: trafficMessage(value) });
|
|
112
133
|
});
|
|
113
134
|
}
|
|
114
135
|
}
|
|
115
|
-
class ServerTrafficHandle extends TrafficHandle {
|
|
136
|
+
export class ServerTrafficHandle extends TrafficHandle {
|
|
116
137
|
observeAnswers(observer) {
|
|
117
|
-
return wire.observe(this.
|
|
138
|
+
return wire.observe(this.target, "server", "answer", null, (event, questionId, value) => {
|
|
118
139
|
if (typeof event !== "string" || typeof questionId !== "string")
|
|
119
140
|
return;
|
|
120
141
|
const raw = value;
|
|
@@ -128,31 +149,32 @@ class ServerHandle extends ServerBase {
|
|
|
128
149
|
constructor(owner) {
|
|
129
150
|
super();
|
|
130
151
|
this.owner = owner;
|
|
131
|
-
this.traffic = new ServerTrafficHandle(owner, "server");
|
|
152
|
+
this.traffic = new ServerTrafficHandle(owner.address, "server");
|
|
153
|
+
bindEvents(this, endpointEvents(owner.address, "server"));
|
|
132
154
|
}
|
|
133
|
-
process() { return this.owner; }
|
|
134
|
-
publish(event, payload) { wire.send("end-host", "send", this.owner.
|
|
155
|
+
async process() { return this.owner; }
|
|
156
|
+
publish(event, payload = undefined) { wire.send("end-host", "send", this.owner.address, "server", event, payload); }
|
|
135
157
|
async exists() {
|
|
136
|
-
const answer = await wire.request(["exists", "server", this.owner.
|
|
158
|
+
const answer = await wire.request(["exists", "server", this.owner.address]);
|
|
137
159
|
return answer[0];
|
|
138
160
|
}
|
|
139
|
-
async start() { await wire.request(["start-endpoint", this.owner.
|
|
140
|
-
async stop() { await wire.request(["stop-endpoint", this.owner.
|
|
141
|
-
async waitReady(timeout) { await wire.request(["wait-ready", this.owner.
|
|
142
|
-
async ask(event, payload) {
|
|
161
|
+
async start() { await wire.request(["start-endpoint", this.owner.address, "server"]); }
|
|
162
|
+
async stop() { await wire.request(["stop-endpoint", this.owner.address, "server"]); }
|
|
163
|
+
async waitReady(timeout) { await wire.request(["wait-ready", this.owner.address], timeout); }
|
|
164
|
+
async ask(event, payload = undefined) {
|
|
143
165
|
return this.askWithin(undefined, event, payload);
|
|
144
166
|
}
|
|
145
167
|
timeout(milliseconds) {
|
|
146
|
-
return { ask: (event, payload) => this.askWithin(milliseconds, event, payload) };
|
|
168
|
+
return { ask: (event, payload = undefined) => this.askWithin(milliseconds, event, payload) };
|
|
147
169
|
}
|
|
148
170
|
async askWithin(timeout, event, payload) {
|
|
149
171
|
const deadline = new Deadline(timeout);
|
|
150
|
-
await wire.requestWithin(["wait-ready", this.owner.
|
|
151
|
-
const source = await wire.identity;
|
|
152
|
-
const address = `client:${source}:${crypto.randomUUID()}`;
|
|
172
|
+
await wire.requestWithin(["wait-ready", this.owner.address, true], deadline);
|
|
173
|
+
const source = await wire.identity();
|
|
174
|
+
const address = `client:${source.process}:${crypto.randomUUID()}`;
|
|
153
175
|
const questionId = crypto.randomUUID();
|
|
154
176
|
const waiting = wire.expectWithin(address, deadline);
|
|
155
|
-
wire.send("end-host", "ask", this.owner.
|
|
177
|
+
wire.send("end-host", "ask", this.owner.address, "server", address, questionId, event, payload);
|
|
156
178
|
try {
|
|
157
179
|
return await waiting;
|
|
158
180
|
}
|
|
@@ -164,34 +186,33 @@ class ServerHandle extends ServerBase {
|
|
|
164
186
|
class ClientHandle extends ClientBase {
|
|
165
187
|
owner;
|
|
166
188
|
traffic;
|
|
189
|
+
window;
|
|
167
190
|
constructor(owner) {
|
|
168
191
|
super();
|
|
169
192
|
this.owner = owner;
|
|
170
|
-
this.traffic = new TrafficHandle(owner, "client");
|
|
193
|
+
this.traffic = new TrafficHandle(owner.address, "client");
|
|
194
|
+
this.window = window(async () => owner.address);
|
|
195
|
+
bindEvents(this, endpointEvents(owner.address, "client"));
|
|
171
196
|
}
|
|
172
|
-
process() { return this.owner; }
|
|
173
|
-
publish(event, payload) { wire.send("end-host", "send", this.owner.
|
|
197
|
+
async process() { return this.owner; }
|
|
198
|
+
publish(event, payload = undefined) { wire.send("end-host", "send", this.owner.address, "client", event, payload); }
|
|
174
199
|
async exists() {
|
|
175
|
-
const answer = await wire.request(["exists", "client", this.owner.
|
|
200
|
+
const answer = await wire.request(["exists", "client", this.owner.address]);
|
|
176
201
|
return answer[0];
|
|
177
202
|
}
|
|
178
|
-
async start(overrides = {}) { await wire.request(["start-endpoint", this.owner.
|
|
179
|
-
async stop() { await wire.request(["stop-endpoint", this.owner.
|
|
180
|
-
async window() {
|
|
181
|
-
await wire.request(["window", this.owner.identity]);
|
|
182
|
-
return new WindowHandle(this);
|
|
183
|
-
}
|
|
203
|
+
async start(overrides = {}) { await wire.request(["start-endpoint", this.owner.address, "client", overrides]); }
|
|
204
|
+
async stop() { await wire.request(["stop-endpoint", this.owner.address, "client"]); }
|
|
184
205
|
}
|
|
185
|
-
class WindowHandle extends
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
206
|
+
class WindowHandle extends Events {
|
|
207
|
+
target;
|
|
208
|
+
surface;
|
|
209
|
+
constructor(target) {
|
|
210
|
+
super(...deferredScoped("host-end", target, (_event, values) => values[0]));
|
|
211
|
+
this.target = target;
|
|
212
|
+
this.surface = new WindowSurfaceHandle(target);
|
|
191
213
|
}
|
|
192
|
-
client() { return this.owner; }
|
|
193
214
|
async state() {
|
|
194
|
-
const answer = await wire.request(["window", this.
|
|
215
|
+
const answer = await wire.request(["window", await this.target()]);
|
|
195
216
|
return answer[0];
|
|
196
217
|
}
|
|
197
218
|
async title() { return (await this.state()).title; }
|
|
@@ -201,13 +222,83 @@ class WindowHandle extends WindowBase {
|
|
|
201
222
|
async front() { return (await this.state()).front; }
|
|
202
223
|
async layer() { return (await this.state()).layer; }
|
|
203
224
|
async location() { return (await this.state()).location; }
|
|
204
|
-
async move(position) { await wire.request(["move", this.
|
|
205
|
-
localMove(position) { wire.
|
|
206
|
-
async resize(size) { await wire.request(["resize", this.
|
|
207
|
-
localResize(size) { wire.
|
|
208
|
-
async minimize(minimized = true) { await wire.request(["minimize", this.
|
|
209
|
-
async changeTitle(title) { await wire.request(["changeTitle", this.
|
|
210
|
-
async raise() { await wire.request(["raise", this.
|
|
225
|
+
async move(position) { await wire.request(["move", await this.target(), position]); }
|
|
226
|
+
async localMove(position) { await wire.request(["localMove", await this.target(), position]); }
|
|
227
|
+
async resize(size) { await wire.request(["resize", await this.target(), size]); }
|
|
228
|
+
async localResize(size) { await wire.request(["localResize", await this.target(), size]); }
|
|
229
|
+
async minimize(minimized = true) { await wire.request(["minimize", await this.target(), minimized]); }
|
|
230
|
+
async changeTitle(title) { await wire.request(["changeTitle", await this.target(), title]); }
|
|
231
|
+
async raise() { await wire.request(["raise", await this.target()]); }
|
|
232
|
+
}
|
|
233
|
+
class WindowSurfaceHandle extends Events {
|
|
234
|
+
target;
|
|
235
|
+
constructor(target) {
|
|
236
|
+
super((event, listener, impossible) => {
|
|
237
|
+
if (event !== "change") {
|
|
238
|
+
impossible?.(new Error(`A Window Surface has no "${String(event)}" event`));
|
|
239
|
+
return () => undefined;
|
|
240
|
+
}
|
|
241
|
+
return deferred(target, subject => wire.on("host-end", "surface", (...values) => {
|
|
242
|
+
const message = unscoped(subject, values);
|
|
243
|
+
if (message)
|
|
244
|
+
listener(message[0]);
|
|
245
|
+
}, subject, impossible), impossible);
|
|
246
|
+
}, observer => deferred(target, subject => wire.on("host-end", "surface", (...values) => {
|
|
247
|
+
const message = unscoped(subject, values);
|
|
248
|
+
if (message)
|
|
249
|
+
observer("change", message[0]);
|
|
250
|
+
}, subject)));
|
|
251
|
+
this.target = target;
|
|
252
|
+
}
|
|
253
|
+
async snapshot() {
|
|
254
|
+
const answer = await wire.request(["window", await this.target()]);
|
|
255
|
+
return answer[0].surface;
|
|
256
|
+
}
|
|
257
|
+
async set(settings = {}) { await wire.request(["surfaceSet", await this.target(), settings]); }
|
|
258
|
+
async remove() { await wire.request(["surfaceRemove", await this.target()]); }
|
|
259
|
+
}
|
|
260
|
+
function deferredScoped(route, target, convert) {
|
|
261
|
+
return [
|
|
262
|
+
(event, listener, impossible) => {
|
|
263
|
+
if (!windowEvent(event)) {
|
|
264
|
+
impossible?.(new Error(`A Window has no "${event}" event`));
|
|
265
|
+
return () => undefined;
|
|
266
|
+
}
|
|
267
|
+
return deferred(target, subject => wire.on(route, event, (...values) => {
|
|
268
|
+
const message = unscoped(subject, values);
|
|
269
|
+
if (message)
|
|
270
|
+
listener(convert(event, message));
|
|
271
|
+
}, subject, impossible), impossible);
|
|
272
|
+
},
|
|
273
|
+
observer => deferred(target, subject => wire.onAll(route, (event, ...values) => {
|
|
274
|
+
if (typeof event !== "string" || !windowEvent(event))
|
|
275
|
+
return;
|
|
276
|
+
const message = unscoped(subject, values);
|
|
277
|
+
if (message)
|
|
278
|
+
observer(event, convert(event, message));
|
|
279
|
+
}, subject))
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
function windowEvent(event) {
|
|
283
|
+
return event === "move" || event === "resize" || event === "minimize" || event === "changeTitle" || event === "front";
|
|
284
|
+
}
|
|
285
|
+
function deferred(target, register, impossible) {
|
|
286
|
+
let active = true;
|
|
287
|
+
let stop = () => undefined;
|
|
288
|
+
void target().then(address => {
|
|
289
|
+
if (active)
|
|
290
|
+
stop = register(address.reference);
|
|
291
|
+
}, error => {
|
|
292
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
293
|
+
if (active && impossible)
|
|
294
|
+
impossible(failure);
|
|
295
|
+
else if (active)
|
|
296
|
+
queueMicrotask(() => { throw failure; });
|
|
297
|
+
});
|
|
298
|
+
return () => {
|
|
299
|
+
active = false;
|
|
300
|
+
stop();
|
|
301
|
+
};
|
|
211
302
|
}
|
|
212
303
|
export function scoped(route, subject, convert) {
|
|
213
304
|
return new Events((event, listener, impossible) => wire.on(route, event, (...values) => {
|
|
@@ -222,16 +313,23 @@ export function scoped(route, subject, convert) {
|
|
|
222
313
|
observer(event, convert(event, message));
|
|
223
314
|
}, subject));
|
|
224
315
|
}
|
|
316
|
+
/** Destinationless events originating from one Endpoint handle. */
|
|
317
|
+
export function endpointEvents(target, half) {
|
|
318
|
+
return new Events((event, listener, impossible) => wire.follow(target, half, event, listener, impossible), observer => wire.follow(target, half, null, (event, payload) => {
|
|
319
|
+
if (typeof event === "string")
|
|
320
|
+
observer(event, payload);
|
|
321
|
+
}));
|
|
322
|
+
}
|
|
225
323
|
function unscoped(subject, values) {
|
|
226
324
|
if (subject === null)
|
|
227
325
|
return values;
|
|
228
326
|
return values[0] === subject ? values.slice(1) : null;
|
|
229
327
|
}
|
|
230
328
|
function programEvent(event, values) {
|
|
231
|
-
if (event === "
|
|
329
|
+
if (event === "endpointStart" || event === "endpointStop")
|
|
330
|
+
return lifecycleEndpoint(values[0], values[1]);
|
|
331
|
+
if (event === "processCreate")
|
|
232
332
|
return process(values[0]);
|
|
233
|
-
if (event === "serverStop")
|
|
234
|
-
return { process: process(values[0]), code: numberOrNull(values[1]), signal: stringOrNull(values[2]) };
|
|
235
333
|
if (event === "processExit")
|
|
236
334
|
return { process: process(values[0]), ...exit(values[1], values[2]) };
|
|
237
335
|
if (event === "uninstall")
|
|
@@ -239,12 +337,20 @@ function programEvent(event, values) {
|
|
|
239
337
|
return undefined;
|
|
240
338
|
}
|
|
241
339
|
function processEvent(event, values) {
|
|
242
|
-
if (event === "
|
|
243
|
-
return
|
|
340
|
+
if (event === "endpointStart" || event === "endpointStop")
|
|
341
|
+
return lifecycleEndpoint(values[0], values[1]);
|
|
244
342
|
if (event === "exit")
|
|
245
343
|
return exit(values[0], values[1]);
|
|
246
344
|
return undefined;
|
|
247
345
|
}
|
|
346
|
+
function lifecycleEndpoint(record, kind) {
|
|
347
|
+
const owner = process(record);
|
|
348
|
+
if (kind === "server")
|
|
349
|
+
return owner.server;
|
|
350
|
+
if (kind === "client")
|
|
351
|
+
return owner.client;
|
|
352
|
+
throw new Error("The host returned an invalid Endpoint lifecycle event");
|
|
353
|
+
}
|
|
248
354
|
export function exit(code, signal) {
|
|
249
355
|
const namedSignal = stringOrNull(signal);
|
|
250
356
|
return { status: namedSignal === null ? "exited" : "signaled", code: numberOrNull(code), signal: namedSignal };
|
|
@@ -263,23 +369,42 @@ export function bindEvents(target, events) {
|
|
|
263
369
|
observe: events.observe.bind(events)
|
|
264
370
|
});
|
|
265
371
|
}
|
|
266
|
-
export function program(record) {
|
|
267
|
-
|
|
372
|
+
export function program(record) {
|
|
373
|
+
const handle = handles.obtain(`program:${record.reference}`, () => new ProgramHandle(record));
|
|
374
|
+
handle.update(record);
|
|
375
|
+
return handle;
|
|
376
|
+
}
|
|
377
|
+
export function process(record, endpoints = {}) {
|
|
378
|
+
return handles.obtain(`process:${record.reference}`, () => new ProcessHandle(record, endpoints));
|
|
379
|
+
}
|
|
268
380
|
export function endpoint(reference) {
|
|
269
381
|
if (!reference)
|
|
270
382
|
throw new Error("The boundary returned an invalid Endpoint reference");
|
|
271
|
-
const owner =
|
|
383
|
+
const owner = process(reference.process);
|
|
272
384
|
return reference.kind === "server" ? owner.server : owner.client;
|
|
273
385
|
}
|
|
386
|
+
export function claimEndpoint(reference, kind, endpoint) {
|
|
387
|
+
return handles.adopt(`endpoint:${reference}:${kind}`, endpoint);
|
|
388
|
+
}
|
|
389
|
+
function endpointHandle(owner, kind, preferred) {
|
|
390
|
+
return handles.obtain(`endpoint:${owner.reference}:${kind}`, () => preferred ?? (kind === "server" ? new ServerHandle(owner) : new ClientHandle(owner)));
|
|
391
|
+
}
|
|
392
|
+
export function window(target) {
|
|
393
|
+
return new WindowHandle(target);
|
|
394
|
+
}
|
|
274
395
|
/** Resolves only endpoint identities intentionally visible to this Client. */
|
|
275
396
|
export function visibleEndpoint(reference) {
|
|
276
397
|
if (reference === null)
|
|
277
398
|
return null;
|
|
278
399
|
return endpoint(reference);
|
|
279
400
|
}
|
|
401
|
+
/** Runtime constructor shared by all client-visible Program handles. */
|
|
280
402
|
export const Program = CoreProgram;
|
|
403
|
+
/** Runtime constructor shared by all client-visible Process handles. */
|
|
281
404
|
export const Process = CoreProcess;
|
|
405
|
+
/** Runtime constructor shared by all client-visible Endpoint handles. */
|
|
282
406
|
export const Endpoint = CoreEndpoint;
|
|
407
|
+
/** Runtime constructor shared by all client-visible Server handles. */
|
|
283
408
|
export const Server = CoreServer;
|
|
409
|
+
/** Runtime constructor shared by all client-visible Client handles. */
|
|
284
410
|
export const Client = CoreClient;
|
|
285
|
-
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,25 +1,20 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
x: number;
|
|
5
|
-
y: number;
|
|
6
|
-
}>;
|
|
7
|
-
/** One desktop layer's available workspace. */
|
|
8
|
-
export type Surface = Readonly<{
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
11
|
-
gutter: number;
|
|
12
|
-
}>;
|
|
13
|
-
/** Session-local desktop events available to a Client endpoint. */
|
|
14
|
-
export type HostEvents = {
|
|
15
|
-
surface: Surface;
|
|
16
|
-
pointerMove: PointerPosition;
|
|
17
|
-
};
|
|
1
|
+
import type { Permissions, ServedFile, Theme, ThemeProperties } from "@phreshos/core";
|
|
2
|
+
import { type HostPointer } from "./pointer.js";
|
|
3
|
+
import { type HostSurface } from "./surface.js";
|
|
18
4
|
/** Desktop capabilities structurally available to a Client endpoint. */
|
|
19
|
-
export interface Host
|
|
20
|
-
|
|
21
|
-
|
|
5
|
+
export interface Host {
|
|
6
|
+
/** Read-only system Theme explicitly read from and observed through the desktop host. */
|
|
7
|
+
readonly theme: Theme<ThemeProperties>;
|
|
8
|
+
/** This Client Window's asynchronous surface read and live updates. */
|
|
9
|
+
readonly surface: HostSurface;
|
|
10
|
+
/** Permission-guarded desktop pointer reads and live movement. */
|
|
11
|
+
readonly pointer: HostPointer;
|
|
12
|
+
/** Permission decisions for capabilities guarded by the desktop. */
|
|
13
|
+
readonly permissions: Permissions;
|
|
14
|
+
/** Stores one value as a publicly reachable file. */
|
|
22
15
|
serve(value: unknown): Promise<ServedFile>;
|
|
16
|
+
/** Performs an unrestricted server-side fetch on behalf of this Client. */
|
|
23
17
|
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
24
18
|
}
|
|
19
|
+
/** Desktop capabilities available to this Client. */
|
|
25
20
|
export declare const host: Host;
|
package/dist/host.js
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { content } from "./content.js";
|
|
2
|
-
import
|
|
2
|
+
import ClientTheme from "./theme.js";
|
|
3
3
|
import wire from "./wire.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return answer[0];
|
|
13
|
-
}
|
|
14
|
-
async surface(layer) {
|
|
15
|
-
const answer = await wire.request(["surface", undefined, layer]);
|
|
16
|
-
return answer[0];
|
|
17
|
-
}
|
|
4
|
+
import ClientPermissions from "./permissions.js";
|
|
5
|
+
import ClientPointer, {} from "./pointer.js";
|
|
6
|
+
import ClientSurface, {} from "./surface.js";
|
|
7
|
+
class ClientHost {
|
|
8
|
+
theme = new ClientTheme();
|
|
9
|
+
surface = new ClientSurface();
|
|
10
|
+
pointer = new ClientPointer();
|
|
11
|
+
permissions = new ClientPermissions();
|
|
18
12
|
async serve(value) {
|
|
19
13
|
const source = content(value);
|
|
20
14
|
const channel = new MessageChannel();
|
|
@@ -85,22 +79,6 @@ class ClientHost extends Events {
|
|
|
85
79
|
});
|
|
86
80
|
return response;
|
|
87
81
|
}
|
|
88
|
-
withPointerSampling(sample, stop) {
|
|
89
|
-
if (!sample)
|
|
90
|
-
return stop;
|
|
91
|
-
if (this.pointerListeners++ === 0)
|
|
92
|
-
window.addEventListener("pointermove", this.samplePointer);
|
|
93
|
-
let active = true;
|
|
94
|
-
return () => {
|
|
95
|
-
if (!active)
|
|
96
|
-
return;
|
|
97
|
-
active = false;
|
|
98
|
-
stop();
|
|
99
|
-
this.pointerListeners--;
|
|
100
|
-
if (this.pointerListeners === 0)
|
|
101
|
-
window.removeEventListener("pointermove", this.samplePointer);
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
82
|
}
|
|
105
83
|
function requestHeaders(normalized, supplied) {
|
|
106
84
|
if (!supplied)
|
|
@@ -142,8 +120,5 @@ function closeControl(signal, port, abort) {
|
|
|
142
120
|
signal.removeEventListener("abort", abort);
|
|
143
121
|
port.close();
|
|
144
122
|
}
|
|
145
|
-
|
|
146
|
-
return () => { for (const cleanup of cleanups)
|
|
147
|
-
cleanup(); };
|
|
148
|
-
}
|
|
123
|
+
/** Desktop capabilities available to this Client. */
|
|
149
124
|
export const host = new ClientHost();
|
package/dist/main.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
export { host, type Host
|
|
1
|
+
export { host, type Host } from "./host.js";
|
|
2
|
+
export { type HostPointer, type PointerEvents, type PointerPosition } from "./pointer.js";
|
|
3
|
+
export { type HostSurface, type Surface, type SurfaceEvents } from "./surface.js";
|
|
2
4
|
export { current, type Current, type CurrentServer } from "./current.js";
|
|
3
5
|
export { type Channel, type ChannelCapture, type ChannelEvents, type ChannelMessage } from "./channel.js";
|
|
4
|
-
export { Client, Endpoint, Process, Program, Server, Window, type AnswerCapture, type AnswerMessage, type AnswerObserver, type AskCapture, type AskMessage, type AskObserver, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
|
|
5
|
-
export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, Position, ProgramArea, ProgramEvents, ProgramProcessExit,
|
|
6
|
+
export { Client, Endpoint, Process, Program, Server, type Window, type AnswerCapture, type AnswerMessage, type AnswerObserver, type AskCapture, type AskMessage, type AskObserver, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
|
|
7
|
+
export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permissions, Position, ProgramArea, ProgramEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermissions, Timeoutable, Theme, ThemeEvents, ThemeProperties, Value, WritableTheme, WindowEvents, WindowLayer, WindowState, WindowSurface, WindowSurfaceEasing, WindowSurfaceEvents, WindowSurfaceSettings, WindowSurfaceTransaction } from "@phreshos/core";
|
package/dist/main.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { host } from "./host.js";
|
|
2
|
+
export {} from "./pointer.js";
|
|
3
|
+
export {} from "./surface.js";
|
|
2
4
|
export { current } from "./current.js";
|
|
3
5
|
export {} from "./channel.js";
|
|
4
|
-
export { Client, Endpoint, Process, Program, Server
|
|
6
|
+
export { Client, Endpoint, Process, Program, Server } from "./domain.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PermissionDecision, PermissionName, Permissions, TimedPermissions } from "@phreshos/core";
|
|
2
|
+
/** Client permission access bound to the current Process boundary. */
|
|
3
|
+
export default class ClientPermissions implements Permissions {
|
|
4
|
+
granted(name: PermissionName): Promise<PermissionDecision>;
|
|
5
|
+
request(name: PermissionName): Promise<PermissionDecision>;
|
|
6
|
+
timeout(milliseconds: number): TimedPermissions;
|
|
7
|
+
private requestWithin;
|
|
8
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import wire from "./wire.js";
|
|
2
|
+
const defaultPermissionTimeout = 30_000;
|
|
3
|
+
/** Client permission access bound to the current Process boundary. */
|
|
4
|
+
export default class ClientPermissions {
|
|
5
|
+
async granted(name) {
|
|
6
|
+
const answer = await wire.request(["permission-granted", name]);
|
|
7
|
+
return answer[0];
|
|
8
|
+
}
|
|
9
|
+
request(name) {
|
|
10
|
+
return this.requestWithin(name, defaultPermissionTimeout);
|
|
11
|
+
}
|
|
12
|
+
timeout(milliseconds) {
|
|
13
|
+
if (!Number.isFinite(milliseconds) || milliseconds < 0)
|
|
14
|
+
throw new Error("A permission timeout must be a non-negative finite number");
|
|
15
|
+
return Object.freeze({ request: (name) => this.requestWithin(name, milliseconds) });
|
|
16
|
+
}
|
|
17
|
+
async requestWithin(name, milliseconds) {
|
|
18
|
+
const answer = await wire.requestOrNull(["permission-request", name], milliseconds);
|
|
19
|
+
return answer?.[0] ?? null;
|
|
20
|
+
}
|
|
21
|
+
}
|