@phreshos/server 0.1.13 → 0.1.15
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 +28 -19
- package/dist/domain.d.ts +4 -4
- package/dist/domain.js +28 -19
- package/dist/host.d.ts +2 -4
- package/dist/main.d.ts +1 -1
- package/dist/service.d.ts +1 -19
- package/dist/service.js +0 -20
- package/dist/wire.d.ts +4 -0
- package/dist/wire.js +82 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -110,9 +110,12 @@ incarnation is ready. The Server SDK owns one deadline across readiness and the
|
|
|
110
110
|
answer; absence or incarnation loss rejects without turning a boundary into a
|
|
111
111
|
waiter.
|
|
112
112
|
|
|
113
|
-
Server Program handles add `install()` and `fork()`.
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
Server Program handles add `install()` and `fork()`. `install()` is an async
|
|
114
|
+
generator that opens immediately, yields ordered `stdout` and `stderr` command
|
|
115
|
+
chunks, completes only after installation succeeds, and throws the owning
|
|
116
|
+
System error when installation fails. Their filesystem areas also expose
|
|
117
|
+
`path()` and traversal-safe `resolve()`, because filesystem work is performed
|
|
118
|
+
locally in the Server SDK after the Host supplies only the area root.
|
|
116
119
|
Object descriptions passed to `host.program.create()` therefore require an
|
|
117
120
|
explicit absolute storage root as well as at least one declared Endpoint.
|
|
118
121
|
|
|
@@ -149,31 +152,23 @@ rejects instead of reconfiguring the existing Process.
|
|
|
149
152
|
`host.service(key)` creates the exact opaque service handle. The
|
|
150
153
|
handler exposes its authored `name`, `enabled()`, `waitReady()`, lifecycle
|
|
151
154
|
subscriptions, and channel; it does not expose its Program or Endpoint as
|
|
152
|
-
separate fields. Services are
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
installed policy and API documentation before the Endpoint starts.
|
|
155
|
+
separate fields. Services are runtime bindings explicitly enabled by their
|
|
156
|
+
providing Endpoints; creating the providing Process remains the Program's
|
|
157
|
+
responsibility.
|
|
156
158
|
|
|
157
|
-
|
|
158
|
-
wait for readiness without reproducing Program-specific launch rules:
|
|
159
|
+
Readiness accepts an optional timeout directly:
|
|
159
160
|
|
|
160
161
|
```ts
|
|
161
|
-
await
|
|
162
|
-
await clientService.createAndWaitReady({ minimize: true })
|
|
162
|
+
await service.waitReady(10_000)
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
Programs may provide one agent-independent operating document. Its availability
|
|
166
|
+
is projected without loading it, and its content is read from the Program:
|
|
167
167
|
|
|
168
168
|
```ts
|
|
169
|
-
await
|
|
170
|
-
await service.timeout(5_000).waitReady()
|
|
171
|
-
await service.timeout(5_000).waitReady(10_000)
|
|
169
|
+
if (program.hasAgent) console.log(await program.agent())
|
|
172
170
|
```
|
|
173
171
|
|
|
174
|
-
The System owns the single deadline across Process creation, Endpoint startup,
|
|
175
|
-
and Service readiness. The creation capability exists only in the Server SDK.
|
|
176
|
-
|
|
177
172
|
`program.icon()` requests a guaranteed PNG `Blob` in `small`, `medium`, or
|
|
178
173
|
`large` form without exposing the Program's source path or the system's private
|
|
179
174
|
asset-hosting address. Omitting the size selects `medium`.
|
|
@@ -198,3 +193,17 @@ The system validates this through the same launch contract as
|
|
|
198
193
|
`program.process.create()`. Setting startup does not create a Process immediately.
|
|
199
194
|
`uninstall(false)` preserves the configuration but makes it inactive until the
|
|
200
195
|
Program is installed again; removing everything deletes it.
|
|
196
|
+
|
|
197
|
+
Installation and uninstallation expose any declared Server command output as
|
|
198
|
+
ordered streams. Consuming the generator waits for the authoritative operation
|
|
199
|
+
to finish:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
for await (const chunk of program.install()) {
|
|
203
|
+
(chunk.stream === "stderr" ? process.stderr : process.stdout).write(chunk.text)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
for await (const chunk of program.uninstall(true)) {
|
|
207
|
+
(chunk.stream === "stderr" ? process.stderr : process.stdout).write(chunk.text)
|
|
208
|
+
}
|
|
209
|
+
```
|
package/dist/domain.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture, type AskCapture, type Cleanup, type ClientDeclaration, type Exit, type Launch, type Position, type ProgramPermission, type ProgramProcess as CoreProgramProcess, type ProgramArea as CoreProgramArea, type Size, type TrafficMessage, type Window as CoreWindow, type WindowState } from "@phreshos/core";
|
|
1
|
+
import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer, type AnswerCapture, type AskCapture, type Cleanup, type ClientDeclaration, type Exit, type Launch, type Position, type ProgramCommandChunk, type ProgramPermission, type ProgramProcess as CoreProgramProcess, type ProgramArea as CoreProgramArea, type Size, type TrafficMessage, type Window as CoreWindow, type WindowState } from "@phreshos/core";
|
|
2
2
|
import Events from "./events.js";
|
|
3
3
|
import { type ProgramStartup } from "./startup.js";
|
|
4
4
|
import { type ClientServiceHandler, type ServerServiceHandler } from "./service.js";
|
|
@@ -16,7 +16,6 @@ export interface ProgramArea extends CoreProgramArea {
|
|
|
16
16
|
/** Client-safe Program data transported by the authoritative host. */
|
|
17
17
|
export interface EndpointDeclarationRecord {
|
|
18
18
|
start: boolean;
|
|
19
|
-
hasService: boolean;
|
|
20
19
|
}
|
|
21
20
|
export interface ClientDeclarationRecord extends EndpointDeclarationRecord {
|
|
22
21
|
title: string | null;
|
|
@@ -32,6 +31,7 @@ export interface ProgramRecord {
|
|
|
32
31
|
name: string;
|
|
33
32
|
version: string | null;
|
|
34
33
|
description: string | null;
|
|
34
|
+
hasAgent: boolean;
|
|
35
35
|
server: EndpointDeclarationRecord | null;
|
|
36
36
|
client: ClientDeclarationRecord | null;
|
|
37
37
|
}
|
|
@@ -63,8 +63,8 @@ export interface Program<Events extends object = {}> extends Omit<CoreProgram<Ev
|
|
|
63
63
|
readonly permission: ProgramPermission;
|
|
64
64
|
/** Operations and lifecycle observation for this Program's Processes. */
|
|
65
65
|
readonly process: ProgramProcess;
|
|
66
|
-
/** Installs this Program
|
|
67
|
-
install():
|
|
66
|
+
/** Installs this Program while yielding its command output in order. */
|
|
67
|
+
install(): AsyncGenerator<ProgramCommandChunk, void, void>;
|
|
68
68
|
/** Creates a new runtime Program with the supplied stable identity. */
|
|
69
69
|
fork(identity: string): Promise<Program>;
|
|
70
70
|
}
|
package/dist/domain.js
CHANGED
|
@@ -43,11 +43,12 @@ class ProgramHandle extends ProgramBase {
|
|
|
43
43
|
get name() { return this.record.name; }
|
|
44
44
|
get version() { return this.record.version; }
|
|
45
45
|
get description() { return this.record.description; }
|
|
46
|
+
get hasAgent() { return this.record.hasAgent; }
|
|
46
47
|
get server() {
|
|
47
|
-
return this.record.server ? declaration(this.
|
|
48
|
+
return this.record.server ? declaration(this.record.server) : null;
|
|
48
49
|
}
|
|
49
50
|
get client() {
|
|
50
|
-
return this.record.client ? clientDeclaration(this.
|
|
51
|
+
return this.record.client ? clientDeclaration(this.record.client) : null;
|
|
51
52
|
}
|
|
52
53
|
get address() { return { identity: this.identity, reference: this.reference }; }
|
|
53
54
|
update(record) {
|
|
@@ -59,35 +60,49 @@ class ProgramHandle extends ProgramBase {
|
|
|
59
60
|
const answer = await wire.request(["icon", this.address, size]);
|
|
60
61
|
return new Blob([Uint8Array.from(answer[0])], { type: "image/png" });
|
|
61
62
|
}
|
|
63
|
+
async agent() {
|
|
64
|
+
if (!this.hasAgent)
|
|
65
|
+
return null;
|
|
66
|
+
const answer = await wire.request(["program-agent", this.address]);
|
|
67
|
+
return answer[0];
|
|
68
|
+
}
|
|
62
69
|
async installed() {
|
|
63
70
|
const answer = await wire.request(["installed", this.address]);
|
|
64
71
|
return answer[0];
|
|
65
72
|
}
|
|
66
|
-
async install() {
|
|
67
|
-
await wire.
|
|
68
|
-
|
|
73
|
+
async *install() {
|
|
74
|
+
for await (const value of wire.stream(["install", this.address])) {
|
|
75
|
+
yield programCommandChunk(value);
|
|
76
|
+
}
|
|
69
77
|
}
|
|
70
78
|
async fork(identity) {
|
|
71
79
|
const answer = await wire.request(["fork", this.address, identity]);
|
|
72
80
|
return program(answer[0]);
|
|
73
81
|
}
|
|
74
|
-
async uninstall(everything = false) {
|
|
75
|
-
await wire.
|
|
82
|
+
async *uninstall(everything = false) {
|
|
83
|
+
for await (const value of wire.stream(["uninstall", this.address, everything])) {
|
|
84
|
+
yield programCommandChunk(value);
|
|
85
|
+
}
|
|
76
86
|
}
|
|
77
87
|
async forget() {
|
|
78
88
|
await wire.request(["forget", this.address]);
|
|
79
89
|
}
|
|
80
90
|
}
|
|
81
|
-
function
|
|
91
|
+
function programCommandChunk(value) {
|
|
92
|
+
const chunk = value;
|
|
93
|
+
if (!chunk || (chunk.stream !== "stdout" && chunk.stream !== "stderr") || typeof chunk.text !== "string") {
|
|
94
|
+
throw new Error("The host returned an invalid Program command chunk");
|
|
95
|
+
}
|
|
96
|
+
return Object.freeze({ stream: chunk.stream, text: chunk.text });
|
|
97
|
+
}
|
|
98
|
+
function declaration(record) {
|
|
82
99
|
return Object.freeze({
|
|
83
|
-
start: record.start
|
|
84
|
-
hasService: () => record.hasService,
|
|
85
|
-
docs: () => endpointDocs(address, endpoint, record.hasService)
|
|
100
|
+
start: record.start
|
|
86
101
|
});
|
|
87
102
|
}
|
|
88
|
-
function clientDeclaration(
|
|
103
|
+
function clientDeclaration(record) {
|
|
89
104
|
return Object.freeze({
|
|
90
|
-
...declaration(
|
|
105
|
+
...declaration(record),
|
|
91
106
|
title: record.title,
|
|
92
107
|
size: record.size,
|
|
93
108
|
position: record.position,
|
|
@@ -95,12 +110,6 @@ function clientDeclaration(address, record) {
|
|
|
95
110
|
minimize: record.minimize
|
|
96
111
|
});
|
|
97
112
|
}
|
|
98
|
-
async function endpointDocs(address, endpoint, declared) {
|
|
99
|
-
if (!declared)
|
|
100
|
-
return null;
|
|
101
|
-
const answer = await wire.request(["endpoint-docs", address, endpoint]);
|
|
102
|
-
return answer[0];
|
|
103
|
-
}
|
|
104
113
|
class ProgramProcessHandle {
|
|
105
114
|
address;
|
|
106
115
|
constructor(address, reference) {
|
package/dist/host.d.ts
CHANGED
|
@@ -7,8 +7,6 @@ export type ServerDescription = Readonly<{
|
|
|
7
7
|
location: string;
|
|
8
8
|
/** Whether newly created Processes start this Server by default. */
|
|
9
9
|
start?: boolean;
|
|
10
|
-
/** Absolute Markdown file documenting the Service this Server may expose. */
|
|
11
|
-
serviceDocs?: string;
|
|
12
10
|
/** Command used to install the Server's production dependencies. */
|
|
13
11
|
installCommand?: string;
|
|
14
12
|
/** Command used to start the Server from its production directory. */
|
|
@@ -20,8 +18,6 @@ export type ClientDescription = Readonly<{
|
|
|
20
18
|
location: string;
|
|
21
19
|
/** Whether newly created Processes start this Client by default. */
|
|
22
20
|
start?: boolean;
|
|
23
|
-
/** Absolute Markdown file documenting the Service this Client may expose. */
|
|
24
|
-
serviceDocs?: string;
|
|
25
21
|
/** Default Window title. */
|
|
26
22
|
title?: string;
|
|
27
23
|
/** Default Window size. */
|
|
@@ -44,6 +40,8 @@ type Description = Readonly<{
|
|
|
44
40
|
description?: string;
|
|
45
41
|
/** Absolute validated PNG source used to derive the Program's hosted icon sizes. */
|
|
46
42
|
icon?: string;
|
|
43
|
+
/** Absolute Markdown file describing Program-specific operation to agents. */
|
|
44
|
+
agent?: string;
|
|
47
45
|
/** Absolute directory used for the Program's persistent storage. */
|
|
48
46
|
storage: string;
|
|
49
47
|
}>;
|
package/dist/main.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export { host, type Host, type HostProcess, type HostProgram, type ClientDescrip
|
|
|
2
2
|
export { current, type Current, type CurrentClient } from "./current.js";
|
|
3
3
|
export { type Answerer, type Channel } from "./channel.js";
|
|
4
4
|
export { type ProgramStartup } from "./startup.js";
|
|
5
|
-
export { ClientServiceHandler, ServerServiceHandler
|
|
5
|
+
export { ClientServiceHandler, ServerServiceHandler } from "./service.js";
|
|
6
6
|
export { ServiceHandler, type ClientServiceChannel, type ServerServiceChannel, type ServiceChannel, type ServiceKey, type ServiceLifecycleEvents } from "@phreshos/core";
|
|
7
7
|
export { Client, Endpoint, Process, Program, Server, type Window, type ProgramArea, type ProgramProcess } from "./domain.js";
|
|
8
8
|
export type { AnswerCapture, AnswerMessage, AnswerObserver, Askable, AskCapture, AskMessage, AskObserver, Capture, Captures, ChannelCapture, ChannelEvents, ChannelMessage, ClientTraffic, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointTraffic, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, Position, ProgramEvents, ProgramPermission, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, ServedFile, ServerTraffic, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TrafficCapture, TrafficEvents, TrafficMessage, Value, Theme, ThemeEvents, ThemeProperties, WallpaperLaunch, FileWallpaper, DesktopWallpaper, WritableTheme, WindowEvents, WindowGeometry, WindowLayer, WindowState } from "@phreshos/core";
|
package/dist/service.d.ts
CHANGED
|
@@ -1,31 +1,13 @@
|
|
|
1
|
-
import { ClientServiceHandler as CoreClientServiceHandler, ServerServiceHandler as CoreServerServiceHandler, type
|
|
1
|
+
import { ClientServiceHandler as CoreClientServiceHandler, ServerServiceHandler as CoreServerServiceHandler, type ServiceHandler, type ServiceKey } from "@phreshos/core";
|
|
2
2
|
import type { HandleAddress } from "./domain.js";
|
|
3
|
-
/** Timed view of a Server Endpoint Service available through the Server SDK. */
|
|
4
|
-
export interface TimedServerServiceHandler {
|
|
5
|
-
waitReady(timeout?: number): Promise<void>;
|
|
6
|
-
createAndWaitReady(timeout?: number): Promise<void>;
|
|
7
|
-
}
|
|
8
|
-
/** Timed view of a Client Endpoint Service available through the Server SDK. */
|
|
9
|
-
export interface TimedClientServiceHandler {
|
|
10
|
-
waitReady(timeout?: number): Promise<void>;
|
|
11
|
-
createAndWaitReady(client?: LaunchClient, timeout?: number): Promise<void>;
|
|
12
|
-
}
|
|
13
3
|
/** Server-SDK handle for a Service provided by a Server Endpoint. */
|
|
14
4
|
export declare class ServerServiceHandler<Events extends object = {}> extends CoreServerServiceHandler<Events> {
|
|
15
5
|
protected constructor();
|
|
16
6
|
}
|
|
17
|
-
export interface ServerServiceHandler<Events extends object = {}> extends Timeoutable<TimedServerServiceHandler> {
|
|
18
|
-
createAndWaitReady(timeout?: number): Promise<void>;
|
|
19
|
-
timeout(milliseconds: number): TimedServerServiceHandler;
|
|
20
|
-
}
|
|
21
7
|
/** Server-SDK handle for a Service provided by a Client Endpoint. */
|
|
22
8
|
export declare class ClientServiceHandler<Events extends object = {}> extends CoreClientServiceHandler<Events> {
|
|
23
9
|
protected constructor();
|
|
24
10
|
}
|
|
25
|
-
export interface ClientServiceHandler<Events extends object = {}> extends Timeoutable<TimedClientServiceHandler> {
|
|
26
|
-
createAndWaitReady(client?: LaunchClient, timeout?: number): Promise<void>;
|
|
27
|
-
timeout(milliseconds: number): TimedClientServiceHandler;
|
|
28
|
-
}
|
|
29
11
|
export declare function prepareService<EventsMap extends object = {}>(key: ServiceKey & {
|
|
30
12
|
endpoint: "server";
|
|
31
13
|
}): ServerServiceHandler<EventsMap>;
|
package/dist/service.js
CHANGED
|
@@ -69,15 +69,6 @@ class ServerHandler extends ServerServiceHandler {
|
|
|
69
69
|
async waitReady(timeout) {
|
|
70
70
|
await wire.request(["service-wait-ready", this.key, timeout], timeout);
|
|
71
71
|
}
|
|
72
|
-
async createAndWaitReady(timeout) {
|
|
73
|
-
await wire.request(["service-create-and-wait-ready", this.key, undefined, timeout], timeout);
|
|
74
|
-
}
|
|
75
|
-
timeout(milliseconds) {
|
|
76
|
-
return Object.freeze({
|
|
77
|
-
waitReady: (timeout) => this.waitReady(timeout ?? milliseconds),
|
|
78
|
-
createAndWaitReady: (timeout) => this.createAndWaitReady(timeout ?? milliseconds)
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
72
|
}
|
|
82
73
|
class ClientHandler extends ClientServiceHandler {
|
|
83
74
|
key;
|
|
@@ -97,17 +88,6 @@ class ClientHandler extends ClientServiceHandler {
|
|
|
97
88
|
async waitReady(timeout) {
|
|
98
89
|
await wire.request(["service-wait-ready", this.key, timeout], timeout);
|
|
99
90
|
}
|
|
100
|
-
async createAndWaitReady(client, timeout) {
|
|
101
|
-
await wire.request(["service-create-and-wait-ready", this.key, client, timeout], timeout);
|
|
102
|
-
}
|
|
103
|
-
timeout(milliseconds) {
|
|
104
|
-
return Object.freeze({
|
|
105
|
-
waitReady: (timeout) => this.waitReady(timeout ?? milliseconds),
|
|
106
|
-
createAndWaitReady: (client, timeout) => {
|
|
107
|
-
return this.createAndWaitReady(client, timeout ?? milliseconds);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
91
|
}
|
|
112
92
|
export function prepareService(key) {
|
|
113
93
|
if (!isServiceKey(key))
|
package/dist/wire.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ type TrafficKind = "publish" | "ask" | "answer";
|
|
|
7
7
|
/** The server endpoint's sole IPC adapter. */
|
|
8
8
|
declare class Wire {
|
|
9
9
|
private readonly pending;
|
|
10
|
+
private readonly streams;
|
|
10
11
|
private readonly subscribers;
|
|
11
12
|
private readonly every;
|
|
12
13
|
private readonly answerers;
|
|
@@ -17,6 +18,8 @@ declare class Wire {
|
|
|
17
18
|
send(route: string, ...values: unknown[]): void;
|
|
18
19
|
request(values: unknown[], timeout?: number): Promise<unknown>;
|
|
19
20
|
requestWithin(values: unknown[], deadline: Deadline): Promise<unknown>;
|
|
21
|
+
/** Opens one long-running host operation and yields its ordered values. */
|
|
22
|
+
stream(values: unknown[], timeout?: number): AsyncIterableIterator<unknown>;
|
|
20
23
|
/** Resolves this endpoint's Process address only for operations that need it. */
|
|
21
24
|
identity(): Promise<{
|
|
22
25
|
process: string;
|
|
@@ -40,6 +43,7 @@ declare class Wire {
|
|
|
40
43
|
private releaseWaiting;
|
|
41
44
|
private forgetIncoming;
|
|
42
45
|
private settle;
|
|
46
|
+
private receiveStream;
|
|
43
47
|
}
|
|
44
48
|
declare const _default: Wire;
|
|
45
49
|
export default _default;
|
package/dist/wire.js
CHANGED
|
@@ -5,6 +5,7 @@ import { deserialize, serialize } from "./messagepack.js";
|
|
|
5
5
|
/** The server endpoint's sole IPC adapter. */
|
|
6
6
|
class Wire {
|
|
7
7
|
pending = new Map();
|
|
8
|
+
streams = new Map();
|
|
8
9
|
subscribers = new Map();
|
|
9
10
|
every = new Map();
|
|
10
11
|
answerers = new Map();
|
|
@@ -35,6 +36,10 @@ class Wire {
|
|
|
35
36
|
}
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
39
|
+
if (values[0] === "stream" && typeof values[1] === "string" && typeof values[2] === "string") {
|
|
40
|
+
this.receiveStream(values[1], values[2], values[3]);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
38
43
|
if (values[0] === "answer" && typeof values[1] === "string") {
|
|
39
44
|
this.settle(values[1], values.at(-1));
|
|
40
45
|
return;
|
|
@@ -66,6 +71,46 @@ class Wire {
|
|
|
66
71
|
this.send("end-host", "wait", question, ...values);
|
|
67
72
|
});
|
|
68
73
|
}
|
|
74
|
+
/** Opens one long-running host operation and yields its ordered values. */
|
|
75
|
+
stream(values, timeout = defaultTimeout) {
|
|
76
|
+
const wire = this;
|
|
77
|
+
return (async function* () {
|
|
78
|
+
const question = randomUUID();
|
|
79
|
+
const state = {
|
|
80
|
+
queue: [],
|
|
81
|
+
opened: false,
|
|
82
|
+
ended: false,
|
|
83
|
+
failure: null,
|
|
84
|
+
wake: null,
|
|
85
|
+
timer: setTimeout(() => {
|
|
86
|
+
state.failure = new Error(`Answer timeout ${timeout}ms`);
|
|
87
|
+
state.wake?.();
|
|
88
|
+
state.wake = null;
|
|
89
|
+
}, timeout)
|
|
90
|
+
};
|
|
91
|
+
wire.send("boundary", "expect", question);
|
|
92
|
+
wire.streams.set(question, state);
|
|
93
|
+
wire.send("end-host", "stream", question, ...values);
|
|
94
|
+
try {
|
|
95
|
+
while (true) {
|
|
96
|
+
if (state.queue.length) {
|
|
97
|
+
yield state.queue.shift();
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (state.failure)
|
|
101
|
+
throw state.failure;
|
|
102
|
+
if (state.ended)
|
|
103
|
+
return;
|
|
104
|
+
await new Promise(resolve => { state.wake = resolve; });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
clearTimeout(state.timer);
|
|
109
|
+
wire.streams.delete(question);
|
|
110
|
+
wire.send("boundary", "forget", question);
|
|
111
|
+
}
|
|
112
|
+
})();
|
|
113
|
+
}
|
|
69
114
|
/** Resolves this endpoint's Process address only for operations that need it. */
|
|
70
115
|
identity() {
|
|
71
116
|
if (!this.identityPromise) {
|
|
@@ -255,7 +300,44 @@ class Wire {
|
|
|
255
300
|
else
|
|
256
301
|
pending.reject(new Error("The boundary returned an invalid outcome"));
|
|
257
302
|
}
|
|
303
|
+
receiveStream(question, operation, value) {
|
|
304
|
+
const stream = this.streams.get(question);
|
|
305
|
+
if (!stream || stream.ended || stream.failure)
|
|
306
|
+
return;
|
|
307
|
+
if (operation === "open") {
|
|
308
|
+
if (stream.opened)
|
|
309
|
+
return;
|
|
310
|
+
stream.opened = true;
|
|
311
|
+
clearTimeout(stream.timer);
|
|
312
|
+
}
|
|
313
|
+
else if (!stream.opened) {
|
|
314
|
+
stream.failure = new Error("The boundary produced a stream value before opening the stream");
|
|
315
|
+
}
|
|
316
|
+
else if (operation === "data") {
|
|
317
|
+
if (stream.queue.length >= maximumStreamQueue) {
|
|
318
|
+
stream.failure = new Error(`Host stream queue exceeded its capacity of ${maximumStreamQueue}`);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
stream.queue.push(value);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
else if (operation === "answer") {
|
|
325
|
+
const outcome = value;
|
|
326
|
+
if (outcome?.success === true)
|
|
327
|
+
stream.ended = true;
|
|
328
|
+
else if (outcome?.success === false && typeof outcome.error === "string")
|
|
329
|
+
stream.failure = new Error(outcome.error);
|
|
330
|
+
else
|
|
331
|
+
stream.failure = new Error("The boundary returned an invalid stream outcome");
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
stream.failure = new Error(`The boundary returned an invalid stream operation "${operation}"`);
|
|
335
|
+
}
|
|
336
|
+
stream.wake?.();
|
|
337
|
+
stream.wake = null;
|
|
338
|
+
}
|
|
258
339
|
}
|
|
340
|
+
const maximumStreamQueue = 256;
|
|
259
341
|
function failed(error) {
|
|
260
342
|
return {
|
|
261
343
|
success: false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "The SDK used by a Program's server endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
"prepack": "node --run build"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@phreshos/core": "^0.1.
|
|
49
|
+
"@phreshos/core": "^0.1.12"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@msgpack/msgpack": "^3.1.3"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
|
-
"@phreshos/core": "^0.1.
|
|
55
|
+
"@phreshos/core": "^0.1.13",
|
|
56
56
|
"@types/node": "^26.2.0",
|
|
57
57
|
"typescript": "^6.0.3"
|
|
58
58
|
}
|