@phreshos/client 0.1.50 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client-memory.d.ts +3 -0
- package/dist/client-memory.js +57 -0
- package/dist/context.js +5 -2
- package/dist/domain.js +8 -4
- package/dist/permission-requests.d.ts +2 -0
- package/dist/permission-requests.js +84 -0
- package/dist/permissions.d.ts +3 -2
- package/dist/permissions.js +17 -6
- package/dist/storage.d.ts +5 -1
- package/dist/storage.js +34 -1
- package/dist/system.js +4 -1
- package/package.json +3 -3
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import wire from "./wire.js";
|
|
2
|
+
/** The System, not this browser document, owns the values and ordering. */
|
|
3
|
+
export default function clientMemory(target) {
|
|
4
|
+
const snapshots = new Map();
|
|
5
|
+
const address = () => target ? Promise.resolve(target) : wire.identity().then(({ process, reference }) => ({ identity: process, reference }));
|
|
6
|
+
const ask = async (operation, key, value, expected) => {
|
|
7
|
+
const [result] = await wire.request(["client-memory", await address(), operation, key, value, expected]);
|
|
8
|
+
return result;
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
async get(key) {
|
|
12
|
+
const snapshot = await ask("snapshot", key);
|
|
13
|
+
snapshots.set(key, snapshot);
|
|
14
|
+
return snapshot.value;
|
|
15
|
+
},
|
|
16
|
+
async set(key, value) { snapshots.set(key, await ask("set", key, value)); },
|
|
17
|
+
async update(key, updater) {
|
|
18
|
+
let snapshot = snapshots.get(key) ?? await ask("snapshot", key);
|
|
19
|
+
for (;;) {
|
|
20
|
+
const next = updater(snapshot.value);
|
|
21
|
+
const result = await ask("compareAndSet", key, next, snapshot);
|
|
22
|
+
snapshots.set(key, result.snapshot);
|
|
23
|
+
if (result.changed)
|
|
24
|
+
return result.snapshot.value;
|
|
25
|
+
if (result.snapshot.run !== snapshot.run)
|
|
26
|
+
throw new Error("The Client run changed during the update");
|
|
27
|
+
snapshot = result.snapshot;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async delete(key) { const deleted = await ask("delete", key); snapshots.delete(key); return deleted; },
|
|
31
|
+
async entries() { return await ask("entries"); },
|
|
32
|
+
subscribe(key, subscriber) {
|
|
33
|
+
let active = true;
|
|
34
|
+
let stop;
|
|
35
|
+
let latest;
|
|
36
|
+
void address().then(value => {
|
|
37
|
+
if (!active)
|
|
38
|
+
return;
|
|
39
|
+
// Register first; an older snapshot response cannot overtake a newer event.
|
|
40
|
+
stop = wire.on("host-client-memory", key, (identity, snapshot) => {
|
|
41
|
+
if (identity !== value.identity || !active)
|
|
42
|
+
return;
|
|
43
|
+
deliver(snapshot);
|
|
44
|
+
}, value.identity);
|
|
45
|
+
void ask("snapshot", key).then(snapshot => deliver(snapshot)).catch(() => undefined);
|
|
46
|
+
}).catch(() => undefined);
|
|
47
|
+
const deliver = (snapshot) => {
|
|
48
|
+
if (!active || (latest && (latest.run !== snapshot.run || latest.revision > snapshot.revision)))
|
|
49
|
+
return;
|
|
50
|
+
latest = snapshot;
|
|
51
|
+
snapshots.set(key, snapshot);
|
|
52
|
+
subscriber(snapshot.value);
|
|
53
|
+
};
|
|
54
|
+
return () => { active = false; stop?.(); };
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
package/dist/context.js
CHANGED
|
@@ -2,7 +2,8 @@ import Deadline from "./deadline.js";
|
|
|
2
2
|
import { ClientEndpoint, ServerEndpoint, ServerTrafficHandle, TrafficHandle, endpointLifecycle, endpointEvents, process, program, visibleEndpoint, presentation, window as windowHandle, } from "./domain.js";
|
|
3
3
|
import Events from "./events.js";
|
|
4
4
|
import wire from "./wire.js";
|
|
5
|
-
import
|
|
5
|
+
import clientMemory from "./client-memory.js";
|
|
6
|
+
import { contextPermissions } from "./permissions.js";
|
|
6
7
|
class ContextServerHandle extends ServerEndpoint {
|
|
7
8
|
owner;
|
|
8
9
|
subscribe;
|
|
@@ -68,6 +69,7 @@ class ContextClientHandle extends ClientEndpoint {
|
|
|
68
69
|
traffic = new TrafficHandle(null, "client");
|
|
69
70
|
lifecycle = endpointLifecycle(currentAddress, "client");
|
|
70
71
|
window = windowHandle(currentAddress);
|
|
72
|
+
memory = clientMemory(null);
|
|
71
73
|
constructor(owner) {
|
|
72
74
|
super();
|
|
73
75
|
this.owner = owner;
|
|
@@ -95,8 +97,9 @@ contextClient = new ContextClientHandle(owner);
|
|
|
95
97
|
class ClientContext extends Events {
|
|
96
98
|
server = contextServer;
|
|
97
99
|
window = contextClient.window;
|
|
100
|
+
memory = contextClient.memory;
|
|
98
101
|
presentation = presentation(currentAddress);
|
|
99
|
-
permissions =
|
|
102
|
+
permissions = contextPermissions();
|
|
100
103
|
constructor() {
|
|
101
104
|
super((event, listener, impossible) => wire.on("end-end", event, value => listener(contextMessage(value)), null, impossible), (listener, impossible) => wire.onAll("end-end", (event, value) => {
|
|
102
105
|
if (typeof event === "string")
|
package/dist/domain.js
CHANGED
|
@@ -2,10 +2,11 @@ import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process
|
|
|
2
2
|
import Events, { stream } from "./events.js";
|
|
3
3
|
import Deadline from "./deadline.js";
|
|
4
4
|
import HandleRegistry from "./handle-registry.js";
|
|
5
|
-
import { area, sql, store } from "./storage.js";
|
|
5
|
+
import { area, programLogs, sql, store } from "./storage.js";
|
|
6
6
|
import startup from "./startup.js";
|
|
7
7
|
import { programPermissions } from "./permissions.js";
|
|
8
8
|
import wire from "./wire.js";
|
|
9
|
+
import clientMemory from "./client-memory.js";
|
|
9
10
|
const handles = new HandleRegistry();
|
|
10
11
|
class ProgramHandle extends CoreProgram {
|
|
11
12
|
subscribe;
|
|
@@ -29,7 +30,7 @@ class ProgramHandle extends CoreProgram {
|
|
|
29
30
|
this.data = area(this.address, "data");
|
|
30
31
|
this.cache = area(this.address, "cache");
|
|
31
32
|
this.store = store(this.address);
|
|
32
|
-
this.logs =
|
|
33
|
+
this.logs = programLogs(this.address);
|
|
33
34
|
this.database = sql("database", this.address);
|
|
34
35
|
this.startup = startup(this.address);
|
|
35
36
|
this.permissions = programPermissions(this.address);
|
|
@@ -79,10 +80,10 @@ class ProgramHandle extends CoreProgram {
|
|
|
79
80
|
const answer = await wire.request(["program-processes", this.address]);
|
|
80
81
|
return answer[0].map(record => process(record));
|
|
81
82
|
}
|
|
82
|
-
async
|
|
83
|
+
async oldestProcess() {
|
|
83
84
|
return chronological(await this.processes())[0] ?? null;
|
|
84
85
|
}
|
|
85
|
-
async
|
|
86
|
+
async newestProcess() {
|
|
86
87
|
return chronological(await this.processes()).at(-1) ?? null;
|
|
87
88
|
}
|
|
88
89
|
async findProcess(identityOrName) {
|
|
@@ -285,12 +286,14 @@ class ClientEndpointHandle extends CoreClientEndpoint {
|
|
|
285
286
|
traffic;
|
|
286
287
|
lifecycle;
|
|
287
288
|
window;
|
|
289
|
+
memory;
|
|
288
290
|
constructor(owner) {
|
|
289
291
|
super();
|
|
290
292
|
this.owner = owner;
|
|
291
293
|
this.traffic = new TrafficHandle(owner.address, "client");
|
|
292
294
|
this.lifecycle = endpointLifecycle(() => Promise.resolve(owner.address), "client");
|
|
293
295
|
this.window = window(async () => owner.address);
|
|
296
|
+
this.memory = clientMemory(owner.address);
|
|
294
297
|
const events = endpointEvents(owner.address, "client");
|
|
295
298
|
this.subscribe = events.subscribe;
|
|
296
299
|
this.wait = events.wait;
|
|
@@ -360,6 +363,7 @@ class WindowPresentationHandle {
|
|
|
360
363
|
async resize(size) { await this.change("windowPresentationResize", size); }
|
|
361
364
|
async setGeometry(geometry) { await this.change("windowPresentationGeometry", geometry); }
|
|
362
365
|
async setSurface(surface) { await this.change("windowPresentationSurface", surface); }
|
|
366
|
+
async setInteractive(interactive) { await this.change("windowPresentationInteractive", interactive); }
|
|
363
367
|
async raise() { await this.change("windowPresentationRaise"); }
|
|
364
368
|
async change(operation, value) {
|
|
365
369
|
await wire.request([operation, await this.target(), value, this.selected ?? null]);
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { PermissionRequest as CorePermissionRequest, parsePermission, parsePermissionRequestSnapshot } from "@phreshos/core";
|
|
2
|
+
import { endpoint } from "./domain.js";
|
|
3
|
+
import Events from "./events.js";
|
|
4
|
+
import wire from "./wire.js";
|
|
5
|
+
const handles = new Map();
|
|
6
|
+
class PermissionRequestHandle extends CorePermissionRequest {
|
|
7
|
+
subscribe;
|
|
8
|
+
wait;
|
|
9
|
+
events;
|
|
10
|
+
identity;
|
|
11
|
+
from;
|
|
12
|
+
createdAt;
|
|
13
|
+
expiresAt;
|
|
14
|
+
name;
|
|
15
|
+
scope;
|
|
16
|
+
constructor(snapshot) {
|
|
17
|
+
super();
|
|
18
|
+
this.identity = snapshot.identity;
|
|
19
|
+
this.from = endpoint(snapshot.from);
|
|
20
|
+
this.createdAt = snapshot.createdAt;
|
|
21
|
+
this.expiresAt = snapshot.expiresAt;
|
|
22
|
+
this.name = snapshot.name;
|
|
23
|
+
this.scope = snapshot.scope;
|
|
24
|
+
const events = new Events((event, listener, impossible) => wire.on("permission-host", event, (...values) => {
|
|
25
|
+
listener(parsePermission(this.name, values[2]));
|
|
26
|
+
}, this.identity, impossible), (listener, impossible) => wire.onAll("permission-host", (event, ...values) => {
|
|
27
|
+
if (event === "resolve")
|
|
28
|
+
listener(event, parsePermission(this.name, values[2]));
|
|
29
|
+
}, this.identity, impossible));
|
|
30
|
+
this.subscribe = events.subscribe;
|
|
31
|
+
this.wait = events.wait;
|
|
32
|
+
this.events = events.events;
|
|
33
|
+
}
|
|
34
|
+
async pending() {
|
|
35
|
+
const answer = await wire.request(["permission-request-pending", this.identity]);
|
|
36
|
+
return answer[0] === true;
|
|
37
|
+
}
|
|
38
|
+
async allow() { await wire.request(["permission-request-allow", this.identity]); }
|
|
39
|
+
async deny() { await wire.request(["permission-request-deny", this.identity]); }
|
|
40
|
+
async cancel() { await wire.request(["permission-request-cancel", this.identity]); }
|
|
41
|
+
}
|
|
42
|
+
function requestHandle(value) {
|
|
43
|
+
const snapshot = parsePermissionRequestSnapshot(value);
|
|
44
|
+
const current = handles.get(snapshot.identity);
|
|
45
|
+
if (current)
|
|
46
|
+
return current;
|
|
47
|
+
const created = new PermissionRequestHandle(snapshot);
|
|
48
|
+
handles.set(snapshot.identity, created);
|
|
49
|
+
return created;
|
|
50
|
+
}
|
|
51
|
+
class PermissionRegistry extends Events {
|
|
52
|
+
constructor() {
|
|
53
|
+
super((event, listener, impossible) => wire.on("host-permission", permissionEvent(event), (...values) => {
|
|
54
|
+
listener(systemPermissionEvent(event, values));
|
|
55
|
+
}, null, impossible), (listener, impossible) => wire.onAll("host-permission", (event, ...values) => {
|
|
56
|
+
if (typeof event !== "string")
|
|
57
|
+
return;
|
|
58
|
+
const name = publicPermissionEvent(event);
|
|
59
|
+
if (name)
|
|
60
|
+
listener(name, systemPermissionEvent(name, values));
|
|
61
|
+
}, null, impossible));
|
|
62
|
+
}
|
|
63
|
+
async requests() {
|
|
64
|
+
const [values] = await wire.request(["host-permission-requests"]);
|
|
65
|
+
return values.map(requestHandle);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function permissionEvent(event) {
|
|
69
|
+
return event === "permissionRequest" ? "request" : "resolve";
|
|
70
|
+
}
|
|
71
|
+
function publicPermissionEvent(event) {
|
|
72
|
+
if (event === "request")
|
|
73
|
+
return "permissionRequest";
|
|
74
|
+
if (event === "resolve")
|
|
75
|
+
return "permissionResolve";
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
function systemPermissionEvent(event, values) {
|
|
79
|
+
const request = requestHandle(values[1]);
|
|
80
|
+
if (event === "permissionRequest")
|
|
81
|
+
return request;
|
|
82
|
+
return { request, permission: parsePermission(request.name, values[2]) };
|
|
83
|
+
}
|
|
84
|
+
export const systemPermissions = new PermissionRegistry();
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { ProgramPermissions } from "@phreshos/core";
|
|
1
|
+
import type { ContextPermissions, ProgramPermissions } from "@phreshos/core";
|
|
2
2
|
import type { HandleAddress } from "./domain.js";
|
|
3
|
-
export declare const defaultPermissionTimeout = 120000;
|
|
4
3
|
/** Bind authoritative permission state to one exact Program handle. */
|
|
5
4
|
export declare function programPermissions(program?: HandleAddress): ProgramPermissions;
|
|
5
|
+
/** Bind permission reads and requests to the currently executing Client Endpoint. */
|
|
6
|
+
export declare function contextPermissions(): ContextPermissions;
|
package/dist/permissions.js
CHANGED
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
import { parsePermission, parsePermissions } from "@phreshos/core";
|
|
2
2
|
import wire from "./wire.js";
|
|
3
|
-
export const defaultPermissionTimeout = 120_000;
|
|
4
3
|
/** Bind authoritative permission state to one exact Program handle. */
|
|
5
4
|
export function programPermissions(program) {
|
|
6
5
|
const operate = (operation, name, permission) => (wire.request(["program-permissions", program, operation, name, permission]));
|
|
6
|
+
return {
|
|
7
|
+
async get(name) { return parsePermission(name, (await operate("get", name))[0]); },
|
|
8
|
+
async all() { return parsePermissions((await operate("all"))[0]); },
|
|
9
|
+
async allows(name, permission = true) { return (await operate("allows", name, permission))[0] === true; },
|
|
10
|
+
async allow(name, permission = true) { await operate("allow", name, permission); },
|
|
11
|
+
async deny(name) { await operate("deny", name); }
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** Bind permission reads and requests to the currently executing Client Endpoint. */
|
|
15
|
+
export function contextPermissions() {
|
|
16
|
+
const operate = (operation, name, permission) => (wire.request(["program-permissions", undefined, operation, name, permission]));
|
|
7
17
|
const timed = (timeout) => ({
|
|
8
18
|
async request(name, permission = true) {
|
|
9
19
|
const identity = crypto.randomUUID();
|
|
10
|
-
const
|
|
11
|
-
|
|
20
|
+
const request = ["context-permission-request", identity, name, permission];
|
|
21
|
+
if (timeout !== undefined)
|
|
22
|
+
request.push(timeout);
|
|
23
|
+
const result = await wire.request(request);
|
|
24
|
+
return parsePermission(name, result[0]);
|
|
12
25
|
}
|
|
13
26
|
});
|
|
14
27
|
return {
|
|
15
28
|
async get(name) { return parsePermission(name, (await operate("get", name))[0]); },
|
|
16
29
|
async all() { return parsePermissions((await operate("all"))[0]); },
|
|
17
30
|
async allows(name, permission = true) { return (await operate("allows", name, permission))[0] === true; },
|
|
18
|
-
|
|
19
|
-
async deny(name) { await operate("deny", name); },
|
|
20
|
-
request: timed(defaultPermissionTimeout).request,
|
|
31
|
+
request: timed().request,
|
|
21
32
|
timeout(milliseconds) {
|
|
22
33
|
if (!Number.isFinite(milliseconds) || milliseconds < 0)
|
|
23
34
|
throw new Error("A permission timeout must be a non-negative finite number");
|
package/dist/storage.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Storage, type ProgramSql, type ProgramStore } from "@phreshos/core";
|
|
1
|
+
import { Storage, type ProgramLogs, type ProgramSql, type ProgramStore, type SystemLogs } from "@phreshos/core";
|
|
2
2
|
import type { HandleAddress } from "./domain.js";
|
|
3
3
|
/** Client-side access to one exact Program storage area. */
|
|
4
4
|
export declare function area(program: HandleAddress, which: "data" | "cache"): Storage;
|
|
@@ -6,3 +6,7 @@ export declare function area(program: HandleAddress, which: "data" | "cache"): S
|
|
|
6
6
|
export declare function systemStorage(): Storage;
|
|
7
7
|
export declare function store(program: HandleAddress): ProgramStore;
|
|
8
8
|
export declare function sql(kind: "database" | "logs", program: HandleAddress): ProgramSql;
|
|
9
|
+
/** Read and observe one Program's captured Endpoint output. */
|
|
10
|
+
export declare function programLogs(program: HandleAddress): ProgramLogs;
|
|
11
|
+
/** Read and observe records produced by the PhreshOS System. */
|
|
12
|
+
export declare function systemLogs(): SystemLogs;
|
package/dist/storage.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Storage, StorageFile } from "@phreshos/core";
|
|
1
|
+
import { Storage, StorageFile, parseProgramLogRecord, parseSystemLogRecord } from "@phreshos/core";
|
|
2
2
|
import { content } from "./content.js";
|
|
3
3
|
import controlledStream from "./controlled-stream.js";
|
|
4
4
|
import wire from "./wire.js";
|
|
5
|
+
import Events from "./events.js";
|
|
5
6
|
/** Client-side access to one exact Program storage area. */
|
|
6
7
|
export function area(program, which) {
|
|
7
8
|
return new RemoteStorage(new ProgramStorageBoundary(program, which), []);
|
|
@@ -221,3 +222,35 @@ export function sql(kind, program) {
|
|
|
221
222
|
}
|
|
222
223
|
};
|
|
223
224
|
}
|
|
225
|
+
/** Read and observe one Program's captured Endpoint output. */
|
|
226
|
+
export function programLogs(program) {
|
|
227
|
+
const database = sql("logs", program);
|
|
228
|
+
const events = new Events((event, listener, impossible) => wire.on("program-log", event, (...values) => listener(parseProgramLogRecord(values[1])), program.reference, impossible), (listener, impossible) => wire.onAll("program-log", (event, ...values) => {
|
|
229
|
+
if (event === "log")
|
|
230
|
+
listener(event, parseProgramLogRecord(values[1]));
|
|
231
|
+
}, program.reference, impossible));
|
|
232
|
+
return {
|
|
233
|
+
query: database.query,
|
|
234
|
+
subscribe: events.subscribe,
|
|
235
|
+
wait: events.wait,
|
|
236
|
+
events: events.events
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/** Read and observe records produced by the PhreshOS System. */
|
|
240
|
+
export function systemLogs() {
|
|
241
|
+
const events = new Events((event, listener, impossible) => wire.on("host-log", event, (...values) => listener(parseSystemLogRecord(values[1])), null, impossible), (listener, impossible) => wire.onAll("host-log", (event, ...values) => {
|
|
242
|
+
if (event === "log")
|
|
243
|
+
listener(event, parseSystemLogRecord(values[1]));
|
|
244
|
+
}, null, impossible));
|
|
245
|
+
return {
|
|
246
|
+
async query(statement, ...rest) {
|
|
247
|
+
const text = typeof statement === "string" ? statement : statement.raw.join("?");
|
|
248
|
+
const values = typeof statement === "string" ? (Array.isArray(rest[0]) ? rest[0] : []) : rest;
|
|
249
|
+
const answer = await wire.request(["system-logs", text, values]);
|
|
250
|
+
return answer[0];
|
|
251
|
+
},
|
|
252
|
+
subscribe: events.subscribe,
|
|
253
|
+
wait: events.wait,
|
|
254
|
+
events: events.events
|
|
255
|
+
};
|
|
256
|
+
}
|
package/dist/system.js
CHANGED
|
@@ -5,15 +5,18 @@ import { prepareService } from "./service.js";
|
|
|
5
5
|
import { uploads } from "./uploads.js";
|
|
6
6
|
import Events from "./events.js";
|
|
7
7
|
import { exit, process, program } from "./domain.js";
|
|
8
|
-
import { systemStorage } from "./storage.js";
|
|
8
|
+
import { systemLogs, systemStorage } from "./storage.js";
|
|
9
9
|
import network from "./network.js";
|
|
10
10
|
import { connection, session } from "./authentication.js";
|
|
11
|
+
import { systemPermissions } from "./permission-requests.js";
|
|
11
12
|
class ClientSystem {
|
|
12
13
|
storage = systemStorage();
|
|
13
14
|
appearance = new ClientAppearance();
|
|
14
15
|
program = new SystemProgramHandle();
|
|
15
16
|
process = new SystemProcessHandle();
|
|
16
17
|
authentication = new SystemAuthenticationHandle();
|
|
18
|
+
permissions = systemPermissions;
|
|
19
|
+
logs = systemLogs();
|
|
17
20
|
service = new SystemServiceHandle();
|
|
18
21
|
uploads = uploads;
|
|
19
22
|
network = network;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.52",
|
|
4
4
|
"description": "The SDK used by a Program's client endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"test": "vitest run --project default"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
|
|
49
|
+
"@phreshos/core": "^0.1.62"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@the-link/messagepack": "^0.1.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@phreshos/core": "^0.1.
|
|
55
|
+
"@phreshos/core": "^0.1.62",
|
|
56
56
|
"typescript": "^6.0.3",
|
|
57
57
|
"vitest": "^4.1.10"
|
|
58
58
|
}
|