@phreshos/client 0.1.19 → 0.1.21
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 +30 -25
- package/dist/appearance.d.ts +15 -0
- package/dist/appearance.js +18 -0
- package/dist/controlled-stream.d.ts +2 -0
- package/dist/controlled-stream.js +26 -0
- package/dist/desktop.d.ts +1 -1
- package/dist/desktop.js +1 -1
- package/dist/domain.js +1 -1
- package/dist/main.d.ts +4 -4
- package/dist/main.js +1 -1
- package/dist/pointer.d.ts +1 -1
- package/dist/storage.js +2 -26
- package/dist/{host.d.ts → system.d.ts} +13 -11
- package/dist/{host.js → system.js} +8 -44
- package/dist/theme.d.ts +4 -9
- package/dist/theme.js +8 -7
- package/dist/uploads.d.ts +12 -0
- package/dist/uploads.js +58 -0
- package/dist/wire.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -15,26 +15,31 @@ provide its runtime environment.
|
|
|
15
15
|
|
|
16
16
|
It uses the domain objects and shared contracts from `@phreshos/core` through
|
|
17
17
|
a peer dependency. It does not redefine those objects, own server-side
|
|
18
|
-
capabilities, or contain
|
|
19
|
-
|
|
20
|
-
Its `
|
|
21
|
-
|
|
22
|
-
unrestricted server-side Fetch.
|
|
23
|
-
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
18
|
+
capabilities, or contain System and transport implementations.
|
|
19
|
+
|
|
20
|
+
Its `System` contract exposes only desktop-session capabilities: read-only
|
|
21
|
+
System Appearance, the mutable Theme local to this Desktop, desktop and pointer
|
|
22
|
+
state, flat public uploads, and unrestricted server-side Fetch.
|
|
23
|
+
`system.appearance.snapshot()` reads complete unresolved authoritative state.
|
|
24
|
+
`system.theme.snapshot()` reads only the effective `"light" | "dark"` mode;
|
|
25
|
+
`update("default")` resumes following the native environment. Both expose
|
|
26
|
+
ordinary live-only `change` subscriptions with no initial replay. A Client
|
|
27
|
+
cannot write authoritative Appearance or discover system-wide Programs and
|
|
28
|
+
Processes.
|
|
29
|
+
|
|
30
|
+
`system.uploads` has the same flat contract as the Server SDK. It writes a
|
|
31
|
+
value, then reads or describes it using exactly one opaque generated file key;
|
|
32
|
+
it exposes no filesystem path, path segments, listing, deletion, or clearing.
|
|
28
33
|
|
|
29
34
|
Desktop and pointer access are independent objects rather than events merged
|
|
30
|
-
into
|
|
35
|
+
into System:
|
|
31
36
|
|
|
32
37
|
```ts
|
|
33
|
-
const desktop = await
|
|
34
|
-
const stopDesktop =
|
|
38
|
+
const desktop = await system.desktop.size()
|
|
39
|
+
const stopDesktop = system.desktop.subscribe("resize", next => undefined)
|
|
35
40
|
|
|
36
|
-
const position = await
|
|
37
|
-
const stopPointer =
|
|
41
|
+
const position = await system.pointer.position()
|
|
42
|
+
const stopPointer = system.pointer.subscribe("move", next => undefined)
|
|
38
43
|
```
|
|
39
44
|
|
|
40
45
|
The desktop size is the complete desktop area containing this Client,
|
|
@@ -44,7 +49,7 @@ movement both require the `pointer` permission. Reads are asynchronous
|
|
|
44
49
|
requests; subscriptions receive only future publications and never replay a
|
|
45
50
|
retained value.
|
|
46
51
|
|
|
47
|
-
Permission decisions belong to the current Program, not to the desktop
|
|
52
|
+
Permission decisions belong to the current Program, not to the desktop System.
|
|
48
53
|
They are available from a Program handle and flattened through `current` as the
|
|
49
54
|
same canonical capability:
|
|
50
55
|
|
|
@@ -61,13 +66,13 @@ current.permission === program.permission
|
|
|
61
66
|
`granted()` reads the effective decision without prompting. `request()` asks
|
|
62
67
|
only when no known decision can answer immediately and returns `null` if its
|
|
63
68
|
deadline expires. The desktop remains the internal enforcement boundary, but
|
|
64
|
-
permission ownership does not alter the public shape of `
|
|
69
|
+
permission ownership does not alter the public shape of `system`.
|
|
65
70
|
|
|
66
|
-
The Client
|
|
71
|
+
The Client System exposes only direct, exact Service handles. Creating a handle
|
|
67
72
|
does not read or start the Service and exposes no Service registry:
|
|
68
73
|
|
|
69
74
|
```ts
|
|
70
|
-
const service =
|
|
75
|
+
const service = system.service({
|
|
71
76
|
program: "counter",
|
|
72
77
|
endpoint: "server",
|
|
73
78
|
name: "state"
|
|
@@ -115,7 +120,7 @@ absence or incarnation loss rejects without turning the boundary into a waiter.
|
|
|
115
120
|
The package provides two contextual runtime entry points:
|
|
116
121
|
|
|
117
122
|
```ts
|
|
118
|
-
import {
|
|
123
|
+
import { system, current } from "@phreshos/client"
|
|
119
124
|
```
|
|
120
125
|
|
|
121
126
|
It also re-exports the shared Core runtime classes—`Program`, `Process`,
|
|
@@ -137,12 +142,12 @@ reserved for directed publications, questions, and answers.
|
|
|
137
142
|
|
|
138
143
|
Messages sent by an Endpoint in the same Program contain that real Endpoint in
|
|
139
144
|
`from`. If the sender belongs to another Program, `from` is `null`; the foreign
|
|
140
|
-
identity is removed by the
|
|
145
|
+
identity is removed by the authoritative System before the message reaches the desktop.
|
|
141
146
|
The same rule applies to Client-visible traffic destinations.
|
|
142
147
|
|
|
143
148
|
Client-visible Program handles can operate only within their own Program.
|
|
144
149
|
They deliberately omit `install()` and `fork()`, and their `Storage` values never
|
|
145
|
-
expose
|
|
150
|
+
expose native filesystem paths. Every Client-side Window handle exposes the same
|
|
146
151
|
authoritative, subscribable Window capability and its `window.local` physical
|
|
147
152
|
representation on this desktop. Local reads and updates have no events and do
|
|
148
153
|
not change Server state. `current.window` is only convenient access to the
|
|
@@ -177,7 +182,7 @@ them through one authoritative request and produces one `geometry` event.
|
|
|
177
182
|
Calling `move()` and `resize()` sequentially or through `Promise.all()` remains
|
|
178
183
|
two independent operations and can expose an intermediate state remotely.
|
|
179
184
|
|
|
180
|
-
An `under` or `over` representation may request one local
|
|
185
|
+
An `under` or `over` representation may request one local system-rendered Surface:
|
|
181
186
|
|
|
182
187
|
```ts
|
|
183
188
|
await current.window.local.surface.set({ duration: 240, easing: "ease-out", wait: true })
|
|
@@ -190,13 +195,13 @@ representation. Reloading or destroying it resets the representation from
|
|
|
190
195
|
authoritative truth, while other desktops remain unaffected. Program code may
|
|
191
196
|
synchronize desired presence through its Server and explicitly apply it
|
|
192
197
|
again. `set()` and `remove()` accept only a required `VisibilityTransition`;
|
|
193
|
-
Programs cannot configure the
|
|
198
|
+
Programs cannot configure the System Surface's material, opacity, or radius.
|
|
194
199
|
The transition uses milliseconds and a stable named or cubic Bézier easing;
|
|
195
200
|
the desktop performs the motion, honors reduced motion, and removes the Surface
|
|
196
201
|
only after its exit transition completes. `wait: true` makes the request settle
|
|
197
202
|
with that transition. A new iframe representation restores nothing. The
|
|
198
203
|
container follows the iframe geometry while the independently rounded Surface
|
|
199
|
-
neither clips nor masks Client content.
|
|
204
|
+
neither clips nor masks Client content. The ordinary `window` layer rejects
|
|
200
205
|
the capability.
|
|
201
206
|
|
|
202
207
|
`program.icon()` requests the current Program's guaranteed PNG `Blob` on
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Events from "./events.js";
|
|
2
|
+
/** Read-only System Appearance reached through the Desktop boundary. */
|
|
3
|
+
export default class ClientAppearance extends Events {
|
|
4
|
+
constructor();
|
|
5
|
+
snapshot(): Promise<Readonly<{
|
|
6
|
+
background: import("@phreshos/core").ThemedValue<string, string>;
|
|
7
|
+
foreground: import("@phreshos/core").ThemedValue<string, string>;
|
|
8
|
+
accent: import("@phreshos/core").ThemedValue<string, string>;
|
|
9
|
+
spacing: import("@phreshos/core").ThemedValue<number>;
|
|
10
|
+
radius: import("@phreshos/core").ThemedValue<number>;
|
|
11
|
+
surface: import("@phreshos/core").ThemedValue<import("@phreshos/core").AppearanceSurface, import("@phreshos/core").AppearanceSurface>;
|
|
12
|
+
signInWallpaper: import("@phreshos/core").ThemedValue<string | null, string | null>;
|
|
13
|
+
desktopWallpaper: import("@phreshos/core").ThemedValue<string | null, string | null>;
|
|
14
|
+
}>>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createAppearanceSnapshot } from "@phreshos/core";
|
|
2
|
+
import Events from "./events.js";
|
|
3
|
+
import wire from "./wire.js";
|
|
4
|
+
/** Read-only System Appearance reached through the Desktop boundary. */
|
|
5
|
+
export default class ClientAppearance extends Events {
|
|
6
|
+
constructor() {
|
|
7
|
+
super((event, listener, impossible) => wire.on("host-appearance", event, value => {
|
|
8
|
+
listener(createAppearanceSnapshot(value));
|
|
9
|
+
}, null, impossible), observer => wire.onAll("host-appearance", (event, value) => {
|
|
10
|
+
if (typeof event === "string")
|
|
11
|
+
observer(event, createAppearanceSnapshot(value));
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
async snapshot() {
|
|
15
|
+
const [appearance] = await wire.request(["appearance"]);
|
|
16
|
+
return createAppearanceSnapshot(appearance);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Wrap a transferred byte stream so cancellation and completion release its transport. */
|
|
2
|
+
export default function controlledStream(body, abort, close) {
|
|
3
|
+
const reader = body.getReader();
|
|
4
|
+
return new ReadableStream({
|
|
5
|
+
async pull(controller) {
|
|
6
|
+
try {
|
|
7
|
+
const next = await reader.read();
|
|
8
|
+
if (next.done) {
|
|
9
|
+
close();
|
|
10
|
+
controller.close();
|
|
11
|
+
}
|
|
12
|
+
else
|
|
13
|
+
controller.enqueue(next.value);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
close();
|
|
17
|
+
controller.error(error);
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
async cancel(reason) {
|
|
21
|
+
abort();
|
|
22
|
+
close();
|
|
23
|
+
await reader.cancel(reason);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
package/dist/desktop.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type DesktopEvents = {
|
|
|
11
11
|
resize: DesktopSize;
|
|
12
12
|
};
|
|
13
13
|
/** Explicit desktop size reads and future resizes. */
|
|
14
|
-
export interface
|
|
14
|
+
export interface SystemDesktop extends Subscribable<DesktopEvents, never> {
|
|
15
15
|
/** Reads the complete current desktop area in CSS pixels. */
|
|
16
16
|
size(): Promise<DesktopSize>;
|
|
17
17
|
}
|
package/dist/desktop.js
CHANGED
|
@@ -17,7 +17,7 @@ export default class ClientDesktop extends Events {
|
|
|
17
17
|
const answer = await wire.request(["desktop"]);
|
|
18
18
|
const size = createSize(answer[0]);
|
|
19
19
|
if (!size)
|
|
20
|
-
throw new Error("The
|
|
20
|
+
throw new Error("The system returned an invalid desktop size");
|
|
21
21
|
return size;
|
|
22
22
|
}
|
|
23
23
|
}
|
package/dist/domain.js
CHANGED
|
@@ -410,7 +410,7 @@ function lifecycleEndpoint(record, kind) {
|
|
|
410
410
|
return owner.server;
|
|
411
411
|
if (kind === "client")
|
|
412
412
|
return owner.client;
|
|
413
|
-
throw new Error("The
|
|
413
|
+
throw new Error("The system returned an invalid Endpoint lifecycle event");
|
|
414
414
|
}
|
|
415
415
|
export function exit(code, signal) {
|
|
416
416
|
const namedSignal = stringOrNull(signal);
|
package/dist/main.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { type
|
|
3
|
-
export { type DesktopEvents, type DesktopSize, type
|
|
1
|
+
export { system, type System } from "./system.js";
|
|
2
|
+
export { type SystemPointer, type PointerEvents, type PointerPosition } from "./pointer.js";
|
|
3
|
+
export { type DesktopEvents, type DesktopSize, type SystemDesktop } from "./desktop.js";
|
|
4
4
|
export { current, type Current, type CurrentServer } from "./current.js";
|
|
5
5
|
export { type Channel, type ChannelCapture, type ChannelEvents, type ChannelMessage } from "./channel.js";
|
|
6
6
|
export { ClientServiceHandler, ServerServiceHandler, 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 ProgramProcess, type AnswerCapture, type AnswerMessage, type AnswerObserver, type AskCapture, type AskMessage, type AskObserver, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
|
|
8
|
-
export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permission, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable,
|
|
8
|
+
export type { Askable, Capture, Captures, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EntryStat, EventMessage, EventName, EventObserver, EventOptions, EventSubscriber, Exit, FileStat, Launch, LaunchClient, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, PermissionDecision, Permission, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramSql, ProgramStore, ProcessEvents, Publishable, SystemUploads, Upload, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, TimedPermission, Timeoutable, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, SystemTheme, ThemedValue, Theme, ThemeEvents, ThemePreference, Value, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, VisibilityTransition, Transaction } from "@phreshos/core";
|
package/dist/main.js
CHANGED
package/dist/pointer.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type PointerEvents = {
|
|
|
13
13
|
move: PointerPosition;
|
|
14
14
|
};
|
|
15
15
|
/** Permission-guarded pointer positions and future movement. */
|
|
16
|
-
export interface
|
|
16
|
+
export interface SystemPointer extends Subscribable<PointerEvents, never> {
|
|
17
17
|
/**
|
|
18
18
|
* Reads the current desktop pointer position, or `null` before one is known.
|
|
19
19
|
* Rejects unless the `pointer` permission is currently granted.
|
package/dist/storage.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { content } from "./content.js";
|
|
2
|
+
import controlledStream from "./controlled-stream.js";
|
|
2
3
|
import wire from "./wire.js";
|
|
3
4
|
/** Client-side Program storage, structurally scoped by the iframe boundary. */
|
|
4
5
|
export function area(which) {
|
|
@@ -18,7 +19,7 @@ export function area(which) {
|
|
|
18
19
|
}
|
|
19
20
|
if (!(answer[0] instanceof ReadableStream))
|
|
20
21
|
throw new Error("The storage response has no byte stream");
|
|
21
|
-
return
|
|
22
|
+
return controlledStream(answer[0], abort, close);
|
|
22
23
|
}
|
|
23
24
|
catch (error) {
|
|
24
25
|
abort();
|
|
@@ -69,28 +70,3 @@ export function sql(kind) {
|
|
|
69
70
|
}
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
|
-
function controlled(body, abort, close) {
|
|
73
|
-
const reader = body.getReader();
|
|
74
|
-
return new ReadableStream({
|
|
75
|
-
async pull(controller) {
|
|
76
|
-
try {
|
|
77
|
-
const next = await reader.read();
|
|
78
|
-
if (next.done) {
|
|
79
|
-
close();
|
|
80
|
-
controller.close();
|
|
81
|
-
}
|
|
82
|
-
else
|
|
83
|
-
controller.enqueue(next.value);
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
close();
|
|
87
|
-
controller.error(error);
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
async cancel(reason) {
|
|
91
|
-
abort();
|
|
92
|
-
close();
|
|
93
|
-
await reader.cancel(reason);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { type
|
|
3
|
-
import { type
|
|
1
|
+
import type { AppearanceSource, ClientServiceHandler, ServerServiceHandler, ServiceKey, SystemUploads, SystemTheme } from "@phreshos/core";
|
|
2
|
+
import { type SystemPointer } from "./pointer.js";
|
|
3
|
+
import { type SystemDesktop } from "./desktop.js";
|
|
4
4
|
type ServiceEndpoint = ServiceKey["endpoint"];
|
|
5
5
|
type ServiceAddress<Endpoint extends ServiceEndpoint> = Omit<ServiceKey, "endpoint"> & Readonly<{
|
|
6
6
|
endpoint: Endpoint;
|
|
7
7
|
}>;
|
|
8
8
|
type ServiceHandle<Endpoint extends ServiceEndpoint, Events extends object> = Endpoint extends "server" ? ServerServiceHandler<Events> : ClientServiceHandler<Events>;
|
|
9
9
|
/** Desktop capabilities structurally available to a Client endpoint. */
|
|
10
|
-
export interface
|
|
11
|
-
/**
|
|
12
|
-
readonly
|
|
10
|
+
export interface System {
|
|
11
|
+
/** Complete unresolved Appearance read from the System authority. */
|
|
12
|
+
readonly appearance: AppearanceSource;
|
|
13
|
+
/** Mutable effective Theme local to this Desktop. */
|
|
14
|
+
readonly theme: SystemTheme;
|
|
13
15
|
/** Layer-independent desktop size reads and live updates. */
|
|
14
|
-
readonly desktop:
|
|
16
|
+
readonly desktop: SystemDesktop;
|
|
15
17
|
/** Permission-guarded desktop pointer reads and live movement. */
|
|
16
|
-
readonly pointer:
|
|
17
|
-
/**
|
|
18
|
-
|
|
18
|
+
readonly pointer: SystemPointer;
|
|
19
|
+
/** Flat System-owned public uploads capability. */
|
|
20
|
+
readonly uploads: SystemUploads;
|
|
19
21
|
/** Performs an unrestricted server-side fetch on behalf of this Client. */
|
|
20
22
|
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
21
23
|
/** Returns a precisely typed stable handle for either service endpoint. */
|
|
@@ -26,5 +28,5 @@ export interface Host {
|
|
|
26
28
|
service<ServiceEvents extends object>(key: ServiceAddress<"client">): ClientServiceHandler<ServiceEvents>;
|
|
27
29
|
}
|
|
28
30
|
/** Desktop capabilities available to this Client. */
|
|
29
|
-
export declare const
|
|
31
|
+
export declare const system: System;
|
|
30
32
|
export {};
|
|
@@ -1,29 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ClientAppearance from "./appearance.js";
|
|
2
2
|
import ClientTheme from "./theme.js";
|
|
3
3
|
import wire from "./wire.js";
|
|
4
4
|
import ClientPointer, {} from "./pointer.js";
|
|
5
5
|
import ClientDesktop, {} from "./desktop.js";
|
|
6
6
|
import { prepareService } from "./service.js";
|
|
7
|
-
|
|
7
|
+
import { uploads } from "./uploads.js";
|
|
8
|
+
import controlledStream from "./controlled-stream.js";
|
|
9
|
+
class ClientSystem {
|
|
10
|
+
appearance = new ClientAppearance();
|
|
8
11
|
theme = new ClientTheme();
|
|
9
12
|
desktop = new ClientDesktop();
|
|
10
13
|
pointer = new ClientPointer();
|
|
14
|
+
uploads = uploads;
|
|
11
15
|
service(key) { return prepareService(key); }
|
|
12
|
-
async serve(value) {
|
|
13
|
-
const source = content(value);
|
|
14
|
-
const channel = new MessageChannel();
|
|
15
|
-
const abort = () => channel.port1.postMessage("abort");
|
|
16
|
-
try {
|
|
17
|
-
const answer = await wire.request(["serve", source.body, { extension: source.extension, type: source.type }, channel.port2], undefined, source.body instanceof ReadableStream ? [source.body, channel.port2] : [channel.port2]);
|
|
18
|
-
channel.port1.close();
|
|
19
|
-
return answer[0];
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
abort();
|
|
23
|
-
channel.port1.close();
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
16
|
async fetch(input, init) {
|
|
28
17
|
const request = new Request(input, {
|
|
29
18
|
...init,
|
|
@@ -63,7 +52,7 @@ class ClientHost {
|
|
|
63
52
|
throw request.signal.aborted ? request.signal.reason : error;
|
|
64
53
|
}
|
|
65
54
|
const responseBody = result.body
|
|
66
|
-
?
|
|
55
|
+
? controlledStream(result.body, abort, () => closeControl(request.signal, control.port1, abort))
|
|
67
56
|
: null;
|
|
68
57
|
if (!responseBody)
|
|
69
58
|
closeControl(request.signal, control.port1, abort);
|
|
@@ -91,34 +80,9 @@ function requestHeaders(normalized, supplied) {
|
|
|
91
80
|
const names = new Set(explicit.map(([name]) => name.toLowerCase()));
|
|
92
81
|
return [...normalized.entries()].filter(([name]) => !names.has(name.toLowerCase())).concat(explicit);
|
|
93
82
|
}
|
|
94
|
-
function controlled(body, abort, close) {
|
|
95
|
-
const reader = body.getReader();
|
|
96
|
-
return new ReadableStream({
|
|
97
|
-
async pull(controller) {
|
|
98
|
-
try {
|
|
99
|
-
const next = await reader.read();
|
|
100
|
-
if (next.done) {
|
|
101
|
-
close();
|
|
102
|
-
controller.close();
|
|
103
|
-
}
|
|
104
|
-
else
|
|
105
|
-
controller.enqueue(next.value);
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
close();
|
|
109
|
-
controller.error(error);
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
async cancel(reason) {
|
|
113
|
-
abort();
|
|
114
|
-
close();
|
|
115
|
-
await reader.cancel(reason);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
83
|
function closeControl(signal, port, abort) {
|
|
120
84
|
signal.removeEventListener("abort", abort);
|
|
121
85
|
port.close();
|
|
122
86
|
}
|
|
123
87
|
/** Desktop capabilities available to this Client. */
|
|
124
|
-
export const
|
|
88
|
+
export const system = new ClientSystem();
|
package/dist/theme.d.ts
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
|
+
import { type Theme, type ThemePreference } from "@phreshos/core";
|
|
1
2
|
import Events from "./events.js";
|
|
2
|
-
/**
|
|
3
|
+
/** Mutable Theme local to the current Desktop. */
|
|
3
4
|
export default class ClientTheme extends Events {
|
|
4
5
|
constructor();
|
|
5
|
-
snapshot(): Promise<
|
|
6
|
-
|
|
7
|
-
foreground: string;
|
|
8
|
-
accent: string;
|
|
9
|
-
spacing: number;
|
|
10
|
-
radius: number;
|
|
11
|
-
surface: import("@phreshos/core").ThemeSurface;
|
|
12
|
-
}>>;
|
|
6
|
+
snapshot(): Promise<Theme>;
|
|
7
|
+
update(theme: ThemePreference): Promise<void>;
|
|
13
8
|
}
|
package/dist/theme.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {} from "@phreshos/core";
|
|
2
2
|
import Events from "./events.js";
|
|
3
3
|
import wire from "./wire.js";
|
|
4
|
-
/**
|
|
4
|
+
/** Mutable Theme local to the current Desktop. */
|
|
5
5
|
export default class ClientTheme extends Events {
|
|
6
6
|
constructor() {
|
|
7
|
-
super((event, listener, impossible) => wire.on("host-theme", event, value => {
|
|
8
|
-
listener(createThemeSnapshot(value));
|
|
9
|
-
}, null, impossible), observer => wire.onAll("host-theme", (event, value) => {
|
|
7
|
+
super((event, listener, impossible) => wire.on("host-theme", event, value => listener(value), null, impossible), observer => wire.onAll("host-theme", (event, value) => {
|
|
10
8
|
if (typeof event === "string")
|
|
11
|
-
observer(event,
|
|
9
|
+
observer(event, value);
|
|
12
10
|
}));
|
|
13
11
|
}
|
|
14
12
|
async snapshot() {
|
|
15
13
|
const [theme] = await wire.request(["theme"]);
|
|
16
|
-
return
|
|
14
|
+
return theme;
|
|
15
|
+
}
|
|
16
|
+
async update(theme) {
|
|
17
|
+
await wire.request(["update-theme", theme]);
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type SystemUploads, type Upload } from "@phreshos/core";
|
|
2
|
+
/** Flat upload access transported through the Client's Desktop boundary. */
|
|
3
|
+
declare class ClientUploads implements SystemUploads {
|
|
4
|
+
write(value: unknown): Promise<Upload>;
|
|
5
|
+
stream(file: string): Promise<ReadableStream<Uint8Array>>;
|
|
6
|
+
bytes(file: string): Promise<Uint8Array<ArrayBuffer>>;
|
|
7
|
+
text(file: string): Promise<string>;
|
|
8
|
+
json<Value>(file: string): Promise<Value>;
|
|
9
|
+
stat(file: string): Promise<Upload | null>;
|
|
10
|
+
}
|
|
11
|
+
export declare const uploads: ClientUploads;
|
|
12
|
+
export {};
|
package/dist/uploads.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { isUploadFile } from "@phreshos/core";
|
|
2
|
+
import { content } from "./content.js";
|
|
3
|
+
import controlledStream from "./controlled-stream.js";
|
|
4
|
+
import wire from "./wire.js";
|
|
5
|
+
/** Flat upload access transported through the Client's Desktop boundary. */
|
|
6
|
+
class ClientUploads {
|
|
7
|
+
async write(value) {
|
|
8
|
+
const source = content(value);
|
|
9
|
+
const channel = new MessageChannel();
|
|
10
|
+
const abort = () => channel.port1.postMessage("abort");
|
|
11
|
+
try {
|
|
12
|
+
const answer = await wire.request(["uploads", "write", source.body, { extension: source.extension, type: source.type }, channel.port2], undefined, source.body instanceof ReadableStream ? [source.body, channel.port2] : [channel.port2]);
|
|
13
|
+
channel.port1.close();
|
|
14
|
+
return answer[0];
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
abort();
|
|
18
|
+
channel.port1.close();
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async stream(file) {
|
|
23
|
+
requireFile(file);
|
|
24
|
+
const channel = new MessageChannel();
|
|
25
|
+
const abort = () => channel.port1.postMessage("abort");
|
|
26
|
+
const close = () => channel.port1.close();
|
|
27
|
+
try {
|
|
28
|
+
const answer = await wire.request(["uploads", "stream", file, channel.port2], undefined, [channel.port2]);
|
|
29
|
+
if (!(answer[0] instanceof ReadableStream))
|
|
30
|
+
throw new Error("The upload response has no byte stream");
|
|
31
|
+
return controlledStream(answer[0], abort, close);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
abort();
|
|
35
|
+
close();
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async bytes(file) {
|
|
40
|
+
return new Uint8Array(await new Response(await this.stream(file)).arrayBuffer());
|
|
41
|
+
}
|
|
42
|
+
async text(file) {
|
|
43
|
+
return new Response(await this.stream(file)).text();
|
|
44
|
+
}
|
|
45
|
+
async json(file) {
|
|
46
|
+
return JSON.parse(await this.text(file));
|
|
47
|
+
}
|
|
48
|
+
async stat(file) {
|
|
49
|
+
requireFile(file);
|
|
50
|
+
const answer = await wire.request(["uploads", "stat", file]);
|
|
51
|
+
return answer[0];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function requireFile(file) {
|
|
55
|
+
if (!isUploadFile(file))
|
|
56
|
+
throw new Error("That is not an upload file");
|
|
57
|
+
}
|
|
58
|
+
export const uploads = new ClientUploads();
|
package/dist/wire.js
CHANGED
|
@@ -319,7 +319,7 @@ class Wire {
|
|
|
319
319
|
stream.failure = new Error("The boundary produced a stream value before opening the stream");
|
|
320
320
|
else if (operation === "data") {
|
|
321
321
|
if (stream.queue.length >= maximumStreamQueue)
|
|
322
|
-
stream.failure = new Error(`
|
|
322
|
+
stream.failure = new Error(`System stream queue exceeded its capacity of ${maximumStreamQueue}`);
|
|
323
323
|
else
|
|
324
324
|
stream.queue.push(value);
|
|
325
325
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "The SDK used by a Program's client endpoint.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
"prepack": "node --run build"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@phreshos/core": "^0.1.
|
|
50
|
+
"@phreshos/core": "^0.1.17"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
53
|
"@msgpack/msgpack": "^3.1.3"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@phreshos/core": "^0.1.
|
|
56
|
+
"@phreshos/core": "^0.1.18",
|
|
57
57
|
"typescript": "^6.0.3"
|
|
58
58
|
}
|
|
59
59
|
}
|