@phreshos/node 0.1.3 → 0.1.5
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/events.d.ts +2 -2
- package/dist/events.js +87 -48
- package/dist/main.d.ts +1 -1
- package/dist/system.d.ts +1 -191
- package/dist/system.js +151 -124
- package/package.json +2 -2
package/dist/events.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Subscribable } from "@phreshos/core";
|
|
2
2
|
type Wait = (event: string | null, signal: AbortSignal, timeout?: number) => Promise<unknown>;
|
|
3
|
-
/**
|
|
3
|
+
/** Adapts authoritative one-event waits into the shared Subscribable contract. */
|
|
4
4
|
export default class Events<Definitions extends object, Fallback = never> {
|
|
5
5
|
private readonly names;
|
|
6
6
|
private readonly wait;
|
|
@@ -8,6 +8,6 @@ export default class Events<Definitions extends object, Fallback = never> {
|
|
|
8
8
|
readonly subscribe: Subscribable<Definitions, Fallback>["subscribe"];
|
|
9
9
|
readonly waitFor: Subscribable<Definitions, Fallback>["waitFor"];
|
|
10
10
|
readonly events: Subscribable<Definitions, Fallback>["events"];
|
|
11
|
-
|
|
11
|
+
private listen;
|
|
12
12
|
}
|
|
13
13
|
export {};
|
package/dist/events.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Adapts authoritative one-event waits into the shared Subscribable contract. */
|
|
2
2
|
export default class Events {
|
|
3
3
|
names;
|
|
4
4
|
wait;
|
|
@@ -6,73 +6,112 @@ export default class Events {
|
|
|
6
6
|
this.names = names;
|
|
7
7
|
this.wait = wait;
|
|
8
8
|
}
|
|
9
|
-
subscribe = ((
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
})();
|
|
24
|
-
return () => controller.abort();
|
|
9
|
+
subscribe = ((eventOrSubscriber, subscriber) => {
|
|
10
|
+
if (typeof eventOrSubscriber === "string")
|
|
11
|
+
return this.listen(eventOrSubscriber, subscriber);
|
|
12
|
+
if (this.names.length) {
|
|
13
|
+
const stops = this.names.map(event => this.listen(event, message => eventOrSubscriber({ event, message })));
|
|
14
|
+
return () => stops.forEach(stop => stop());
|
|
15
|
+
}
|
|
16
|
+
return this.listen(null, value => {
|
|
17
|
+
const capture = value;
|
|
18
|
+
if (typeof capture.event === "string")
|
|
19
|
+
eventOrSubscriber({ event: capture.event, message: capture.payload });
|
|
20
|
+
});
|
|
25
21
|
});
|
|
26
22
|
waitFor = ((event, timeout) => {
|
|
27
23
|
return this.wait(event, new AbortController().signal, timeout);
|
|
28
24
|
});
|
|
29
|
-
events = ((
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
yield await wait(event, controller.signal, 86_400_000);
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
if (!controller.signal.aborted && timeout(error))
|
|
42
|
-
continue;
|
|
43
|
-
throw error;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
finally {
|
|
48
|
-
options.signal?.removeEventListener("abort", abort);
|
|
49
|
-
controller.abort();
|
|
25
|
+
events = ((eventOrOptions = {}, namedOptions = {}) => {
|
|
26
|
+
if (typeof eventOrOptions === "string") {
|
|
27
|
+
return stream((subscriber, impossible) => this.listen(eventOrOptions, subscriber, impossible), namedOptions);
|
|
28
|
+
}
|
|
29
|
+
return stream((subscriber, impossible) => {
|
|
30
|
+
if (this.names.length) {
|
|
31
|
+
const stops = this.names.map(event => this.listen(event, message => subscriber({ event, message }), impossible));
|
|
32
|
+
return () => stops.forEach(stop => stop());
|
|
50
33
|
}
|
|
51
|
-
|
|
34
|
+
return this.listen(null, value => {
|
|
35
|
+
const capture = value;
|
|
36
|
+
if (typeof capture.event === "string")
|
|
37
|
+
subscriber({ event: capture.event, message: capture.payload });
|
|
38
|
+
}, impossible);
|
|
39
|
+
}, eventOrOptions);
|
|
52
40
|
});
|
|
53
|
-
|
|
54
|
-
if (this.names.length) {
|
|
55
|
-
const stops = this.names.map(event => this.subscribe(event, message => observer({ event, message })));
|
|
56
|
-
return () => stops.forEach(stop => stop());
|
|
57
|
-
}
|
|
41
|
+
listen(event, subscriber, impossible) {
|
|
58
42
|
const controller = new AbortController();
|
|
59
43
|
void (async () => {
|
|
60
44
|
while (!controller.signal.aborted) {
|
|
61
45
|
try {
|
|
62
|
-
|
|
63
|
-
observer({ event: capture.event, message: capture.payload });
|
|
46
|
+
subscriber(await this.wait(event, controller.signal, 86_400_000));
|
|
64
47
|
}
|
|
65
48
|
catch (error) {
|
|
66
49
|
if (!controller.signal.aborted && timeout(error))
|
|
67
50
|
continue;
|
|
68
51
|
if (!controller.signal.aborted)
|
|
69
|
-
|
|
52
|
+
impossible?.(failure(error));
|
|
53
|
+
controller.abort();
|
|
70
54
|
}
|
|
71
55
|
}
|
|
72
56
|
})();
|
|
73
57
|
return () => controller.abort();
|
|
74
|
-
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function stream(register, options = {}) {
|
|
61
|
+
const capacity = options.capacity ?? 64;
|
|
62
|
+
if (capacity !== Infinity && (!Number.isInteger(capacity) || capacity < 0)) {
|
|
63
|
+
throw new Error("An event queue capacity must be a non-negative integer or Infinity");
|
|
64
|
+
}
|
|
65
|
+
return (async function* () {
|
|
66
|
+
const queue = [];
|
|
67
|
+
let ended = false;
|
|
68
|
+
let failure = null;
|
|
69
|
+
let wake = null;
|
|
70
|
+
const stop = register(message => {
|
|
71
|
+
if (ended || failure)
|
|
72
|
+
return;
|
|
73
|
+
if (queue.length >= capacity)
|
|
74
|
+
failure = new Error(`Event queue exceeded its capacity of ${capacity}`);
|
|
75
|
+
else
|
|
76
|
+
queue.push(message);
|
|
77
|
+
wake?.();
|
|
78
|
+
wake = null;
|
|
79
|
+
}, error => {
|
|
80
|
+
if (ended || failure)
|
|
81
|
+
return;
|
|
82
|
+
failure = error;
|
|
83
|
+
wake?.();
|
|
84
|
+
wake = null;
|
|
85
|
+
});
|
|
86
|
+
const abort = () => {
|
|
87
|
+
ended = true;
|
|
88
|
+
wake?.();
|
|
89
|
+
wake = null;
|
|
90
|
+
};
|
|
91
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
92
|
+
if (options.signal?.aborted)
|
|
93
|
+
abort();
|
|
94
|
+
try {
|
|
95
|
+
while (!ended) {
|
|
96
|
+
if (queue.length) {
|
|
97
|
+
yield queue.shift();
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (failure)
|
|
101
|
+
throw failure;
|
|
102
|
+
await new Promise(resolve => { wake = resolve; });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
ended = true;
|
|
107
|
+
stop();
|
|
108
|
+
options.signal?.removeEventListener("abort", abort);
|
|
109
|
+
}
|
|
110
|
+
})();
|
|
75
111
|
}
|
|
76
112
|
function timeout(error) {
|
|
77
113
|
return error instanceof Error && /timeout|timed out/i.test(error.message);
|
|
78
114
|
}
|
|
115
|
+
function failure(error) {
|
|
116
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
117
|
+
}
|
package/dist/main.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { gatewayAddress } from "./address.js";
|
|
2
2
|
export { Client, ClientService, Endpoint, Process, Program, Server, ServerService, System, type ProgramProcessRunEvent, type ProgramProcessRunOptions } from "./system.js";
|
|
3
|
-
export { Service, type
|
|
3
|
+
export { Service, type ServiceKey, type ServiceLifecycle, type ServiceLifecycleEvents } from "@phreshos/core";
|
|
4
4
|
export { resolveHome } from "./home.js";
|
|
5
5
|
export { Project, type Manifest, type PackedProject, type ProjectMode, type ProjectOptions, type ProjectRunOptions } from "./project.js";
|
package/dist/system.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { Client as CoreClient, ClientService as CoreClientService, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, ServerService as CoreServerService, type
|
|
2
|
-
import Events from "./events.js";
|
|
3
|
-
import { type TransportEvent } from "./transport.js";
|
|
1
|
+
import { Client as CoreClient, ClientService as CoreClientService, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, ServerService as CoreServerService, type ProgramDefinition, type ProgramCommandChunk, type ServiceKey, type System as CoreSystem, type SystemClientEntity, type SystemEndpointEntity, type SystemProcessEntity, type SystemProcess, type SystemProgram, type SystemProgramEntity, type SystemServerEntity, type SystemUploads, type WritableAppearance } from "@phreshos/core";
|
|
4
2
|
export type ProgramProcessRunOptions = Readonly<{
|
|
5
3
|
signal?: AbortSignal;
|
|
6
4
|
}>;
|
|
@@ -14,29 +12,13 @@ export type ProgramProcessRunEvent = Readonly<{
|
|
|
14
12
|
process: SystemProcessEntity;
|
|
15
13
|
exit: import("@phreshos/core").Exit;
|
|
16
14
|
}>;
|
|
17
|
-
interface SystemTransport {
|
|
18
|
-
control(request: object, signal?: AbortSignal): Promise<unknown>;
|
|
19
|
-
api(request: object, signal?: AbortSignal): Promise<unknown>;
|
|
20
|
-
lifecycle(request: object, signal?: AbortSignal): AsyncGenerator<TransportEvent, void, void>;
|
|
21
|
-
}
|
|
22
|
-
declare const ProgramBase: new () => object;
|
|
23
|
-
declare const ProcessBase: new () => object;
|
|
24
|
-
declare const ServerBase: new () => object;
|
|
25
|
-
declare const ClientBase: new () => object;
|
|
26
15
|
/** One connected owner-local implementation of the shared System contract. */
|
|
27
16
|
export declare class System implements CoreSystem {
|
|
28
|
-
private readonly connection;
|
|
29
|
-
readonly home: string;
|
|
30
|
-
readonly address: string;
|
|
31
17
|
readonly storage: import("@phreshos/core").Storage;
|
|
32
18
|
readonly appearance: WritableAppearance;
|
|
33
19
|
readonly program: SystemProgram;
|
|
34
20
|
readonly process: SystemProcess;
|
|
35
21
|
readonly uploads: SystemUploads;
|
|
36
|
-
readonly transport: SystemTransport;
|
|
37
|
-
private closed;
|
|
38
|
-
private readonly lifetime;
|
|
39
|
-
private readonly handles;
|
|
40
22
|
private constructor();
|
|
41
23
|
/** Connect to the System selected by argument, environment, or owner default. */
|
|
42
24
|
static connect(home?: string): Promise<System>;
|
|
@@ -50,148 +32,6 @@ export declare class System implements CoreSystem {
|
|
|
50
32
|
service<EventsMap extends object = {}>(key: ServiceKey & {
|
|
51
33
|
endpoint: "client";
|
|
52
34
|
}): ClientService<EventsMap>;
|
|
53
|
-
programHandle(snapshot: ProgramSnapshot): ProgramHandle;
|
|
54
|
-
processHandle(snapshot: ProcessSnapshot): ProcessHandle;
|
|
55
|
-
private signal;
|
|
56
|
-
private requireConnected;
|
|
57
|
-
}
|
|
58
|
-
interface ProgramHandle extends SystemProgramEntity {
|
|
59
|
-
}
|
|
60
|
-
declare class ProgramHandle extends ProgramBase {
|
|
61
|
-
private readonly system;
|
|
62
|
-
private readonly reference;
|
|
63
|
-
readonly identity: string;
|
|
64
|
-
readonly process: ProgramProcesses;
|
|
65
|
-
readonly startup: ProgramStartup;
|
|
66
|
-
private snapshot;
|
|
67
|
-
constructor(system: System, snapshot: ProgramSnapshot);
|
|
68
|
-
get name(): string;
|
|
69
|
-
get version(): string | null;
|
|
70
|
-
get description(): string | null;
|
|
71
|
-
get hasAgent(): boolean;
|
|
72
|
-
get server(): EndpointDeclaration | null;
|
|
73
|
-
get client(): ClientDeclaration | null;
|
|
74
|
-
update(snapshot: ProgramSnapshot): void;
|
|
75
|
-
agent(): Promise<string | null>;
|
|
76
|
-
installed(): Promise<boolean>;
|
|
77
|
-
install(): AsyncGenerator<Readonly<{
|
|
78
|
-
stream: "stdout" | "stderr";
|
|
79
|
-
text: string;
|
|
80
|
-
}>, void, void>;
|
|
81
|
-
uninstall(everything?: boolean): AsyncGenerator<Readonly<{
|
|
82
|
-
stream: "stdout" | "stderr";
|
|
83
|
-
text: string;
|
|
84
|
-
}>, void, void>;
|
|
85
|
-
forget(): Promise<void>;
|
|
86
|
-
address(): Readonly<{
|
|
87
|
-
identity: string;
|
|
88
|
-
reference: string;
|
|
89
|
-
}>;
|
|
90
|
-
}
|
|
91
|
-
declare class ProgramStartup {
|
|
92
|
-
private readonly system;
|
|
93
|
-
private readonly program;
|
|
94
|
-
constructor(system: System, program: ProgramHandle);
|
|
95
|
-
get(): Promise<Readonly<{
|
|
96
|
-
name?: string;
|
|
97
|
-
server?: boolean;
|
|
98
|
-
client?: boolean | LaunchClient;
|
|
99
|
-
options?: Readonly<Record<string, string>>;
|
|
100
|
-
}> | null>;
|
|
101
|
-
enable(launch?: Launch): Promise<void>;
|
|
102
|
-
disable(): Promise<void>;
|
|
103
|
-
private change;
|
|
104
|
-
}
|
|
105
|
-
declare class ProgramProcesses extends Events<SystemProgramProcessEvents> {
|
|
106
|
-
private readonly system;
|
|
107
|
-
private readonly program;
|
|
108
|
-
constructor(system: System, program: ProgramHandle);
|
|
109
|
-
list(): Promise<ProcessHandle[]>;
|
|
110
|
-
first(): Promise<ProcessHandle | null>;
|
|
111
|
-
last(): Promise<ProcessHandle | null>;
|
|
112
|
-
find(identityOrName: string): Promise<ProcessHandle | null>;
|
|
113
|
-
create(launch?: Launch): Promise<ProcessHandle>;
|
|
114
|
-
run(launch?: Launch, options?: ProgramProcessRunOptions): AsyncGenerator<ProgramProcessRunEvent, void, void>;
|
|
115
|
-
findOrCreate(launch: Launch & {
|
|
116
|
-
name: string;
|
|
117
|
-
}): Promise<ProcessHandle>;
|
|
118
|
-
exitAll(): Promise<string[]>;
|
|
119
|
-
private createExact;
|
|
120
|
-
}
|
|
121
|
-
interface ProcessHandle extends SystemProcessEntity {
|
|
122
|
-
}
|
|
123
|
-
declare class ProcessHandle extends ProcessBase {
|
|
124
|
-
private readonly system;
|
|
125
|
-
private readonly snapshot;
|
|
126
|
-
readonly identity: string;
|
|
127
|
-
readonly name: string | null;
|
|
128
|
-
readonly startedAt: Date;
|
|
129
|
-
readonly server: ServerEndpoint;
|
|
130
|
-
readonly client: ClientEndpoint;
|
|
131
|
-
constructor(system: System, snapshot: ProcessSnapshot);
|
|
132
|
-
program(): ProgramHandle;
|
|
133
|
-
exit(): Promise<void>;
|
|
134
|
-
exited(): Promise<boolean>;
|
|
135
|
-
}
|
|
136
|
-
interface ServerEndpoint extends SystemServerEntity {
|
|
137
|
-
}
|
|
138
|
-
declare class ServerEndpoint extends ServerBase {
|
|
139
|
-
private readonly system;
|
|
140
|
-
private readonly owner;
|
|
141
|
-
readonly endpoint: "server";
|
|
142
|
-
private readonly base;
|
|
143
|
-
constructor(system: System, owner: ProcessHandle);
|
|
144
|
-
process(): Promise<ProcessHandle>;
|
|
145
|
-
exists(): Promise<boolean>;
|
|
146
|
-
start(): Promise<void>;
|
|
147
|
-
stop(): Promise<void>;
|
|
148
|
-
publish(event: string, payload?: unknown): void;
|
|
149
|
-
ask<Answer = unknown>(event: string, payload?: unknown): Promise<Answer>;
|
|
150
|
-
timeout(milliseconds: number): {
|
|
151
|
-
ask: <Answer = unknown>(event: string, payload?: unknown) => Promise<Answer>;
|
|
152
|
-
};
|
|
153
|
-
waitReady(timeout?: number): Promise<void>;
|
|
154
|
-
service<EventsMap extends object = {}>(): Promise<ServerService<EventsMap> | null>;
|
|
155
|
-
}
|
|
156
|
-
interface ClientEndpoint extends SystemClientEntity {
|
|
157
|
-
}
|
|
158
|
-
declare class ClientEndpoint extends ClientBase {
|
|
159
|
-
readonly endpoint: "client";
|
|
160
|
-
readonly window: SystemWindow;
|
|
161
|
-
private readonly base;
|
|
162
|
-
constructor(system: System, owner: ProcessHandle);
|
|
163
|
-
process(): Promise<ProcessHandle>;
|
|
164
|
-
exists(): Promise<boolean>;
|
|
165
|
-
start(overrides?: LaunchClient): Promise<void>;
|
|
166
|
-
stop(): Promise<void>;
|
|
167
|
-
publish(event: string, payload?: unknown): void;
|
|
168
|
-
service<EventsMap extends object = {}>(): Promise<ClientService<EventsMap> | null>;
|
|
169
|
-
}
|
|
170
|
-
declare class SystemWindow extends Events<WindowEvents> implements Window {
|
|
171
|
-
private readonly system;
|
|
172
|
-
private readonly process;
|
|
173
|
-
constructor(system: System, process: ProcessHandle);
|
|
174
|
-
title(): Promise<string>;
|
|
175
|
-
position(): Promise<Readonly<{
|
|
176
|
-
x: import("@phreshos/core").Value;
|
|
177
|
-
y: import("@phreshos/core").Value;
|
|
178
|
-
}>>;
|
|
179
|
-
size(): Promise<Readonly<{
|
|
180
|
-
width: import("@phreshos/core").Value;
|
|
181
|
-
height: import("@phreshos/core").Value;
|
|
182
|
-
}>>;
|
|
183
|
-
minimized(): Promise<boolean>;
|
|
184
|
-
front(): Promise<boolean>;
|
|
185
|
-
layer(): Promise<import("@phreshos/core").Layer>;
|
|
186
|
-
location(): Promise<string>;
|
|
187
|
-
move(position: Position): Promise<void>;
|
|
188
|
-
resize(size: Size): Promise<void>;
|
|
189
|
-
setGeometry(geometry: WindowGeometry): Promise<void>;
|
|
190
|
-
minimize(minimized?: boolean): Promise<void>;
|
|
191
|
-
changeTitle(title: string): Promise<void>;
|
|
192
|
-
raise(): Promise<void>;
|
|
193
|
-
private snapshot;
|
|
194
|
-
private change;
|
|
195
35
|
}
|
|
196
36
|
/** Node-SDK handle for a Service provided by a Server Endpoint. */
|
|
197
37
|
export declare class ServerService<EventsMap extends object = {}> extends CoreServerService<EventsMap> {
|
|
@@ -201,35 +41,6 @@ export declare class ServerService<EventsMap extends object = {}> extends CoreSe
|
|
|
201
41
|
export declare class ClientService<EventsMap extends object = {}> extends CoreClientService<EventsMap> {
|
|
202
42
|
protected constructor();
|
|
203
43
|
}
|
|
204
|
-
interface ProgramSnapshot {
|
|
205
|
-
reference: string;
|
|
206
|
-
identity: string;
|
|
207
|
-
name: string;
|
|
208
|
-
version: string | null;
|
|
209
|
-
description: string | null;
|
|
210
|
-
installed?: boolean;
|
|
211
|
-
hasAgent: boolean;
|
|
212
|
-
server: {
|
|
213
|
-
start: boolean;
|
|
214
|
-
} | null;
|
|
215
|
-
client: ClientDeclaration | null;
|
|
216
|
-
}
|
|
217
|
-
interface ProcessSnapshot {
|
|
218
|
-
reference: string;
|
|
219
|
-
identity: string;
|
|
220
|
-
name: string | null;
|
|
221
|
-
program: string;
|
|
222
|
-
programSnapshot?: ProgramSnapshot;
|
|
223
|
-
startedAt: string;
|
|
224
|
-
server: {
|
|
225
|
-
declared: boolean;
|
|
226
|
-
running: boolean;
|
|
227
|
-
};
|
|
228
|
-
client: {
|
|
229
|
-
declared: boolean;
|
|
230
|
-
running: boolean;
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
44
|
export type Program = SystemProgramEntity;
|
|
234
45
|
export declare const Program: typeof CoreProgram;
|
|
235
46
|
export type Process = SystemProcessEntity;
|
|
@@ -240,4 +51,3 @@ export type Server<EventsMap extends object = {}> = SystemServerEntity<EventsMap
|
|
|
240
51
|
export declare const Server: typeof CoreServer;
|
|
241
52
|
export type Client<EventsMap extends object = {}> = SystemClientEntity<EventsMap>;
|
|
242
53
|
export declare const Client: typeof CoreClient;
|
|
243
|
-
export {};
|
package/dist/system.js
CHANGED
|
@@ -7,88 +7,94 @@ import { resolveHome } from "./home.js";
|
|
|
7
7
|
import { filesystemStorage } from "./storage.js";
|
|
8
8
|
import { openConnection, request, streamProgram } from "./transport.js";
|
|
9
9
|
import Uploads from "./uploads.js";
|
|
10
|
+
const systems = new WeakMap();
|
|
10
11
|
const ProgramBase = CoreProgram;
|
|
11
12
|
const ProcessBase = CoreProcess;
|
|
12
13
|
const ServerBase = CoreServer;
|
|
13
14
|
const ClientBase = CoreClient;
|
|
14
15
|
/** One connected owner-local implementation of the shared System contract. */
|
|
15
16
|
export class System {
|
|
16
|
-
connection;
|
|
17
|
-
home;
|
|
18
|
-
address;
|
|
19
17
|
storage = filesystemStorage(homedir(), "the native home directory");
|
|
20
18
|
appearance;
|
|
21
19
|
program;
|
|
22
20
|
process;
|
|
23
21
|
uploads;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
this.address = address;
|
|
32
|
-
this.transport = {
|
|
33
|
-
control: (value, signal) => request(address, "system", value, this.signal(signal)),
|
|
34
|
-
api: (value, signal) => request(address, "api", value, this.signal(signal)),
|
|
35
|
-
lifecycle: (value, signal) => streamProgram(address, value, this.signal(signal))
|
|
22
|
+
constructor(address, connection) {
|
|
23
|
+
const lifetime = new AbortController();
|
|
24
|
+
const handles = new HandleRegistry();
|
|
25
|
+
const transport = {
|
|
26
|
+
control: (value, signal) => request(address, "system", value, connectedSignal(this, signal)),
|
|
27
|
+
api: (value, signal) => request(address, "api", value, connectedSignal(this, signal)),
|
|
28
|
+
lifecycle: (value, signal) => streamProgram(address, value, connectedSignal(this, signal))
|
|
36
29
|
};
|
|
37
|
-
this
|
|
30
|
+
systems.set(this, { address, connection, handles, lifetime, transport, closed: false });
|
|
31
|
+
this.appearance = new SystemAppearance(transport);
|
|
38
32
|
this.program = new ProgramRegistry(this);
|
|
39
33
|
this.process = new ProcessRegistry(this);
|
|
40
|
-
this.uploads = new Uploads(value =>
|
|
34
|
+
this.uploads = new Uploads(value => transport.api(value));
|
|
41
35
|
}
|
|
42
36
|
/** Connect to the System selected by argument, environment, or owner default. */
|
|
43
37
|
static async connect(home) {
|
|
44
38
|
const resolved = resolveHome(home);
|
|
45
39
|
const address = gatewayAddress(resolved);
|
|
46
|
-
return new System(
|
|
40
|
+
return new System(address, await openConnection(address));
|
|
47
41
|
}
|
|
48
42
|
/** Atomically replace one runtime Program without touching its installed form. */
|
|
49
43
|
async forceCreateProgram(source) {
|
|
50
|
-
|
|
51
|
-
for await (const event of this.
|
|
44
|
+
requireConnected(this);
|
|
45
|
+
for await (const event of transport(this).lifecycle({ word: "force-create", program: source })) {
|
|
52
46
|
if (event.event === "created")
|
|
53
|
-
return
|
|
47
|
+
return programHandle(this, required(event.program));
|
|
54
48
|
}
|
|
55
49
|
throw new Error("The System did not confirm the created Program");
|
|
56
50
|
}
|
|
57
51
|
/** Close this owner connection and abort every attached operation it owns. */
|
|
58
52
|
async disconnect() {
|
|
59
|
-
|
|
53
|
+
const state = systemState(this);
|
|
54
|
+
if (state.closed)
|
|
60
55
|
return;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
state.closed = true;
|
|
57
|
+
state.lifetime.abort(new Error("This System connection is closed"));
|
|
58
|
+
state.connection.destroy();
|
|
59
|
+
state.handles.clear();
|
|
65
60
|
}
|
|
66
61
|
service(key) {
|
|
67
|
-
|
|
62
|
+
requireConnected(this);
|
|
68
63
|
if (!isServiceKey(key))
|
|
69
64
|
throw new Error("A complete service key is required");
|
|
70
65
|
const normalized = Object.freeze({ program: key.program, endpoint: key.endpoint, name: key.name });
|
|
71
66
|
const identity = JSON.stringify([normalized.program, normalized.endpoint, normalized.name]);
|
|
72
|
-
return this.handles.obtain(`service:${identity}`, () => normalized.endpoint === "server"
|
|
67
|
+
return systemState(this).handles.obtain(`service:${identity}`, () => normalized.endpoint === "server"
|
|
73
68
|
? new ServerServiceHandle(this, normalized)
|
|
74
69
|
: new ClientServiceHandle(this, normalized));
|
|
75
70
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
71
|
+
}
|
|
72
|
+
function systemState(system) {
|
|
73
|
+
const state = systems.get(system);
|
|
74
|
+
if (!state)
|
|
75
|
+
throw new Error("Unknown System connection");
|
|
76
|
+
return state;
|
|
77
|
+
}
|
|
78
|
+
function requireConnected(system) {
|
|
79
|
+
if (systemState(system).closed)
|
|
80
|
+
throw new Error("This System connection is closed");
|
|
81
|
+
}
|
|
82
|
+
function connectedSignal(system, signal) {
|
|
83
|
+
const lifetime = systemState(system).lifetime.signal;
|
|
84
|
+
requireConnected(system);
|
|
85
|
+
return signal ? AbortSignal.any([signal, lifetime]) : lifetime;
|
|
86
|
+
}
|
|
87
|
+
function transport(system) {
|
|
88
|
+
requireConnected(system);
|
|
89
|
+
return systemState(system).transport;
|
|
90
|
+
}
|
|
91
|
+
function programHandle(system, snapshot) {
|
|
92
|
+
const handle = systemState(system).handles.obtain(`program:${snapshot.reference}`, () => new ProgramHandle(system, snapshot));
|
|
93
|
+
handle.update(snapshot);
|
|
94
|
+
return handle;
|
|
95
|
+
}
|
|
96
|
+
function processHandle(system, snapshot) {
|
|
97
|
+
return systemState(system).handles.obtain(`process:${snapshot.reference}`, () => new ProcessHandle(system, snapshot));
|
|
92
98
|
}
|
|
93
99
|
class SystemAppearance extends Events {
|
|
94
100
|
transport;
|
|
@@ -106,7 +112,7 @@ class SystemAppearance extends Events {
|
|
|
106
112
|
class ProgramRegistry extends Events {
|
|
107
113
|
system;
|
|
108
114
|
constructor(system) {
|
|
109
|
-
super(["create", "forget", "install", "uninstall"], (event, signal, timeout) => (system.
|
|
115
|
+
super(["create", "forget", "install", "uninstall"], (event, signal, timeout) => (transport(system).control({ capability: "program", operation: "wait", input: { event, timeout } }, signal)
|
|
110
116
|
.then(value => this.event(value))));
|
|
111
117
|
this.system = system;
|
|
112
118
|
}
|
|
@@ -114,12 +120,12 @@ class ProgramRegistry extends Events {
|
|
|
114
120
|
const programs = [];
|
|
115
121
|
let offset = 0;
|
|
116
122
|
while (true) {
|
|
117
|
-
const page = await this.system.
|
|
123
|
+
const page = await transport(this.system).control({
|
|
118
124
|
capability: "program",
|
|
119
125
|
operation: "list",
|
|
120
126
|
input: { installedOnly: onlyInstalled, limit: 100, offset }
|
|
121
127
|
});
|
|
122
|
-
programs.push(...page.data.map(snapshot => this.system
|
|
128
|
+
programs.push(...page.data.map(snapshot => programHandle(this.system, snapshot)));
|
|
123
129
|
offset += page.data.length;
|
|
124
130
|
if (!page.truncated || !page.data.length)
|
|
125
131
|
return programs;
|
|
@@ -127,8 +133,8 @@ class ProgramRegistry extends Events {
|
|
|
127
133
|
}
|
|
128
134
|
async find(identity) {
|
|
129
135
|
try {
|
|
130
|
-
const snapshot = await this.system.
|
|
131
|
-
return this.system
|
|
136
|
+
const snapshot = await transport(this.system).control({ capability: "program", operation: "inspect", input: { program: identity } });
|
|
137
|
+
return programHandle(this.system, snapshot);
|
|
132
138
|
}
|
|
133
139
|
catch (error) {
|
|
134
140
|
if (unknown(error, "Program"))
|
|
@@ -137,9 +143,9 @@ class ProgramRegistry extends Events {
|
|
|
137
143
|
}
|
|
138
144
|
}
|
|
139
145
|
async create(source) {
|
|
140
|
-
for await (const event of this.system.
|
|
146
|
+
for await (const event of transport(this.system).lifecycle({ word: "create", program: source })) {
|
|
141
147
|
if (event.event === "created")
|
|
142
|
-
return this.system
|
|
148
|
+
return programHandle(this.system, required(event.program));
|
|
143
149
|
}
|
|
144
150
|
throw new Error("The System did not confirm the created Program");
|
|
145
151
|
}
|
|
@@ -147,9 +153,9 @@ class ProgramRegistry extends Events {
|
|
|
147
153
|
const waited = value;
|
|
148
154
|
if (waited.event === "uninstall") {
|
|
149
155
|
const payload = waited.payload;
|
|
150
|
-
return { program: this.system
|
|
156
|
+
return { program: programHandle(this.system, required(payload.program)), everythingRemoved: payload.everythingRemoved === true };
|
|
151
157
|
}
|
|
152
|
-
return this.system
|
|
158
|
+
return programHandle(this.system, required(waited.payload));
|
|
153
159
|
}
|
|
154
160
|
}
|
|
155
161
|
class ProgramHandle extends ProgramBase {
|
|
@@ -163,7 +169,7 @@ class ProgramHandle extends ProgramBase {
|
|
|
163
169
|
super();
|
|
164
170
|
this.system = system;
|
|
165
171
|
this.snapshot = snapshot;
|
|
166
|
-
bindEvents(this, new Events(["forget", "uninstall"], (event, signal, timeout) => system.
|
|
172
|
+
bindEvents(this, new Events(["forget", "uninstall"], (event, signal, timeout) => transport(system).control({
|
|
167
173
|
capability: "program", operation: "wait", input: { program: snapshot.identity, event, timeout }
|
|
168
174
|
}, signal).then(value => value.payload)));
|
|
169
175
|
this.reference = snapshot.reference;
|
|
@@ -196,11 +202,11 @@ class ProgramHandle extends ProgramBase {
|
|
|
196
202
|
async agent() {
|
|
197
203
|
if (!this.hasAgent)
|
|
198
204
|
return null;
|
|
199
|
-
const value = await this.system.
|
|
205
|
+
const value = await transport(this.system).control({ capability: "program", operation: "agent", input: { program: this.identity } });
|
|
200
206
|
return typeof value.content === "string" ? value.content : null;
|
|
201
207
|
}
|
|
202
208
|
async installed() {
|
|
203
|
-
for await (const event of this.system.
|
|
209
|
+
for await (const event of transport(this.system).lifecycle({ word: "installed", handle: this.address() })) {
|
|
204
210
|
if (event.event === "installedState")
|
|
205
211
|
return event.installed === true;
|
|
206
212
|
}
|
|
@@ -209,7 +215,7 @@ class ProgramHandle extends ProgramBase {
|
|
|
209
215
|
install() { return command(this.system, { word: "install-existing", handle: this.address() }); }
|
|
210
216
|
uninstall(everything = false) { return command(this.system, { word: "uninstall-existing", handle: this.address(), everything }); }
|
|
211
217
|
async forget() {
|
|
212
|
-
for await (const _event of this.system.
|
|
218
|
+
for await (const _event of transport(this.system).lifecycle({ word: "forget", handle: this.address() })) { /* consume completion */ }
|
|
213
219
|
}
|
|
214
220
|
address() { return Object.freeze({ identity: this.identity, reference: this.reference }); }
|
|
215
221
|
}
|
|
@@ -221,7 +227,7 @@ class ProgramStartup {
|
|
|
221
227
|
this.program = program;
|
|
222
228
|
}
|
|
223
229
|
async get() {
|
|
224
|
-
for await (const event of this.system.
|
|
230
|
+
for await (const event of transport(this.system).lifecycle({
|
|
225
231
|
word: "startup", handle: this.program.address(), operation: "get"
|
|
226
232
|
})) {
|
|
227
233
|
if (event.event === "startup")
|
|
@@ -236,7 +242,7 @@ class ProgramStartup {
|
|
|
236
242
|
await this.change("disable");
|
|
237
243
|
}
|
|
238
244
|
async change(operation, launch) {
|
|
239
|
-
for await (const event of this.system.
|
|
245
|
+
for await (const event of transport(this.system).lifecycle({
|
|
240
246
|
word: "startup", handle: this.program.address(), operation, launch
|
|
241
247
|
})) {
|
|
242
248
|
if (event.event === "startup")
|
|
@@ -249,7 +255,7 @@ class ProgramProcesses extends Events {
|
|
|
249
255
|
system;
|
|
250
256
|
program;
|
|
251
257
|
constructor(system, program) {
|
|
252
|
-
super(["endpointStart", "endpointStop", "create", "exit"], (event, signal, timeout) => system.
|
|
258
|
+
super(["endpointStart", "endpointStop", "create", "exit"], (event, signal, timeout) => transport(system).control({
|
|
253
259
|
capability: "process", operation: "wait", input: { program: program.identity, event, timeout }
|
|
254
260
|
}, signal).then(value => processEvent(system, value)));
|
|
255
261
|
this.system = system;
|
|
@@ -265,13 +271,13 @@ class ProgramProcesses extends Events {
|
|
|
265
271
|
create(launch = {}) { return this.createExact("create-process", launch); }
|
|
266
272
|
async *run(launch = {}, options = {}) {
|
|
267
273
|
let process = null;
|
|
268
|
-
for await (const event of this.system.
|
|
274
|
+
for await (const event of transport(this.system).lifecycle({
|
|
269
275
|
word: "run-process",
|
|
270
276
|
handle: this.program.address(),
|
|
271
277
|
launch
|
|
272
278
|
}, options.signal)) {
|
|
273
279
|
if (event.event === "started") {
|
|
274
|
-
process = this.system
|
|
280
|
+
process = processHandle(this.system, required(event.process));
|
|
275
281
|
yield Object.freeze({ event: "started", process });
|
|
276
282
|
}
|
|
277
283
|
else if (event.event === "output") {
|
|
@@ -312,9 +318,9 @@ class ProgramProcesses extends Events {
|
|
|
312
318
|
return processes.map(process => process.identity);
|
|
313
319
|
}
|
|
314
320
|
async createExact(word, launch) {
|
|
315
|
-
for await (const event of this.system.
|
|
321
|
+
for await (const event of transport(this.system).lifecycle({ word, handle: this.program.address(), launch })) {
|
|
316
322
|
if (event.event === "createdProcess")
|
|
317
|
-
return this.system
|
|
323
|
+
return processHandle(this.system, required(event.process));
|
|
318
324
|
}
|
|
319
325
|
throw new Error("The System did not confirm the created Process");
|
|
320
326
|
}
|
|
@@ -322,7 +328,7 @@ class ProgramProcesses extends Events {
|
|
|
322
328
|
class ProcessRegistry extends Events {
|
|
323
329
|
system;
|
|
324
330
|
constructor(system) {
|
|
325
|
-
super(["endpointStart", "endpointStop", "create", "exit"], (event, signal, timeout) => system.
|
|
331
|
+
super(["endpointStart", "endpointStop", "create", "exit"], (event, signal, timeout) => transport(system).control({
|
|
326
332
|
capability: "process", operation: "wait", input: { event, timeout }
|
|
327
333
|
}, signal).then(value => processEvent(system, value)));
|
|
328
334
|
this.system = system;
|
|
@@ -330,8 +336,8 @@ class ProcessRegistry extends Events {
|
|
|
330
336
|
list() { return listProcesses(this.system); }
|
|
331
337
|
async find(identity) {
|
|
332
338
|
try {
|
|
333
|
-
const snapshot = await this.system.
|
|
334
|
-
return this.system
|
|
339
|
+
const snapshot = await transport(this.system).control({ capability: "process", operation: "inspect", input: { process: identity } });
|
|
340
|
+
return processHandle(this.system, snapshot);
|
|
335
341
|
}
|
|
336
342
|
catch (error) {
|
|
337
343
|
if (unknown(error, "Process"))
|
|
@@ -352,7 +358,7 @@ class ProcessHandle extends ProcessBase {
|
|
|
352
358
|
super();
|
|
353
359
|
this.system = system;
|
|
354
360
|
this.snapshot = snapshot;
|
|
355
|
-
bindEvents(this, new Events(["endpointStart", "endpointStop", "exit"], (event, signal, timeout) => system.
|
|
361
|
+
bindEvents(this, new Events(["endpointStart", "endpointStop", "exit"], (event, signal, timeout) => transport(system).control({
|
|
356
362
|
capability: "process", operation: "wait", input: { process: snapshot.identity, event, timeout }
|
|
357
363
|
}, signal).then(value => processEvent(system, value))));
|
|
358
364
|
this.identity = snapshot.identity;
|
|
@@ -361,9 +367,9 @@ class ProcessHandle extends ProcessBase {
|
|
|
361
367
|
this.server = new ServerEndpoint(system, this);
|
|
362
368
|
this.client = new ClientEndpoint(system, this);
|
|
363
369
|
}
|
|
364
|
-
program() { return this.system
|
|
370
|
+
program() { return programHandle(this.system, required(this.snapshot.programSnapshot, this.snapshot.program)); }
|
|
365
371
|
async exit() {
|
|
366
|
-
await this.system.
|
|
372
|
+
await transport(this.system).control({ capability: "process", operation: "exit", input: { process: this.identity } });
|
|
367
373
|
}
|
|
368
374
|
async exited() { return await this.system.process.find(this.identity) === null; }
|
|
369
375
|
}
|
|
@@ -371,15 +377,19 @@ class EndpointOperations extends Events {
|
|
|
371
377
|
system;
|
|
372
378
|
owner;
|
|
373
379
|
endpoint;
|
|
380
|
+
lifecycle;
|
|
374
381
|
constructor(system, owner, endpoint) {
|
|
375
382
|
super([], (event, signal, timeout) => event === null
|
|
376
|
-
? system.
|
|
377
|
-
: system.
|
|
383
|
+
? transport(system).api({ capability: "endpoint", operation: "wait", process: owner.identity, endpoint, event, timeout }, signal)
|
|
384
|
+
: transport(system).control({
|
|
378
385
|
capability: "endpoint", operation: "wait", input: { process: owner.identity, endpoint, event, timeout }
|
|
379
386
|
}, signal).then(value => value.payload));
|
|
380
387
|
this.system = system;
|
|
381
388
|
this.owner = owner;
|
|
382
389
|
this.endpoint = endpoint;
|
|
390
|
+
this.lifecycle = new Events(["start", "stop"], (event, signal, timeout) => {
|
|
391
|
+
return waitEndpointLifecycle(system, owner, endpoint, event, signal, timeout);
|
|
392
|
+
});
|
|
383
393
|
}
|
|
384
394
|
process() { return Promise.resolve(this.owner); }
|
|
385
395
|
async exists() {
|
|
@@ -389,21 +399,21 @@ class EndpointOperations extends Events {
|
|
|
389
399
|
async start(client) { await this.operation("start", client); }
|
|
390
400
|
async stop() { await this.operation("stop"); }
|
|
391
401
|
async service() {
|
|
392
|
-
const key = await this.system.
|
|
402
|
+
const key = await transport(this.system).api({ capability: "endpoint", operation: "service", process: this.owner.identity, endpoint: this.endpoint });
|
|
393
403
|
return key ? this.system.service(key) : null;
|
|
394
404
|
}
|
|
395
405
|
publish(event, payload) {
|
|
396
|
-
void this.system.
|
|
406
|
+
void transport(this.system).control({ capability: "endpoint", operation: "publish", input: {
|
|
397
407
|
process: this.owner.identity, endpoint: this.endpoint, event, payload
|
|
398
408
|
} });
|
|
399
409
|
}
|
|
400
410
|
inspect() {
|
|
401
|
-
return this.system.
|
|
411
|
+
return transport(this.system).control({ capability: "endpoint", operation: "inspect", input: {
|
|
402
412
|
process: this.owner.identity, endpoint: this.endpoint
|
|
403
413
|
} });
|
|
404
414
|
}
|
|
405
415
|
async operation(operation, client) {
|
|
406
|
-
await this.system.
|
|
416
|
+
await transport(this.system).control({ capability: "endpoint", operation, input: {
|
|
407
417
|
process: this.owner.identity, endpoint: this.endpoint, ...(client ? { client } : {})
|
|
408
418
|
} });
|
|
409
419
|
}
|
|
@@ -412,12 +422,14 @@ class ServerEndpoint extends ServerBase {
|
|
|
412
422
|
system;
|
|
413
423
|
owner;
|
|
414
424
|
endpoint = "server";
|
|
425
|
+
lifecycle;
|
|
415
426
|
base;
|
|
416
427
|
constructor(system, owner) {
|
|
417
428
|
super();
|
|
418
429
|
this.system = system;
|
|
419
430
|
this.owner = owner;
|
|
420
431
|
this.base = new EndpointOperations(system, owner, "server");
|
|
432
|
+
this.lifecycle = this.base.lifecycle;
|
|
421
433
|
bindEvents(this, this.base);
|
|
422
434
|
}
|
|
423
435
|
process() { return this.base.process(); }
|
|
@@ -426,17 +438,17 @@ class ServerEndpoint extends ServerBase {
|
|
|
426
438
|
stop() { return this.base.stop(); }
|
|
427
439
|
publish(event, payload) { return this.base.publish(event, payload); }
|
|
428
440
|
async ask(event, payload) {
|
|
429
|
-
return await this.system.
|
|
441
|
+
return await transport(this.system).control({ capability: "endpoint", operation: "ask", input: {
|
|
430
442
|
process: this.owner.identity, endpoint: "server", event, payload
|
|
431
443
|
} });
|
|
432
444
|
}
|
|
433
445
|
timeout(milliseconds) {
|
|
434
|
-
return { ask: (event, payload) => this.system.
|
|
446
|
+
return { ask: (event, payload) => transport(this.system).control({
|
|
435
447
|
capability: "endpoint", operation: "ask", input: { process: this.owner.identity, endpoint: "server", event, payload, timeout: milliseconds }
|
|
436
448
|
}) };
|
|
437
449
|
}
|
|
438
450
|
async waitReady(timeout) {
|
|
439
|
-
await this.system.
|
|
451
|
+
await transport(this.system).control({ capability: "endpoint", operation: "waitReady", input: { process: this.owner.identity, endpoint: "server", timeout } });
|
|
440
452
|
}
|
|
441
453
|
async service() {
|
|
442
454
|
return await this.base.service();
|
|
@@ -444,11 +456,13 @@ class ServerEndpoint extends ServerBase {
|
|
|
444
456
|
}
|
|
445
457
|
class ClientEndpoint extends ClientBase {
|
|
446
458
|
endpoint = "client";
|
|
459
|
+
lifecycle;
|
|
447
460
|
window;
|
|
448
461
|
base;
|
|
449
462
|
constructor(system, owner) {
|
|
450
463
|
super();
|
|
451
464
|
this.base = new EndpointOperations(system, owner, "client");
|
|
465
|
+
this.lifecycle = this.base.lifecycle;
|
|
452
466
|
bindEvents(this, this.base);
|
|
453
467
|
this.window = new SystemWindow(system, owner);
|
|
454
468
|
}
|
|
@@ -465,7 +479,7 @@ class SystemWindow extends Events {
|
|
|
465
479
|
system;
|
|
466
480
|
process;
|
|
467
481
|
constructor(system, process) {
|
|
468
|
-
super(["move", "resize", "geometry", "minimize", "changeTitle", "front"], (event, signal, timeout) => system.
|
|
482
|
+
super(["move", "resize", "geometry", "minimize", "changeTitle", "front"], (event, signal, timeout) => transport(system).control({
|
|
469
483
|
capability: "window", operation: "wait", input: { process: process.identity, event, timeout }
|
|
470
484
|
}, signal).then(value => value.payload));
|
|
471
485
|
this.system = system;
|
|
@@ -485,44 +499,62 @@ class SystemWindow extends Events {
|
|
|
485
499
|
async changeTitle(title) { await this.change("changeTitle", { title }); }
|
|
486
500
|
async raise() { await this.change("raise", {}); }
|
|
487
501
|
snapshot() {
|
|
488
|
-
return this.system.
|
|
502
|
+
return transport(this.system).control({ capability: "window", operation: "inspect", input: { process: this.process.identity } });
|
|
489
503
|
}
|
|
490
504
|
async change(operation, input) {
|
|
491
|
-
await this.system.
|
|
505
|
+
await transport(this.system).control({ capability: "window", operation, input: { process: this.process.identity, ...input } });
|
|
492
506
|
}
|
|
493
507
|
}
|
|
494
|
-
class ServiceBase
|
|
508
|
+
class ServiceBase {
|
|
495
509
|
system;
|
|
496
510
|
key;
|
|
497
511
|
name;
|
|
512
|
+
lifecycle;
|
|
498
513
|
constructor(system, key) {
|
|
499
|
-
super(["enable", "disable"], (event, signal, timeout) => system.transport.api({
|
|
500
|
-
capability: "service", operation: "wait", scope: "lifecycle", key, event, timeout
|
|
501
|
-
}, signal));
|
|
502
514
|
this.system = system;
|
|
503
515
|
this.key = key;
|
|
516
|
+
this.lifecycle = new Events(["enable", "disable"], (event, signal, timeout) => transport(system).api({
|
|
517
|
+
capability: "service", operation: "wait", scope: "lifecycle", key, event, timeout
|
|
518
|
+
}, signal));
|
|
504
519
|
this.name = key.name;
|
|
505
520
|
}
|
|
506
|
-
async enabled() { return await this.system.
|
|
507
|
-
async waitReady(timeout) { await this.system.
|
|
521
|
+
async enabled() { return await transport(this.system).api({ capability: "service", operation: "enabled", key: this.key }); }
|
|
522
|
+
async waitReady(timeout) { await transport(this.system).api({ capability: "service", operation: "waitReady", key: this.key, timeout }); }
|
|
508
523
|
}
|
|
509
524
|
/** Node-SDK handle for a Service provided by a Server Endpoint. */
|
|
510
525
|
export class ServerService extends CoreServerService {
|
|
511
526
|
constructor() { super(); }
|
|
512
527
|
}
|
|
513
528
|
class ServerServiceHandle extends ServerService {
|
|
529
|
+
system;
|
|
530
|
+
key;
|
|
514
531
|
name;
|
|
515
|
-
|
|
532
|
+
lifecycle;
|
|
516
533
|
base;
|
|
517
534
|
constructor(system, key) {
|
|
518
535
|
super();
|
|
536
|
+
this.system = system;
|
|
537
|
+
this.key = key;
|
|
519
538
|
this.base = new ServiceBase(system, key);
|
|
520
539
|
this.name = key.name;
|
|
521
|
-
this.
|
|
522
|
-
|
|
540
|
+
this.lifecycle = this.base.lifecycle;
|
|
541
|
+
bindEvents(this, new Events([], (event, signal, timeout) => transport(system).api({
|
|
542
|
+
capability: "service", operation: "wait", scope: "events", key, event, timeout
|
|
543
|
+
}, signal)));
|
|
523
544
|
}
|
|
524
545
|
enabled() { return this.base.enabled(); }
|
|
525
546
|
waitReady(timeout) { return this.base.waitReady(timeout); }
|
|
547
|
+
publish = (event, payload) => {
|
|
548
|
+
void transport(this.system).api({ capability: "service", operation: "publish", key: this.key, event, payload });
|
|
549
|
+
};
|
|
550
|
+
async ask(event, payload) {
|
|
551
|
+
return await transport(this.system).api({ capability: "service", operation: "ask", key: this.key, event, payload });
|
|
552
|
+
}
|
|
553
|
+
timeout(milliseconds) {
|
|
554
|
+
return { ask: (event, payload) => transport(this.system).api({
|
|
555
|
+
capability: "service", operation: "ask", key: this.key, event, payload, timeout: milliseconds
|
|
556
|
+
}) };
|
|
557
|
+
}
|
|
526
558
|
}
|
|
527
559
|
/** Node-SDK handle for a Service provided by a Client Endpoint. */
|
|
528
560
|
export class ClientService extends CoreClientService {
|
|
@@ -530,53 +562,33 @@ export class ClientService extends CoreClientService {
|
|
|
530
562
|
}
|
|
531
563
|
class ClientServiceHandle extends ClientService {
|
|
532
564
|
name;
|
|
533
|
-
|
|
565
|
+
lifecycle;
|
|
534
566
|
base;
|
|
535
567
|
constructor(system, key) {
|
|
536
568
|
super();
|
|
537
569
|
this.base = new ServiceBase(system, key);
|
|
538
570
|
this.name = key.name;
|
|
539
|
-
this.
|
|
540
|
-
|
|
571
|
+
this.lifecycle = this.base.lifecycle;
|
|
572
|
+
bindEvents(this, new Events([], (event, signal, timeout) => transport(system).api({
|
|
573
|
+
capability: "service", operation: "wait", scope: "events", key, event, timeout
|
|
574
|
+
}, signal)));
|
|
541
575
|
}
|
|
542
576
|
enabled() { return this.base.enabled(); }
|
|
543
577
|
waitReady(timeout) { return this.base.waitReady(timeout); }
|
|
544
578
|
}
|
|
545
|
-
class ClientServiceChannelHandle extends Events {
|
|
546
|
-
system;
|
|
547
|
-
key;
|
|
548
|
-
constructor(system, key) {
|
|
549
|
-
super([], (event, signal, timeout) => system.transport.api({ capability: "service", operation: "wait", scope: "channel", key, event, timeout }, signal));
|
|
550
|
-
this.system = system;
|
|
551
|
-
this.key = key;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
class ServerServiceChannelHandle extends ClientServiceChannelHandle {
|
|
555
|
-
async ask(event, payload) {
|
|
556
|
-
return await this.system.transport.api({ capability: "service", operation: "ask", key: this.key, event, payload });
|
|
557
|
-
}
|
|
558
|
-
timeout(milliseconds) {
|
|
559
|
-
return { ask: (event, payload) => this.system.transport.api({
|
|
560
|
-
capability: "service", operation: "ask", key: this.key, event, payload, timeout: milliseconds
|
|
561
|
-
}) };
|
|
562
|
-
}
|
|
563
|
-
publish(event, payload) {
|
|
564
|
-
void this.system.transport.api({ capability: "service", operation: "publish", key: this.key, event, payload });
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
579
|
async function listProcesses(system, program) {
|
|
568
580
|
const processes = [];
|
|
569
581
|
let offset = 0;
|
|
570
582
|
while (true) {
|
|
571
|
-
const page = await system.
|
|
572
|
-
processes.push(...page.data.map(snapshot =>
|
|
583
|
+
const page = await transport(system).control({ capability: "process", operation: "list", input: { program, limit: 100, offset } });
|
|
584
|
+
processes.push(...page.data.map(snapshot => processHandle(system, snapshot)));
|
|
573
585
|
offset += page.data.length;
|
|
574
586
|
if (!page.truncated || !page.data.length)
|
|
575
587
|
return processes;
|
|
576
588
|
}
|
|
577
589
|
}
|
|
578
590
|
async function* command(system, request) {
|
|
579
|
-
for await (const event of system.
|
|
591
|
+
for await (const event of transport(system).lifecycle(request)) {
|
|
580
592
|
if (event.event === "output")
|
|
581
593
|
yield {
|
|
582
594
|
stream: event.stream === "stderr" ? "stderr" : "stdout",
|
|
@@ -584,22 +596,38 @@ async function* command(system, request) {
|
|
|
584
596
|
};
|
|
585
597
|
}
|
|
586
598
|
}
|
|
599
|
+
async function waitEndpointLifecycle(system, owner, endpoint, event, signal, timeout = 10_000) {
|
|
600
|
+
if (event !== "start" && event !== "stop")
|
|
601
|
+
throw new Error(`An Endpoint lifecycle has no "${event}" event`);
|
|
602
|
+
const processEventName = event === "start" ? "endpointStart" : "endpointStop";
|
|
603
|
+
const deadline = Date.now() + timeout;
|
|
604
|
+
while (true) {
|
|
605
|
+
const value = await transport(system).control({
|
|
606
|
+
capability: "process",
|
|
607
|
+
operation: "wait",
|
|
608
|
+
input: { process: owner.identity, event: processEventName, timeout: Math.max(0, deadline - Date.now()) }
|
|
609
|
+
}, signal);
|
|
610
|
+
const changed = processEvent(system, value);
|
|
611
|
+
if (changed === owner[endpoint])
|
|
612
|
+
return undefined;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
587
615
|
function processEvent(system, value) {
|
|
588
616
|
const waited = value;
|
|
589
617
|
const payload = waited.payload;
|
|
590
618
|
if (waited.event === "exit" && payload)
|
|
591
619
|
return {
|
|
592
|
-
process:
|
|
620
|
+
process: processHandle(system, required(payload.processSnapshot, String(payload.process ?? ""))),
|
|
593
621
|
status: payload.status,
|
|
594
622
|
code: payload.code,
|
|
595
623
|
signal: payload.signal
|
|
596
624
|
};
|
|
597
625
|
if ((waited.event === "endpointStart" || waited.event === "endpointStop") && payload?.processSnapshot) {
|
|
598
|
-
const process =
|
|
626
|
+
const process = processHandle(system, payload.processSnapshot);
|
|
599
627
|
return payload.endpoint === "client" ? process.client : process.server;
|
|
600
628
|
}
|
|
601
629
|
if (payload && typeof payload.identity === "string")
|
|
602
|
-
return
|
|
630
|
+
return processHandle(system, payload);
|
|
603
631
|
return payload;
|
|
604
632
|
}
|
|
605
633
|
function bindEvents(target, events) {
|
|
@@ -609,8 +637,7 @@ function eventsOf(events) {
|
|
|
609
637
|
return {
|
|
610
638
|
subscribe: events.subscribe,
|
|
611
639
|
waitFor: events.waitFor,
|
|
612
|
-
events: events.events
|
|
613
|
-
observe: events.observe
|
|
640
|
+
events: events.events
|
|
614
641
|
};
|
|
615
642
|
}
|
|
616
643
|
function chronological(left, right) { return left.startedAt.getTime() - right.startedAt.getTime(); }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/node",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Node.js access to PhreshOS and Program projects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"prepack": "node --run build"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@phreshos/core": "^0.1.
|
|
35
|
+
"@phreshos/core": "^0.1.26",
|
|
36
36
|
"adm-zip": "^0.6.0",
|
|
37
37
|
"jiti": "^2.7.0"
|
|
38
38
|
},
|