@phreshos/client 0.1.38 → 0.1.40
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 +6 -0
- package/dist/appearance.d.ts +8 -7
- package/dist/appearance.js +4 -4
- package/dist/context.js +25 -13
- package/dist/desktop/desktop.js +2 -2
- package/dist/desktop/viewport.d.ts +9 -0
- package/dist/desktop/{surface.js → viewport.js} +6 -6
- package/dist/domain.d.ts +21 -61
- package/dist/domain.js +66 -64
- package/dist/events.d.ts +3 -2
- package/dist/events.js +2 -1
- package/dist/launch.d.ts +4 -0
- package/dist/launch.js +14 -0
- package/dist/network.d.ts +7 -0
- package/dist/network.js +79 -0
- package/dist/permissions.d.ts +2 -2
- package/dist/permissions.js +2 -2
- package/dist/service.js +26 -21
- package/dist/system.js +2 -75
- package/package.json +8 -8
- package/dist/desktop/surface.d.ts +0 -9
package/README.md
CHANGED
|
@@ -49,6 +49,12 @@ bun run verify
|
|
|
49
49
|
`verify` checks the types and completions, builds the package, and validates its
|
|
50
50
|
published shape.
|
|
51
51
|
|
|
52
|
+
`check` performs static checks, `build` creates distributable output, and `test`
|
|
53
|
+
runs Vitest assertions from `tests/`. Run `build` before testing built artifacts.
|
|
54
|
+
`verify` runs `check`, `build`, and `test` in order. Operational tooling belongs
|
|
55
|
+
in `scripts/`; tests and their fixtures belong in `tests/`. Verification uses
|
|
56
|
+
the committed dependency graph without local package substitutions.
|
|
57
|
+
|
|
52
58
|
## Related repositories
|
|
53
59
|
|
|
54
60
|
- [`@phreshos/core`](https://github.com/PhreshOS/core) is the one public import
|
package/dist/appearance.d.ts
CHANGED
|
@@ -4,13 +4,14 @@ import Events from "./events.js";
|
|
|
4
4
|
export default class ClientAppearance extends Events<AppearanceEvents, never> implements WritableAppearance {
|
|
5
5
|
constructor();
|
|
6
6
|
snapshot(): Promise<Readonly<{
|
|
7
|
-
colors: import("@phreshos/core").AppearanceColors
|
|
8
|
-
spacing:
|
|
9
|
-
radius:
|
|
10
|
-
shadow: import("@phreshos/core").ThemedValue<import("@phreshos/core").AppearanceShadow
|
|
11
|
-
material: import("@phreshos/core").ThemedValue<import("@phreshos/core").AppearanceMaterial
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
colors: import("@phreshos/core").ThemedValue<import("@phreshos/core").AppearanceColors>;
|
|
8
|
+
spacing: number;
|
|
9
|
+
radius: number;
|
|
10
|
+
shadow: import("@phreshos/core").ThemedValue<import("@phreshos/core").AppearanceShadow>;
|
|
11
|
+
material: import("@phreshos/core").ThemedValue<import("@phreshos/core").AppearanceMaterial>;
|
|
12
|
+
transaction: import("@phreshos/core").AppearanceTransaction;
|
|
13
|
+
signInWallpaper: import("@phreshos/core").ThemedValue<string | null>;
|
|
14
|
+
desktopWallpaper: import("@phreshos/core").ThemedValue<string | null>;
|
|
14
15
|
}>>;
|
|
15
16
|
update(appearance: Appearance): Promise<void>;
|
|
16
17
|
}
|
package/dist/appearance.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseAppearance } from "@phreshos/core";
|
|
2
2
|
import Events from "./events.js";
|
|
3
3
|
import wire from "./wire.js";
|
|
4
4
|
/** Read-only System Appearance reached through the Desktop boundary. */
|
|
5
5
|
export default class ClientAppearance extends Events {
|
|
6
6
|
constructor() {
|
|
7
7
|
super((event, listener, impossible) => wire.on("host-appearance", event, value => {
|
|
8
|
-
listener(
|
|
8
|
+
listener(parseAppearance(value));
|
|
9
9
|
}, null, impossible), observer => wire.onAll("host-appearance", (event, value) => {
|
|
10
10
|
if (typeof event === "string")
|
|
11
|
-
observer(event,
|
|
11
|
+
observer(event, parseAppearance(value));
|
|
12
12
|
}));
|
|
13
13
|
}
|
|
14
14
|
async snapshot() {
|
|
15
15
|
const [appearance] = await wire.request(["appearance"]);
|
|
16
|
-
return
|
|
16
|
+
return parseAppearance(appearance);
|
|
17
17
|
}
|
|
18
18
|
async update(appearance) {
|
|
19
19
|
await wire.request(["updateAppearance", appearance]);
|
package/dist/context.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import Deadline from "./deadline.js";
|
|
2
|
-
import { ClientEndpoint, ServerEndpoint, ServerTrafficHandle, TrafficHandle,
|
|
2
|
+
import { ClientEndpoint, ServerEndpoint, ServerTrafficHandle, TrafficHandle, endpointLifecycle, endpointEvents, process, program, visibleEndpoint, localWindow, window as windowHandle, } from "./domain.js";
|
|
3
3
|
import Events from "./events.js";
|
|
4
4
|
import wire from "./wire.js";
|
|
5
5
|
import { contextPermissions } from "./permissions.js";
|
|
6
|
-
|
|
7
|
-
const ClientEndpointBase = ClientEndpoint;
|
|
8
|
-
class ContextServerHandle extends ServerEndpointBase {
|
|
6
|
+
class ContextServerHandle extends ServerEndpoint {
|
|
9
7
|
owner;
|
|
8
|
+
subscribe;
|
|
9
|
+
wait;
|
|
10
|
+
events;
|
|
10
11
|
traffic = new ServerTrafficHandle(null, "server");
|
|
11
12
|
lifecycle = endpointLifecycle(currentAddress, "server");
|
|
12
13
|
constructor(owner) {
|
|
13
14
|
super();
|
|
14
15
|
this.owner = owner;
|
|
15
|
-
|
|
16
|
+
const events = endpointEvents(null, "server");
|
|
17
|
+
this.subscribe = events.subscribe;
|
|
18
|
+
this.wait = events.wait;
|
|
19
|
+
this.events = events.events;
|
|
16
20
|
}
|
|
17
21
|
process() { return this.owner(); }
|
|
18
|
-
publish(event, payload = undefined) {
|
|
22
|
+
publish = (event, payload = undefined) => {
|
|
23
|
+
wire.send("end-end", event, payload);
|
|
24
|
+
};
|
|
19
25
|
async exists() {
|
|
20
26
|
const answer = await wire.request(["exists", "server"]);
|
|
21
27
|
return answer[0];
|
|
@@ -54,22 +60,28 @@ function owner() {
|
|
|
54
60
|
return ownerPromise;
|
|
55
61
|
}
|
|
56
62
|
contextServer = new ContextServerHandle(owner);
|
|
57
|
-
class ContextClientHandle extends
|
|
63
|
+
class ContextClientHandle extends ClientEndpoint {
|
|
58
64
|
owner;
|
|
65
|
+
subscribe;
|
|
66
|
+
wait;
|
|
67
|
+
events;
|
|
59
68
|
traffic = new TrafficHandle(null, "client");
|
|
60
69
|
lifecycle = endpointLifecycle(currentAddress, "client");
|
|
61
70
|
window = windowHandle(currentAddress);
|
|
62
71
|
constructor(owner) {
|
|
63
72
|
super();
|
|
64
73
|
this.owner = owner;
|
|
65
|
-
|
|
74
|
+
const events = endpointEvents(null, "client");
|
|
75
|
+
this.subscribe = events.subscribe;
|
|
76
|
+
this.wait = events.wait;
|
|
77
|
+
this.events = events.events;
|
|
66
78
|
}
|
|
67
79
|
process() { return this.owner(); }
|
|
68
|
-
publish(event, payload = undefined) {
|
|
80
|
+
publish = (event, payload = undefined) => {
|
|
69
81
|
void wire.identity().then(identity => {
|
|
70
82
|
wire.send("end-host", "send", { identity: identity.process, reference: identity.reference }, "client", event, payload);
|
|
71
83
|
});
|
|
72
|
-
}
|
|
84
|
+
};
|
|
73
85
|
async exists() {
|
|
74
86
|
const answer = await wire.request(["exists", "client"]);
|
|
75
87
|
return answer[0];
|
|
@@ -101,9 +113,9 @@ class ClientContext extends Events {
|
|
|
101
113
|
const answer = await wire.request(["current-program"]);
|
|
102
114
|
return program(answer[0]);
|
|
103
115
|
}
|
|
104
|
-
async
|
|
105
|
-
const
|
|
106
|
-
return
|
|
116
|
+
async options(name) {
|
|
117
|
+
const process = await owner();
|
|
118
|
+
return name === undefined ? process.options() : process.options(name);
|
|
107
119
|
}
|
|
108
120
|
async stop() { await wire.request(["stop-current"]); }
|
|
109
121
|
async isService() { return contextClient.isService(); }
|
package/dist/desktop/desktop.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import ClientPreferences from "./preferences.js";
|
|
2
|
-
import
|
|
2
|
+
import ClientViewport from "./viewport.js";
|
|
3
3
|
/** Desktop access bound to the current Client Endpoint's Process boundary. */
|
|
4
4
|
class ClientDesktop {
|
|
5
|
-
|
|
5
|
+
viewport = new ClientViewport();
|
|
6
6
|
preferences = new ClientPreferences();
|
|
7
7
|
}
|
|
8
8
|
/** The Desktop environment containing this Client Endpoint. */
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DesktopViewportEvents, DesktopViewportSource } from "@phreshos/core";
|
|
2
|
+
import Events from "../events.js";
|
|
3
|
+
/** Read-only access to the Desktop viewport containing this Client Endpoint. */
|
|
4
|
+
export default class ClientViewport extends Events<DesktopViewportEvents, never> implements DesktopViewportSource {
|
|
5
|
+
constructor();
|
|
6
|
+
snapshot(): Promise<Readonly<{
|
|
7
|
+
size: import("@phreshos/core").DesktopSize;
|
|
8
|
+
}>>;
|
|
9
|
+
}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import Events from "../events.js";
|
|
2
2
|
import wire from "../wire.js";
|
|
3
|
-
/** Read-only access to the Desktop
|
|
4
|
-
export default class
|
|
3
|
+
/** Read-only access to the Desktop viewport containing this Client Endpoint. */
|
|
4
|
+
export default class ClientViewport extends Events {
|
|
5
5
|
constructor() {
|
|
6
|
-
super((event, listener, impossible) => wire.on("host-desktop-
|
|
6
|
+
super((event, listener, impossible) => wire.on("host-desktop-viewport", event, value => {
|
|
7
7
|
const snapshot = createSnapshot(value);
|
|
8
8
|
if (snapshot)
|
|
9
9
|
listener(snapshot);
|
|
10
|
-
}, null, impossible), observer => wire.onAll("host-desktop-
|
|
10
|
+
}, null, impossible), observer => wire.onAll("host-desktop-viewport", (event, value) => {
|
|
11
11
|
const snapshot = createSnapshot(value);
|
|
12
12
|
if (typeof event === "string" && snapshot)
|
|
13
13
|
observer(event, snapshot);
|
|
14
14
|
}));
|
|
15
15
|
}
|
|
16
16
|
async snapshot() {
|
|
17
|
-
const answer = await wire.request(["
|
|
17
|
+
const answer = await wire.request(["desktopViewport"]);
|
|
18
18
|
const snapshot = createSnapshot(answer[0]);
|
|
19
19
|
if (!snapshot)
|
|
20
|
-
throw new Error("The System returned an invalid Desktop
|
|
20
|
+
throw new Error("The System returned an invalid Desktop viewport");
|
|
21
21
|
return snapshot;
|
|
22
22
|
}
|
|
23
23
|
}
|
package/dist/domain.d.ts
CHANGED
|
@@ -1,50 +1,12 @@
|
|
|
1
|
-
import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerOutcome, type AnswerSubscriber as CoreAnswerSubscriber, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskSubscriber as CoreAskSubscriber, type Cleanup, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointTraffic as CoreEndpointTraffic, type
|
|
1
|
+
import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerOutcome, type AnswerSubscriber as CoreAnswerSubscriber, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskSubscriber as CoreAskSubscriber, type Cleanup, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointReference as CoreEndpointReference, type EndpointSnapshot, type HandleAddress, type ProcessSnapshot, type ProgramSnapshot, type EndpointTraffic as CoreEndpointTraffic, type EndpointLifecycleEvents, type EndpointDeclaration, type Exit, type EventOptions, type LocalWindow, type ProgramProcess as CoreProgramProcess, type ProgramProcessRunEvent as CoreProgramProcessRunEvent, type ProgramProcessRunOptions as CoreProgramProcessRunOptions, type ServerTraffic as CoreServerTraffic, type TrafficMessage as CoreTrafficMessage, type TrafficCapture as CoreTrafficCapture, type TrafficEvents as CoreTrafficEvents, type Window as CoreWindow, type WindowState } from "@phreshos/core";
|
|
2
2
|
import Events from "./events.js";
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
export interface ClientDeclarationRecord extends EndpointDeclarationRecord {
|
|
12
|
-
title: string | null;
|
|
13
|
-
size: Size | null;
|
|
14
|
-
position: Position | null;
|
|
15
|
-
layer: ClientDeclaration["layer"];
|
|
16
|
-
minimize: boolean | null;
|
|
17
|
-
permissions: ClientDeclaration["permissions"];
|
|
18
|
-
}
|
|
19
|
-
export interface ProgramRecord {
|
|
20
|
-
reference: string;
|
|
21
|
-
identity: string;
|
|
22
|
-
assetId: string;
|
|
23
|
-
installed?: boolean;
|
|
24
|
-
name: string;
|
|
25
|
-
version: string | null;
|
|
26
|
-
description: string | null;
|
|
27
|
-
hasAgent: boolean;
|
|
28
|
-
server: EndpointDeclarationRecord | null;
|
|
29
|
-
client: ClientDeclarationRecord | null;
|
|
30
|
-
}
|
|
31
|
-
export interface ProcessRecord {
|
|
32
|
-
reference: string;
|
|
33
|
-
identity: string;
|
|
34
|
-
name: string | null;
|
|
35
|
-
program: ProgramRecord;
|
|
36
|
-
options: Record<string, string>;
|
|
37
|
-
startedAt: string | Date;
|
|
38
|
-
server: EndpointRecord | null;
|
|
39
|
-
client: EndpointRecord | null;
|
|
40
|
-
}
|
|
41
|
-
export interface EndpointRecord {
|
|
42
|
-
service: boolean;
|
|
43
|
-
}
|
|
44
|
-
export interface EndpointReference {
|
|
45
|
-
kind: "server" | "client";
|
|
46
|
-
process: ProcessRecord;
|
|
47
|
-
}
|
|
3
|
+
export type { HandleAddress };
|
|
4
|
+
export type EndpointDeclarationRecord = EndpointDeclaration;
|
|
5
|
+
export type ClientDeclarationRecord = ClientDeclaration;
|
|
6
|
+
export type ProgramRecord = ProgramSnapshot;
|
|
7
|
+
export type ProcessRecord = ProcessSnapshot;
|
|
8
|
+
export type EndpointRecord = EndpointSnapshot;
|
|
9
|
+
export type EndpointReference = CoreEndpointReference;
|
|
48
10
|
export type WindowRecord = WindowState;
|
|
49
11
|
export type Program = CoreProgram;
|
|
50
12
|
export type ProgramProcess = CoreProgramProcess;
|
|
@@ -79,48 +41,47 @@ export type Endpoint<Events extends object = {}, Fallback = unknown> = CoreEndpo
|
|
|
79
41
|
export type ServerEndpoint<Events extends object = {}, Fallback = unknown> = CoreServerEndpoint<Events, Fallback>;
|
|
80
42
|
export type ClientEndpoint<Events extends object = {}, Fallback = unknown> = CoreClientEndpoint<Events, Fallback>;
|
|
81
43
|
export type Window = CoreWindow;
|
|
82
|
-
export declare class TrafficHandle extends Events {
|
|
44
|
+
export declare class TrafficHandle<EventsMap extends object = {}, Fallback = unknown> extends Events<TrafficEvents<EventsMap>, TrafficMessage<Fallback>> {
|
|
83
45
|
protected readonly target: HandleAddress | null;
|
|
84
46
|
protected readonly kind: "server" | "client";
|
|
85
47
|
constructor(target: HandleAddress | null, kind: "server" | "client");
|
|
86
|
-
subscribeAsks(subscriber: (capture: AskCapture) => unknown): Cleanup;
|
|
87
|
-
asks(options?: EventOptions): AsyncIterableIterator<Readonly<{
|
|
48
|
+
subscribeAsks<Payload = unknown>(subscriber: (capture: AskCapture<Payload>) => unknown): Cleanup;
|
|
49
|
+
asks<Payload = unknown>(options?: EventOptions): AsyncIterableIterator<Readonly<{
|
|
88
50
|
event: string;
|
|
89
51
|
questionId: string;
|
|
90
52
|
message: Readonly<{
|
|
91
53
|
to: ServerEndpoint<{}, unknown> | null;
|
|
92
|
-
payload:
|
|
54
|
+
payload: Payload;
|
|
93
55
|
}>;
|
|
94
56
|
}>>;
|
|
95
57
|
private followAsks;
|
|
96
58
|
}
|
|
97
|
-
export declare class ServerTrafficHandle extends TrafficHandle {
|
|
98
|
-
subscribeAnswers(subscriber: (capture: AnswerCapture) => unknown): Cleanup;
|
|
99
|
-
answers(options?: EventOptions): AsyncIterableIterator<Readonly<{
|
|
59
|
+
export declare class ServerTrafficHandle<EventsMap extends object = {}, Fallback = unknown> extends TrafficHandle<EventsMap, Fallback> {
|
|
60
|
+
subscribeAnswers<Result = unknown>(subscriber: (capture: AnswerCapture<Result>) => unknown): Cleanup;
|
|
61
|
+
answers<Result = unknown>(options?: EventOptions): AsyncIterableIterator<Readonly<{
|
|
100
62
|
event: string;
|
|
101
63
|
questionId: string;
|
|
102
64
|
message: Readonly<{
|
|
103
65
|
to: Endpoint<{}, unknown> | null;
|
|
104
|
-
outcome: AnswerOutcome<
|
|
66
|
+
outcome: AnswerOutcome<Result>;
|
|
105
67
|
}>;
|
|
106
68
|
}>>;
|
|
107
69
|
private followAnswers;
|
|
108
70
|
}
|
|
109
71
|
type WindowTarget = () => Promise<HandleAddress>;
|
|
110
|
-
export declare function scoped(route: string, subject: string | null, convert: (event: string, values: unknown[]) => unknown): Events<
|
|
72
|
+
export declare function scoped<Definitions extends object = {}, Fallback = unknown>(route: string, subject: string | null, convert: (event: string, values: unknown[]) => unknown): Events<Definitions, Fallback>;
|
|
111
73
|
/** Destinationless events originating from one Endpoint handle. */
|
|
112
74
|
export declare function endpointEvents(target: HandleAddress | null, half: "server" | "client"): Events<{}, unknown>;
|
|
113
75
|
/** Start and stop transitions belonging directly to one permanent Endpoint. */
|
|
114
|
-
export declare function endpointLifecycle(target: WindowTarget, half: "server" | "client"): Events<
|
|
76
|
+
export declare function endpointLifecycle(target: WindowTarget, half: "server" | "client"): Events<EndpointLifecycleEvents, never>;
|
|
115
77
|
export declare function exit(code: unknown, signal: unknown): Exit;
|
|
116
78
|
export declare function trafficMessage(value: unknown): TrafficMessage;
|
|
117
|
-
export declare function
|
|
118
|
-
export declare function
|
|
119
|
-
export declare function process(record: ProcessRecord, endpoints?: {
|
|
79
|
+
export declare function program(value: unknown): Program;
|
|
80
|
+
export declare function process(value: unknown, endpoints?: {
|
|
120
81
|
server?: ServerEndpoint;
|
|
121
82
|
client?: ClientEndpoint;
|
|
122
83
|
}): Process;
|
|
123
|
-
export declare function endpoint(
|
|
84
|
+
export declare function endpoint(value: unknown): Endpoint;
|
|
124
85
|
export declare function claimEndpoint(reference: string, kind: "server" | "client", endpoint: Endpoint): Endpoint<{}, unknown>;
|
|
125
86
|
export declare function window(target: WindowTarget): Window;
|
|
126
87
|
export declare function localWindow(target: WindowTarget): LocalWindow;
|
|
@@ -136,4 +97,3 @@ export declare const Endpoint: typeof CoreEndpoint;
|
|
|
136
97
|
export declare const ServerEndpoint: typeof CoreServerEndpoint;
|
|
137
98
|
/** Runtime constructor shared by all client-visible Client Endpoint handles. */
|
|
138
99
|
export declare const ClientEndpoint: typeof CoreClientEndpoint;
|
|
139
|
-
export {};
|
package/dist/domain.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint,
|
|
1
|
+
import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint, parseEndpointReference, parseProcessSnapshot, parseProgramSnapshot } from "@phreshos/core";
|
|
2
2
|
import Events, { stream } from "./events.js";
|
|
3
3
|
import Deadline from "./deadline.js";
|
|
4
4
|
import HandleRegistry from "./handle-registry.js";
|
|
5
5
|
import { area, sql, store } from "./storage.js";
|
|
6
6
|
import startup from "./startup.js";
|
|
7
|
+
import launch from "./launch.js";
|
|
7
8
|
import { programPermissions } from "./permissions.js";
|
|
8
9
|
import wire from "./wire.js";
|
|
9
10
|
const handles = new HandleRegistry();
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class ProgramHandle extends ProgramBase {
|
|
11
|
+
class ProgramHandle extends CoreProgram {
|
|
12
|
+
subscribe;
|
|
13
|
+
wait;
|
|
14
|
+
events;
|
|
15
15
|
identity;
|
|
16
16
|
reference;
|
|
17
17
|
data;
|
|
@@ -20,6 +20,7 @@ class ProgramHandle extends ProgramBase {
|
|
|
20
20
|
logs;
|
|
21
21
|
database;
|
|
22
22
|
startup;
|
|
23
|
+
launch;
|
|
23
24
|
process;
|
|
24
25
|
permissions;
|
|
25
26
|
record;
|
|
@@ -34,21 +35,21 @@ class ProgramHandle extends ProgramBase {
|
|
|
34
35
|
this.logs = sql("logs", this.address);
|
|
35
36
|
this.database = sql("database", this.address);
|
|
36
37
|
this.startup = startup(this.address);
|
|
38
|
+
this.launch = launch(this.address);
|
|
37
39
|
this.permissions = programPermissions(this.address);
|
|
38
40
|
this.process = new ProgramProcessHandle(this.address, record.reference);
|
|
39
|
-
|
|
41
|
+
const events = scoped("program-host", record.reference, programEvent);
|
|
42
|
+
this.subscribe = events.subscribe;
|
|
43
|
+
this.wait = events.wait;
|
|
44
|
+
this.events = events.events;
|
|
40
45
|
}
|
|
41
46
|
get name() { return this.record.name; }
|
|
42
47
|
get assetId() { return this.record.assetId; }
|
|
43
48
|
get version() { return this.record.version; }
|
|
44
49
|
get description() { return this.record.description; }
|
|
45
50
|
get hasAgent() { return this.record.hasAgent; }
|
|
46
|
-
get server() {
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
get client() {
|
|
50
|
-
return this.record.client ? clientDeclaration(this.record.client) : null;
|
|
51
|
-
}
|
|
51
|
+
get server() { return this.record.server; }
|
|
52
|
+
get client() { return this.record.client; }
|
|
52
53
|
get address() { return { identity: this.identity, reference: this.reference }; }
|
|
53
54
|
update(record) {
|
|
54
55
|
if (record.reference !== this.reference)
|
|
@@ -74,10 +75,6 @@ class ProgramHandle extends ProgramBase {
|
|
|
74
75
|
yield programCommandChunk(value);
|
|
75
76
|
}
|
|
76
77
|
}
|
|
77
|
-
async fork(identity) {
|
|
78
|
-
const answer = await wire.request(["fork", this.address, identity]);
|
|
79
|
-
return program(answer[0]);
|
|
80
|
-
}
|
|
81
78
|
async *uninstall(everything = false) {
|
|
82
79
|
for await (const value of wire.stream(["uninstall", this.address, everything])) {
|
|
83
80
|
yield programCommandChunk(value);
|
|
@@ -92,28 +89,17 @@ function programCommandChunk(value) {
|
|
|
92
89
|
}
|
|
93
90
|
return Object.freeze({ stream: chunk.stream, text: chunk.text });
|
|
94
91
|
}
|
|
95
|
-
function declaration(record) {
|
|
96
|
-
return Object.freeze({
|
|
97
|
-
start: record.start,
|
|
98
|
-
service: record.service
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
function clientDeclaration(record) {
|
|
102
|
-
return Object.freeze({
|
|
103
|
-
...declaration(record),
|
|
104
|
-
title: record.title,
|
|
105
|
-
size: record.size,
|
|
106
|
-
position: record.position,
|
|
107
|
-
layer: record.layer,
|
|
108
|
-
minimize: record.minimize,
|
|
109
|
-
permissions: parseClientPermissions(record.permissions)
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
92
|
class ProgramProcessHandle {
|
|
113
93
|
address;
|
|
94
|
+
subscribe;
|
|
95
|
+
wait;
|
|
96
|
+
events;
|
|
114
97
|
constructor(address, reference) {
|
|
115
98
|
this.address = address;
|
|
116
|
-
|
|
99
|
+
const events = scoped("program-process", reference, programProcessEvent);
|
|
100
|
+
this.subscribe = events.subscribe;
|
|
101
|
+
this.wait = events.wait;
|
|
102
|
+
this.events = events.events;
|
|
117
103
|
}
|
|
118
104
|
async list() {
|
|
119
105
|
const answer = await wire.request(["program-process-list", this.address]);
|
|
@@ -165,7 +151,10 @@ function processRunEvent(value) {
|
|
|
165
151
|
function chronological(processes) {
|
|
166
152
|
return processes.sort((left, right) => left.startedAt.getTime() - right.startedAt.getTime());
|
|
167
153
|
}
|
|
168
|
-
class ProcessHandle extends
|
|
154
|
+
class ProcessHandle extends CoreProcess {
|
|
155
|
+
subscribe;
|
|
156
|
+
wait;
|
|
157
|
+
events;
|
|
169
158
|
identity;
|
|
170
159
|
reference;
|
|
171
160
|
name;
|
|
@@ -173,7 +162,7 @@ class ProcessHandle extends ProcessBase {
|
|
|
173
162
|
server;
|
|
174
163
|
client;
|
|
175
164
|
ownerProgram;
|
|
176
|
-
|
|
165
|
+
launchOptions;
|
|
177
166
|
constructor(record, endpoints = {}) {
|
|
178
167
|
super();
|
|
179
168
|
this.identity = record.identity;
|
|
@@ -181,10 +170,13 @@ class ProcessHandle extends ProcessBase {
|
|
|
181
170
|
this.name = record.name;
|
|
182
171
|
this.startedAt = new Date(record.startedAt);
|
|
183
172
|
this.ownerProgram = program(record.program);
|
|
184
|
-
this.
|
|
173
|
+
this.launchOptions = Object.freeze({ ...record.options });
|
|
185
174
|
this.server = endpointHandle(this, "server", endpoints.server);
|
|
186
175
|
this.client = endpointHandle(this, "client", endpoints.client);
|
|
187
|
-
|
|
176
|
+
const events = scoped("process-host", record.reference, processEvent);
|
|
177
|
+
this.subscribe = events.subscribe;
|
|
178
|
+
this.wait = events.wait;
|
|
179
|
+
this.events = events.events;
|
|
188
180
|
}
|
|
189
181
|
program() { return this.ownerProgram; }
|
|
190
182
|
get address() { return { identity: this.identity, reference: this.reference }; }
|
|
@@ -192,11 +184,8 @@ class ProcessHandle extends ProcessBase {
|
|
|
192
184
|
const answer = await wire.request(["parent", this.address]);
|
|
193
185
|
return answer[0] ? process(answer[0]) : null;
|
|
194
186
|
}
|
|
195
|
-
async
|
|
196
|
-
|
|
197
|
-
return this.options[name];
|
|
198
|
-
const answer = await wire.request(["option", this.address, name]);
|
|
199
|
-
return answer[0];
|
|
187
|
+
async options(name) {
|
|
188
|
+
return name === undefined ? this.launchOptions : this.launchOptions[name];
|
|
200
189
|
}
|
|
201
190
|
async exit() { await wire.request(["exit", this.address]); }
|
|
202
191
|
async exited() {
|
|
@@ -245,8 +234,11 @@ export class ServerTrafficHandle extends TrafficHandle {
|
|
|
245
234
|
}, impossible);
|
|
246
235
|
}
|
|
247
236
|
}
|
|
248
|
-
class ServerEndpointHandle extends
|
|
237
|
+
class ServerEndpointHandle extends CoreServerEndpoint {
|
|
249
238
|
owner;
|
|
239
|
+
subscribe;
|
|
240
|
+
wait;
|
|
241
|
+
events;
|
|
250
242
|
traffic;
|
|
251
243
|
lifecycle;
|
|
252
244
|
constructor(owner) {
|
|
@@ -254,10 +246,15 @@ class ServerEndpointHandle extends ServerEndpointBase {
|
|
|
254
246
|
this.owner = owner;
|
|
255
247
|
this.traffic = new ServerTrafficHandle(owner.address, "server");
|
|
256
248
|
this.lifecycle = endpointLifecycle(() => Promise.resolve(owner.address), "server");
|
|
257
|
-
|
|
249
|
+
const events = endpointEvents(owner.address, "server");
|
|
250
|
+
this.subscribe = events.subscribe;
|
|
251
|
+
this.wait = events.wait;
|
|
252
|
+
this.events = events.events;
|
|
258
253
|
}
|
|
259
254
|
async process() { return this.owner; }
|
|
260
|
-
publish(event, payload = undefined) {
|
|
255
|
+
publish = (event, payload = undefined) => {
|
|
256
|
+
wire.send("end-host", "send", this.owner.address, "server", event, payload);
|
|
257
|
+
};
|
|
261
258
|
async exists() {
|
|
262
259
|
const answer = await wire.request(["exists", "server", this.owner.address]);
|
|
263
260
|
return answer[0];
|
|
@@ -288,8 +285,11 @@ class ServerEndpointHandle extends ServerEndpointBase {
|
|
|
288
285
|
}
|
|
289
286
|
}
|
|
290
287
|
}
|
|
291
|
-
class ClientEndpointHandle extends
|
|
288
|
+
class ClientEndpointHandle extends CoreClientEndpoint {
|
|
292
289
|
owner;
|
|
290
|
+
subscribe;
|
|
291
|
+
wait;
|
|
292
|
+
events;
|
|
293
293
|
traffic;
|
|
294
294
|
lifecycle;
|
|
295
295
|
window;
|
|
@@ -299,10 +299,15 @@ class ClientEndpointHandle extends ClientEndpointBase {
|
|
|
299
299
|
this.traffic = new TrafficHandle(owner.address, "client");
|
|
300
300
|
this.lifecycle = endpointLifecycle(() => Promise.resolve(owner.address), "client");
|
|
301
301
|
this.window = window(async () => owner.address);
|
|
302
|
-
|
|
302
|
+
const events = endpointEvents(owner.address, "client");
|
|
303
|
+
this.subscribe = events.subscribe;
|
|
304
|
+
this.wait = events.wait;
|
|
305
|
+
this.events = events.events;
|
|
303
306
|
}
|
|
304
307
|
async process() { return this.owner; }
|
|
305
|
-
publish(event, payload = undefined) {
|
|
308
|
+
publish = (event, payload = undefined) => {
|
|
309
|
+
wire.send("end-host", "send", this.owner.address, "client", event, payload);
|
|
310
|
+
};
|
|
306
311
|
async exists() {
|
|
307
312
|
const answer = await wire.request(["exists", "client", this.owner.address]);
|
|
308
313
|
return answer[0];
|
|
@@ -326,13 +331,14 @@ class WindowHandle extends Events {
|
|
|
326
331
|
async position() { return (await this.state()).position; }
|
|
327
332
|
async size() { return (await this.state()).size; }
|
|
328
333
|
async minimized() { return (await this.state()).minimized; }
|
|
334
|
+
async maximized() { return (await this.state()).maximized; }
|
|
329
335
|
async front() { return (await this.state()).front; }
|
|
330
336
|
async layer() { return (await this.state()).layer; }
|
|
331
|
-
async location() { return (await this.state()).location; }
|
|
332
337
|
async move(position) { await wire.request(["move", await this.target(), position]); }
|
|
333
338
|
async resize(size) { await wire.request(["resize", await this.target(), size]); }
|
|
334
339
|
async setGeometry(geometry) { await wire.request(["setGeometry", await this.target(), geometry]); }
|
|
335
340
|
async minimize(minimized = true) { await wire.request(["minimize", await this.target(), minimized]); }
|
|
341
|
+
async maximize(maximized = true) { await wire.request(["maximize", await this.target(), maximized]); }
|
|
336
342
|
async changeTitle(title) { await wire.request(["changeTitle", await this.target(), title]); }
|
|
337
343
|
async raise() { await wire.request(["raise", await this.target()]); }
|
|
338
344
|
}
|
|
@@ -353,6 +359,8 @@ class LocalWindowHandle {
|
|
|
353
359
|
async resize(size) { await this.change("windowLocalResize", size); }
|
|
354
360
|
async setGeometry(geometry) { await this.change("windowLocalGeometry", geometry); }
|
|
355
361
|
async minimize(minimized = true) { await this.change("windowLocalMinimize", minimized); }
|
|
362
|
+
async maximize(maximized = true) { await this.change("windowLocalMaximize", maximized); }
|
|
363
|
+
async changeTitle(title) { await wire.request(["windowLocalTitle", await this.target(), title]); }
|
|
356
364
|
async follow(window) {
|
|
357
365
|
const target = windowTargets.get(window);
|
|
358
366
|
if (!target)
|
|
@@ -388,7 +396,7 @@ function deferredScoped(route, target, convert) {
|
|
|
388
396
|
];
|
|
389
397
|
}
|
|
390
398
|
function windowEvent(event) {
|
|
391
|
-
return event === "move" || event === "resize" || event === "geometry" || event === "minimize" || event === "changeTitle" || event === "front";
|
|
399
|
+
return event === "move" || event === "resize" || event === "geometry" || event === "minimize" || event === "maximize" || event === "changeTitle" || event === "front";
|
|
392
400
|
}
|
|
393
401
|
function deferred(target, register, impossible) {
|
|
394
402
|
let active = true;
|
|
@@ -481,24 +489,18 @@ export function trafficMessage(value) {
|
|
|
481
489
|
const raw = value;
|
|
482
490
|
return { to: visibleEndpoint(raw.to), payload: raw.payload };
|
|
483
491
|
}
|
|
484
|
-
export function
|
|
485
|
-
|
|
486
|
-
subscribe: events.subscribe.bind(events),
|
|
487
|
-
waitFor: events.waitFor.bind(events),
|
|
488
|
-
events: events.events.bind(events)
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
export function program(record) {
|
|
492
|
+
export function program(value) {
|
|
493
|
+
const record = parseProgramSnapshot(value);
|
|
492
494
|
const handle = handles.obtain(`program:${record.reference}`, () => new ProgramHandle(record));
|
|
493
495
|
handle.update(record);
|
|
494
496
|
return handle;
|
|
495
497
|
}
|
|
496
|
-
export function process(
|
|
498
|
+
export function process(value, endpoints = {}) {
|
|
499
|
+
const record = parseProcessSnapshot(value);
|
|
497
500
|
return handles.obtain(`process:${record.reference}`, () => new ProcessHandle(record, endpoints));
|
|
498
501
|
}
|
|
499
|
-
export function endpoint(
|
|
500
|
-
|
|
501
|
-
throw new Error("The boundary returned an invalid Endpoint reference");
|
|
502
|
+
export function endpoint(value) {
|
|
503
|
+
const reference = parseEndpointReference(value);
|
|
502
504
|
const owner = process(reference.process);
|
|
503
505
|
return reference.kind === "server" ? owner.server : owner.client;
|
|
504
506
|
}
|
package/dist/events.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { subscribableDefinition, type Cleanup, type EventOptions, type Subscribable, type SubscribableDefinition } from "@phreshos/core";
|
|
2
2
|
export declare const defaultTimeout = 10000;
|
|
3
3
|
type Failure = (error: Error) => void;
|
|
4
4
|
type Listener = (message: unknown) => unknown;
|
|
@@ -8,9 +8,10 @@ type Register<Message> = (listener: (message: Message) => unknown, impossible?:
|
|
|
8
8
|
export default class Events<Vocabulary extends object = {}, Fallback = unknown> implements Subscribable<Vocabulary, Fallback> {
|
|
9
9
|
private readonly listen;
|
|
10
10
|
private readonly listenAll;
|
|
11
|
+
readonly [subscribableDefinition]?: SubscribableDefinition<Vocabulary, Fallback>;
|
|
11
12
|
constructor(listen: (event: string, listener: Listener, impossible?: Failure) => Cleanup, listenAll: (listener: EveryListener, impossible?: Failure) => Cleanup);
|
|
12
13
|
readonly subscribe: Subscribable<Vocabulary, Fallback>["subscribe"];
|
|
13
|
-
readonly
|
|
14
|
+
readonly wait: Subscribable<Vocabulary, Fallback>["wait"];
|
|
14
15
|
readonly events: Subscribable<Vocabulary, Fallback>["events"];
|
|
15
16
|
}
|
|
16
17
|
/** Converts one persistent registration into a bounded asynchronous iterator. */
|
package/dist/events.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { subscribableDefinition } from "@phreshos/core";
|
|
1
2
|
export const defaultTimeout = 10_000;
|
|
2
3
|
/** SDK-owned waits, queues and callbacks over boundary forwarding state. */
|
|
3
4
|
export default class Events {
|
|
@@ -12,7 +13,7 @@ export default class Events {
|
|
|
12
13
|
return this.listen(eventOrSubscriber, subscriber);
|
|
13
14
|
return this.listenAll((event, message) => eventOrSubscriber({ event, message }));
|
|
14
15
|
});
|
|
15
|
-
|
|
16
|
+
wait = ((event, timeout = defaultTimeout) => {
|
|
16
17
|
return new Promise((resolve, reject) => {
|
|
17
18
|
let settled = false;
|
|
18
19
|
let stop = () => undefined;
|
package/dist/launch.d.ts
ADDED
package/dist/launch.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { parseLaunch } from "@phreshos/core";
|
|
2
|
+
import wire from "./wire.js";
|
|
3
|
+
/** Bind saved launch operations to one Program handle. */
|
|
4
|
+
export default function launch(program) {
|
|
5
|
+
return {
|
|
6
|
+
async get() {
|
|
7
|
+
const [value] = await wire.request(["launch", program, "get"]);
|
|
8
|
+
return value === null ? null : parseLaunch(value);
|
|
9
|
+
},
|
|
10
|
+
async set(value) {
|
|
11
|
+
await wire.request(["launch", program, "set", parseLaunch(value)]);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Network } from "@phreshos/core";
|
|
2
|
+
declare class ClientNetwork implements Network {
|
|
3
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
4
|
+
websocket(url: string | URL, protocols?: string | string[]): Promise<WebSocket>;
|
|
5
|
+
}
|
|
6
|
+
declare const _default: ClientNetwork;
|
|
7
|
+
export default _default;
|
package/dist/network.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import wire from "./wire.js";
|
|
2
|
+
import controlledStream from "./controlled-stream.js";
|
|
3
|
+
import websocket from "./websocket.js";
|
|
4
|
+
class ClientNetwork {
|
|
5
|
+
async fetch(input, init) {
|
|
6
|
+
const request = new Request(input, {
|
|
7
|
+
...init,
|
|
8
|
+
...init?.body instanceof ReadableStream ? { duplex: "half" } : {}
|
|
9
|
+
});
|
|
10
|
+
const body = request.body;
|
|
11
|
+
const control = new MessageChannel();
|
|
12
|
+
const abort = () => control.port1.postMessage("abort");
|
|
13
|
+
if (request.signal.aborted) {
|
|
14
|
+
control.port1.close();
|
|
15
|
+
control.port2.close();
|
|
16
|
+
throw request.signal.reason;
|
|
17
|
+
}
|
|
18
|
+
request.signal.addEventListener("abort", abort, { once: true });
|
|
19
|
+
const description = {
|
|
20
|
+
body: body !== null,
|
|
21
|
+
cache: request.cache,
|
|
22
|
+
credentials: request.credentials,
|
|
23
|
+
headers: requestHeaders(request.headers, init?.headers),
|
|
24
|
+
integrity: request.integrity,
|
|
25
|
+
keepalive: request.keepalive,
|
|
26
|
+
method: request.method,
|
|
27
|
+
mode: request.mode,
|
|
28
|
+
redirect: request.redirect,
|
|
29
|
+
referrer: request.referrer,
|
|
30
|
+
referrerPolicy: request.referrerPolicy,
|
|
31
|
+
url: request.url
|
|
32
|
+
};
|
|
33
|
+
let result;
|
|
34
|
+
try {
|
|
35
|
+
const answer = await wire.request(["fetch", description, body, control.port2], undefined, body ? [body, control.port2] : [control.port2]);
|
|
36
|
+
result = answer[0];
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
abort();
|
|
40
|
+
closeControl(request.signal, control.port1, abort);
|
|
41
|
+
throw request.signal.aborted ? request.signal.reason : error;
|
|
42
|
+
}
|
|
43
|
+
const responseBody = result.body
|
|
44
|
+
? controlledStream(result.body, abort, () => closeControl(request.signal, control.port1, abort))
|
|
45
|
+
: null;
|
|
46
|
+
if (!responseBody)
|
|
47
|
+
closeControl(request.signal, control.port1, abort);
|
|
48
|
+
const response = new Response(responseBody, {
|
|
49
|
+
headers: result.headers,
|
|
50
|
+
status: result.status,
|
|
51
|
+
statusText: result.statusText
|
|
52
|
+
});
|
|
53
|
+
Object.defineProperties(response, {
|
|
54
|
+
redirected: { configurable: true, enumerable: true, value: result.redirected },
|
|
55
|
+
type: { configurable: true, enumerable: true, value: result.type },
|
|
56
|
+
url: { configurable: true, enumerable: true, value: result.url }
|
|
57
|
+
});
|
|
58
|
+
return response;
|
|
59
|
+
}
|
|
60
|
+
websocket(url, protocols) {
|
|
61
|
+
return websocket(url, protocols);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function requestHeaders(normalized, supplied) {
|
|
65
|
+
if (!supplied)
|
|
66
|
+
return [...normalized.entries()];
|
|
67
|
+
const explicit = supplied instanceof Headers
|
|
68
|
+
? [...supplied.entries()]
|
|
69
|
+
: Array.isArray(supplied)
|
|
70
|
+
? supplied.map(([name, value]) => [name, value])
|
|
71
|
+
: Object.entries(supplied);
|
|
72
|
+
const names = new Set(explicit.map(([name]) => name.toLowerCase()));
|
|
73
|
+
return [...normalized.entries()].filter(([name]) => !names.has(name.toLowerCase())).concat(explicit);
|
|
74
|
+
}
|
|
75
|
+
function closeControl(signal, port, abort) {
|
|
76
|
+
signal.removeEventListener("abort", abort);
|
|
77
|
+
port.close();
|
|
78
|
+
}
|
|
79
|
+
export default new ClientNetwork();
|
package/dist/permissions.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ContextPermissions, ProgramPermissions } from "@phreshos/core";
|
|
2
2
|
import type { HandleAddress } from "./domain.js";
|
|
3
3
|
export declare const defaultPermissionTimeout = 120000;
|
|
4
|
-
/** Bind authoritative
|
|
4
|
+
/** Bind authoritative permission state to one exact Program handle. */
|
|
5
5
|
export declare function programPermissions(program: HandleAddress): ProgramPermissions;
|
|
6
|
-
/**
|
|
6
|
+
/** Permission state and owner requests belonging to the current Client Endpoint. */
|
|
7
7
|
export declare function contextPermissions(): ContextPermissions;
|
package/dist/permissions.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parsePermission, parsePermissions } from "@phreshos/core";
|
|
2
2
|
import wire from "./wire.js";
|
|
3
3
|
export const defaultPermissionTimeout = 120_000;
|
|
4
|
-
/** Bind authoritative
|
|
4
|
+
/** Bind authoritative permission state to one exact Program handle. */
|
|
5
5
|
export function programPermissions(program) {
|
|
6
6
|
const operate = (operation, name, permission) => (wire.request(["program-permissions", program, operation, name, permission]));
|
|
7
7
|
return {
|
|
@@ -12,7 +12,7 @@ export function programPermissions(program) {
|
|
|
12
12
|
async delete(name) { await operate("delete", name); }
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
/**
|
|
15
|
+
/** Permission state and owner requests belonging to the current Client Endpoint. */
|
|
16
16
|
export function contextPermissions() {
|
|
17
17
|
const timed = (timeout) => ({
|
|
18
18
|
async request(name, permission = true) {
|
package/dist/service.js
CHANGED
|
@@ -4,8 +4,6 @@ import Events from "./events.js";
|
|
|
4
4
|
import HandleRegistry from "./handle-registry.js";
|
|
5
5
|
import wire from "./wire.js";
|
|
6
6
|
const handles = new HandleRegistry();
|
|
7
|
-
const ServerServiceBase = CoreServerService;
|
|
8
|
-
const ClientServiceBase = CoreClientService;
|
|
9
7
|
class ServiceHandle {
|
|
10
8
|
key;
|
|
11
9
|
lifecycle;
|
|
@@ -24,8 +22,11 @@ class ServiceHandle {
|
|
|
24
22
|
await wire.request(["service-wait-ready", this.key, timeout], timeout);
|
|
25
23
|
}
|
|
26
24
|
}
|
|
27
|
-
class ServerHandler extends
|
|
25
|
+
class ServerHandler extends CoreServerService {
|
|
28
26
|
key;
|
|
27
|
+
subscribe;
|
|
28
|
+
wait;
|
|
29
|
+
events;
|
|
29
30
|
lifecycle;
|
|
30
31
|
service;
|
|
31
32
|
constructor(key) {
|
|
@@ -33,9 +34,12 @@ class ServerHandler extends ServerServiceBase {
|
|
|
33
34
|
this.key = key;
|
|
34
35
|
this.service = new ServiceHandle(key);
|
|
35
36
|
this.lifecycle = this.service.lifecycle;
|
|
36
|
-
|
|
37
|
+
const events = new Events(...serviceEvents(key, "events"));
|
|
38
|
+
this.subscribe = events.subscribe;
|
|
39
|
+
this.wait = events.wait;
|
|
40
|
+
this.events = events.events;
|
|
37
41
|
}
|
|
38
|
-
publish(event, payload = undefined) { this.service.publish(event, payload); }
|
|
42
|
+
publish = (event, payload = undefined) => { this.service.publish(event, payload); };
|
|
39
43
|
exists() { return this.service.exists(); }
|
|
40
44
|
waitReady(timeout) { return this.service.waitReady(timeout); }
|
|
41
45
|
async ask(event, payload = undefined) {
|
|
@@ -58,31 +62,39 @@ class ServerHandler extends ServerServiceBase {
|
|
|
58
62
|
}
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
|
-
class ClientHandler extends
|
|
65
|
+
class ClientHandler extends CoreClientService {
|
|
66
|
+
subscribe;
|
|
67
|
+
wait;
|
|
68
|
+
events;
|
|
62
69
|
lifecycle;
|
|
63
70
|
service;
|
|
64
71
|
constructor(key) {
|
|
65
72
|
super();
|
|
66
73
|
this.service = new ServiceHandle(key);
|
|
67
74
|
this.lifecycle = this.service.lifecycle;
|
|
68
|
-
|
|
75
|
+
const events = new Events(...serviceEvents(key, "events"));
|
|
76
|
+
this.subscribe = events.subscribe;
|
|
77
|
+
this.wait = events.wait;
|
|
78
|
+
this.events = events.events;
|
|
69
79
|
}
|
|
70
|
-
publish(event, payload = undefined) { this.service.publish(event, payload); }
|
|
80
|
+
publish = (event, payload = undefined) => { this.service.publish(event, payload); };
|
|
71
81
|
exists() { return this.service.exists(); }
|
|
72
82
|
waitReady(timeout) { return this.service.waitReady(timeout); }
|
|
73
83
|
}
|
|
74
84
|
export function prepareService(key) {
|
|
75
85
|
if (!isServiceKey(key))
|
|
76
86
|
throw new Error("A complete service key is required");
|
|
77
|
-
const
|
|
87
|
+
const address = Object.freeze({
|
|
78
88
|
...(key.program === undefined ? {} : { program: key.program }),
|
|
79
|
-
process: key.process
|
|
80
|
-
endpoint: key.endpoint
|
|
89
|
+
process: key.process
|
|
81
90
|
});
|
|
82
91
|
const identity = JSON.stringify([key.program ?? null, key.process, key.endpoint]);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
if (key.endpoint === "server") {
|
|
93
|
+
const normalized = Object.freeze({ ...address, endpoint: "server" });
|
|
94
|
+
return handles.obtain(`service:${identity}`, () => new ServerHandler(normalized));
|
|
95
|
+
}
|
|
96
|
+
const normalized = Object.freeze({ ...address, endpoint: "client" });
|
|
97
|
+
return handles.obtain(`service:${identity}`, () => new ClientHandler(normalized));
|
|
86
98
|
}
|
|
87
99
|
function serviceEvents(key, scope) {
|
|
88
100
|
return [
|
|
@@ -93,10 +105,3 @@ function serviceEvents(key, scope) {
|
|
|
93
105
|
}, impossible)
|
|
94
106
|
];
|
|
95
107
|
}
|
|
96
|
-
function bindEvents(target, events) {
|
|
97
|
-
Object.assign(target, {
|
|
98
|
-
subscribe: events.subscribe.bind(events),
|
|
99
|
-
waitFor: events.waitFor.bind(events),
|
|
100
|
-
events: events.events.bind(events)
|
|
101
|
-
});
|
|
102
|
-
}
|
package/dist/system.js
CHANGED
|
@@ -3,76 +3,18 @@ import ClientAppearance from "./appearance.js";
|
|
|
3
3
|
import wire from "./wire.js";
|
|
4
4
|
import { prepareService } from "./service.js";
|
|
5
5
|
import { uploads } from "./uploads.js";
|
|
6
|
-
import controlledStream from "./controlled-stream.js";
|
|
7
6
|
import Events from "./events.js";
|
|
8
7
|
import { exit, process, program } from "./domain.js";
|
|
9
8
|
import { systemStorage } from "./storage.js";
|
|
10
|
-
import
|
|
9
|
+
import network from "./network.js";
|
|
11
10
|
class ClientSystem {
|
|
12
11
|
storage = systemStorage();
|
|
13
12
|
appearance = new ClientAppearance();
|
|
14
13
|
program = new SystemProgramHandle();
|
|
15
14
|
process = new SystemProcessHandle();
|
|
16
15
|
uploads = uploads;
|
|
16
|
+
network = network;
|
|
17
17
|
service(key) { return prepareService(key); }
|
|
18
|
-
async fetch(input, init) {
|
|
19
|
-
const request = new Request(input, {
|
|
20
|
-
...init,
|
|
21
|
-
...init?.body instanceof ReadableStream ? { duplex: "half" } : {}
|
|
22
|
-
});
|
|
23
|
-
const body = request.body;
|
|
24
|
-
const control = new MessageChannel();
|
|
25
|
-
const abort = () => control.port1.postMessage("abort");
|
|
26
|
-
if (request.signal.aborted) {
|
|
27
|
-
control.port1.close();
|
|
28
|
-
control.port2.close();
|
|
29
|
-
throw request.signal.reason;
|
|
30
|
-
}
|
|
31
|
-
request.signal.addEventListener("abort", abort, { once: true });
|
|
32
|
-
const description = {
|
|
33
|
-
body: body !== null,
|
|
34
|
-
cache: request.cache,
|
|
35
|
-
credentials: request.credentials,
|
|
36
|
-
headers: requestHeaders(request.headers, init?.headers),
|
|
37
|
-
integrity: request.integrity,
|
|
38
|
-
keepalive: request.keepalive,
|
|
39
|
-
method: request.method,
|
|
40
|
-
mode: request.mode,
|
|
41
|
-
redirect: request.redirect,
|
|
42
|
-
referrer: request.referrer,
|
|
43
|
-
referrerPolicy: request.referrerPolicy,
|
|
44
|
-
url: request.url
|
|
45
|
-
};
|
|
46
|
-
let result;
|
|
47
|
-
try {
|
|
48
|
-
const answer = await wire.request(["fetch", description, body, control.port2], undefined, body ? [body, control.port2] : [control.port2]);
|
|
49
|
-
result = answer[0];
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
abort();
|
|
53
|
-
closeControl(request.signal, control.port1, abort);
|
|
54
|
-
throw request.signal.aborted ? request.signal.reason : error;
|
|
55
|
-
}
|
|
56
|
-
const responseBody = result.body
|
|
57
|
-
? controlledStream(result.body, abort, () => closeControl(request.signal, control.port1, abort))
|
|
58
|
-
: null;
|
|
59
|
-
if (!responseBody)
|
|
60
|
-
closeControl(request.signal, control.port1, abort);
|
|
61
|
-
const response = new Response(responseBody, {
|
|
62
|
-
headers: result.headers,
|
|
63
|
-
status: result.status,
|
|
64
|
-
statusText: result.statusText
|
|
65
|
-
});
|
|
66
|
-
Object.defineProperties(response, {
|
|
67
|
-
redirected: { configurable: true, enumerable: true, value: result.redirected },
|
|
68
|
-
type: { configurable: true, enumerable: true, value: result.type },
|
|
69
|
-
url: { configurable: true, enumerable: true, value: result.url }
|
|
70
|
-
});
|
|
71
|
-
return response;
|
|
72
|
-
}
|
|
73
|
-
websocket(url, protocols) {
|
|
74
|
-
return websocket(url, protocols);
|
|
75
|
-
}
|
|
76
18
|
async *shell(command, options = {}) {
|
|
77
19
|
const { signal, ...settings } = options;
|
|
78
20
|
for await (const event of wire.stream(["shell", command, settings], undefined, signal))
|
|
@@ -133,20 +75,5 @@ function systemProgramEvent(event, values) {
|
|
|
133
75
|
return { program: program(values[1]), everything: values[2] === true };
|
|
134
76
|
return values[0];
|
|
135
77
|
}
|
|
136
|
-
function requestHeaders(normalized, supplied) {
|
|
137
|
-
if (!supplied)
|
|
138
|
-
return [...normalized.entries()];
|
|
139
|
-
const explicit = supplied instanceof Headers
|
|
140
|
-
? [...supplied.entries()]
|
|
141
|
-
: Array.isArray(supplied)
|
|
142
|
-
? supplied.map(([name, value]) => [name, value])
|
|
143
|
-
: Object.entries(supplied);
|
|
144
|
-
const names = new Set(explicit.map(([name]) => name.toLowerCase()));
|
|
145
|
-
return [...normalized.entries()].filter(([name]) => !names.has(name.toLowerCase())).concat(explicit);
|
|
146
|
-
}
|
|
147
|
-
function closeControl(signal, port, abort) {
|
|
148
|
-
signal.removeEventListener("abort", abort);
|
|
149
|
-
port.close();
|
|
150
|
-
}
|
|
151
78
|
/** The global System represented through this Client runtime. */
|
|
152
79
|
export const system = new ClientSystem();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"description": "The SDK used by a Program's client endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -41,19 +41,19 @@
|
|
|
41
41
|
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
42
42
|
"check": "tsc --noEmit && tsc -p tsconfig.test.json",
|
|
43
43
|
"build": "node --run clean && tsc --noEmit false --outDir dist --rootDir source",
|
|
44
|
-
"verify
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"prepack": "node --run build"
|
|
44
|
+
"verify": "node --run check && node --run build && node --run test",
|
|
45
|
+
"prepack": "node --run build",
|
|
46
|
+
"test": "vitest run --project default"
|
|
48
47
|
},
|
|
49
48
|
"peerDependencies": {
|
|
50
|
-
"@phreshos/core": "^0.1.
|
|
49
|
+
"@phreshos/core": "^0.1.45"
|
|
51
50
|
},
|
|
52
51
|
"dependencies": {
|
|
53
52
|
"@the-link/messagepack": "^0.1.0"
|
|
54
53
|
},
|
|
55
54
|
"devDependencies": {
|
|
56
|
-
"@phreshos/core": "^0.1.
|
|
57
|
-
"typescript": "^6.0.3"
|
|
55
|
+
"@phreshos/core": "^0.1.45",
|
|
56
|
+
"typescript": "^6.0.3",
|
|
57
|
+
"vitest": "^4.1.10"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { DesktopSurfaceEvents, DesktopSurfaceSource } from "@phreshos/core";
|
|
2
|
-
import Events from "../events.js";
|
|
3
|
-
/** Read-only access to the Desktop surface containing this Client Endpoint. */
|
|
4
|
-
export default class ClientSurface extends Events<DesktopSurfaceEvents, never> implements DesktopSurfaceSource {
|
|
5
|
-
constructor();
|
|
6
|
-
snapshot(): Promise<Readonly<{
|
|
7
|
-
size: import("@phreshos/core").DesktopSize;
|
|
8
|
-
}>>;
|
|
9
|
-
}
|