@phreshos/client 0.1.28 → 0.1.29
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 +52 -192
- package/dist/desktop/desktop.d.ts +13 -0
- package/dist/desktop/desktop.js +9 -0
- package/dist/desktop/pointer.d.ts +11 -0
- package/dist/desktop/pointer.js +59 -0
- package/dist/desktop/preferences.d.ts +11 -0
- package/dist/{desktop-preferences.js → desktop/preferences.js} +4 -5
- package/dist/desktop/surface.d.ts +8 -0
- package/dist/desktop/surface.js +37 -0
- package/dist/main.d.ts +2 -3
- package/dist/main.js +1 -2
- package/dist/system.d.ts +3 -8
- package/dist/system.js +1 -5
- package/package.json +3 -3
- package/dist/desktop-preferences.d.ts +0 -11
- package/dist/desktop.d.ts +0 -25
- package/dist/desktop.js +0 -34
- package/dist/pointer.d.ts +0 -35
- package/dist/pointer.js +0 -54
package/README.md
CHANGED
|
@@ -1,224 +1,84 @@
|
|
|
1
1
|
# `@phreshos/client`
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
Program's client endpoint.
|
|
3
|
+
The SDK for a PhreshOS Program's Client Endpoint.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
The Client SDK adapts the sandboxed desktop boundary to the shared Core domain
|
|
6
|
+
model. It exposes the restricted `system` and `context` available inside a
|
|
7
|
+
Client without redefining Program, Process, Endpoint, Server, or Client.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
active testing. The architecture's components will be released in stages as
|
|
10
|
-
their contracts and integrations are verified.
|
|
9
|
+
## Installation
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
| Package manager | Command |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| npm | `npm install @phreshos/client` |
|
|
14
|
+
| pnpm | `pnpm add @phreshos/client` |
|
|
15
|
+
| Bun | `bun add @phreshos/client` |
|
|
16
|
+
| Yarn | `yarn add @phreshos/client` |
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
a peer dependency. It does not redefine those objects, own server-side
|
|
18
|
-
capabilities, or contain System and transport implementations.
|
|
18
|
+
`@phreshos/core` is a peer dependency.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
System Appearance, mutable preferences local to this Desktop, desktop and
|
|
22
|
-
pointer state, flat public uploads, and unrestricted server-side Fetch.
|
|
23
|
-
`system.appearance.snapshot()` reads complete unresolved authoritative state.
|
|
24
|
-
`system.desktopPreferences.snapshot()` reads the complete effective
|
|
25
|
-
`{ theme, animations }` state. Its updates may set either preference explicitly
|
|
26
|
-
or use `"default"` to resume following the native environment. Both capabilities
|
|
27
|
-
expose ordinary live-only `change` subscriptions with no initial replay. A
|
|
28
|
-
Client cannot write authoritative Appearance or discover system-wide Programs
|
|
29
|
-
and Processes.
|
|
30
|
-
|
|
31
|
-
`system.uploads` has the same flat contract as the Server SDK. It writes a
|
|
32
|
-
value, then reads or describes it using exactly one opaque generated file key;
|
|
33
|
-
it exposes no filesystem path, path segments, listing, deletion, or clearing.
|
|
34
|
-
|
|
35
|
-
Desktop and pointer access are independent objects rather than events merged
|
|
36
|
-
into System:
|
|
20
|
+
## Context
|
|
37
21
|
|
|
38
22
|
```ts
|
|
39
|
-
|
|
40
|
-
const stopDesktop = system.desktop.subscribe("resize", next => undefined)
|
|
23
|
+
import { context } from "@phreshos/client"
|
|
41
24
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
The desktop size is the complete desktop area containing this Client,
|
|
47
|
-
independent of the Client Window's layer. Client code cannot select a layer,
|
|
48
|
-
and the desktop's gutter never enters an endpoint. Pointer position and
|
|
49
|
-
movement both require the `pointer` permission. Reads are asynchronous
|
|
50
|
-
requests; subscriptions receive only future publications and never replay a
|
|
51
|
-
retained value.
|
|
25
|
+
context.subscribe("changed", message => {
|
|
26
|
+
console.log(message)
|
|
27
|
+
})
|
|
52
28
|
|
|
53
|
-
|
|
54
|
-
They are available from a Program handle and flattened through `context` as the
|
|
55
|
-
same canonical capability:
|
|
29
|
+
context.publish("changed", { value: 1 })
|
|
56
30
|
|
|
57
|
-
```ts
|
|
58
31
|
const program = await context.program()
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
await context.permission.timeout(5_000).request("pointer")
|
|
63
|
-
|
|
64
|
-
context.permission === program.permission
|
|
32
|
+
const process = await context.process()
|
|
33
|
+
const server = context.server
|
|
34
|
+
const window = context.window
|
|
65
35
|
```
|
|
66
36
|
|
|
67
|
-
`
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
permission ownership does not alter the public shape of `system`.
|
|
37
|
+
`context` belongs to the executing Client. It provides communication,
|
|
38
|
+
navigation to its Program and Process, its paired Server, its Window, and
|
|
39
|
+
Client-owned capabilities.
|
|
71
40
|
|
|
72
|
-
|
|
73
|
-
does not read or start the Service and exposes no Service registry:
|
|
41
|
+
## System
|
|
74
42
|
|
|
75
43
|
```ts
|
|
76
|
-
|
|
77
|
-
program: "counter",
|
|
78
|
-
process: "main",
|
|
79
|
-
endpoint: "server"
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
await service.waitReady()
|
|
83
|
-
if (await service.exists()) await service.ask("value")
|
|
44
|
+
import { system } from "@phreshos/client"
|
|
84
45
|
|
|
85
|
-
|
|
86
|
-
|
|
46
|
+
const appearance = await system.appearance.snapshot()
|
|
47
|
+
const surface = await system.desktop.surface.snapshot()
|
|
48
|
+
const pointer = await system.desktop.pointer.snapshot()
|
|
49
|
+
const preferences = await system.desktop.preferences.snapshot()
|
|
87
50
|
```
|
|
88
51
|
|
|
89
|
-
The
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
A Client may traverse `Process.parent()` through any number of ancestors in
|
|
95
|
-
its own Program. The first parent outside that Program is structurally hidden
|
|
96
|
-
and returned as `null`; no cross-Program Process handle enters the client. If
|
|
97
|
-
client code fabricates or otherwise obtains an unauthorized handle, the system
|
|
98
|
-
responds exactly as it does for a nonexistent Process.
|
|
99
|
-
|
|
100
|
-
Its JavaScript entry point adapts the iframe boundary to these contracts. The
|
|
101
|
-
SDK owns callbacks, waits, queues, and their cleanup; the boundary owns only
|
|
102
|
-
the forwarding registrations requested by the SDK.
|
|
103
|
-
|
|
104
|
-
Importing the SDK and establishing the iframe's host lease inject no message
|
|
105
|
-
into the endpoint. Identity, Theme, Process, Window, readiness, lifecycle, and
|
|
106
|
-
application values enter only in response to an explicit request or a live
|
|
107
|
-
registration made by Program code.
|
|
108
|
-
|
|
109
|
-
`Context` combines communication with navigation into the executing Client's
|
|
110
|
-
Process. The paired Server is explicitly named as
|
|
111
|
-
`context.server`; its publishing, asking, existence, readiness, start, and stop
|
|
112
|
-
operations never masquerade as properties of `context`. `context.stop()` stops
|
|
113
|
-
the executing Client, while complete Process exit remains available only
|
|
114
|
-
through `context.process()`. It is the canonical Process-owned handle, so
|
|
115
|
-
`context.server === (await context.process()).server`.
|
|
116
|
-
Endpoint `process()` navigation is asynchronous; contextual ownership is
|
|
117
|
-
requested only when navigation needs it and then retained by the SDK.
|
|
118
|
-
`context.name()` returns that retained Process's Program-local name, or `null`
|
|
119
|
-
when its launch was unnamed.
|
|
120
|
-
|
|
121
|
-
All domain handles are canonical within this iframe's JavaScript realm. Lookup,
|
|
122
|
-
navigation, event payloads, and message metadata reuse the same weakly retained
|
|
123
|
-
handle. A Client and its synchronous `window` capability remain stable for the
|
|
124
|
-
Process lifetime; Window operations always address that Client's current live
|
|
125
|
-
presentation state.
|
|
126
|
-
|
|
127
|
-
`server.ask()` does not route a question before the current Server incarnation
|
|
128
|
-
is ready. The Client SDK owns one deadline across readiness and the answer;
|
|
129
|
-
absence or incarnation loss rejects without turning the boundary into a waiter.
|
|
130
|
-
|
|
131
|
-
The package provides two contextual runtime entry points:
|
|
52
|
+
The Client System exposes only capabilities allowed by the desktop boundary:
|
|
53
|
+
read-only Appearance, desktop surface and pointer state, writable desktop
|
|
54
|
+
preferences, uploads, Fetch, and exact Service handles. It does not expose
|
|
55
|
+
system-wide Program or Process registries.
|
|
132
56
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
```
|
|
57
|
+
Requests read current state. Subscriptions observe future publications and do
|
|
58
|
+
not replay an initial value. Importing the SDK performs neither operation.
|
|
136
59
|
|
|
137
|
-
|
|
138
|
-
`Endpoint`, `Server`, and `Client`—and refines the handles returned through
|
|
139
|
-
them. These are the same domain classes used by the Server SDK, so
|
|
140
|
-
`instanceof Server` and `instanceof Client` retain one meaning. `Window`, like
|
|
141
|
-
`ClientTraffic`, is a type-only capability owned by Client and has no
|
|
142
|
-
independent `instanceof` identity.
|
|
143
|
-
|
|
144
|
-
The executing Client's communication belongs directly to `context`. Its
|
|
145
|
-
subscription tools receive events addressed to this Client, while `publish()`
|
|
146
|
-
emits outward from this Client without choosing a destination. Existence and
|
|
147
|
-
readiness belong to `context.server`; the current Client stops through
|
|
148
|
-
`context.stop()`, and explicit Endpoint handles expose their own
|
|
149
|
-
`endpoint.lifecycle` subscriptions.
|
|
150
|
-
|
|
151
|
-
An Endpoint handle is also a selective source: `endpoint.subscribe()` follows
|
|
152
|
-
destinationless events emitted by that Endpoint. Its `traffic` property remains
|
|
153
|
-
reserved for directed publications, questions, and answers.
|
|
154
|
-
|
|
155
|
-
Messages sent by an Endpoint in the same Program contain that real Endpoint in
|
|
156
|
-
`from`. If the sender belongs to another Program, `from` is `null`; the foreign
|
|
157
|
-
identity is removed by the authoritative System before the message reaches the desktop.
|
|
158
|
-
The same rule applies to Client-visible traffic destinations.
|
|
159
|
-
|
|
160
|
-
Client-visible Program handles can operate only within their own Program.
|
|
161
|
-
They deliberately omit `install()` and `fork()`, and their `Storage` values never
|
|
162
|
-
expose native filesystem paths. Every Client-side Window handle exposes the same
|
|
163
|
-
authoritative, subscribable Window capability and its `window.local` physical
|
|
164
|
-
representation on this desktop. Local reads and updates have no events and do
|
|
165
|
-
not change Server state. `context.window` is only convenient access to the
|
|
166
|
-
executing Client's canonical Window; it has no additional authority.
|
|
167
|
-
|
|
168
|
-
A Client may uninstall only its own Program. The operation is an async
|
|
169
|
-
generator so a declared Server cleanup command remains observable without a
|
|
170
|
-
fixed answer timeout:
|
|
60
|
+
## Development
|
|
171
61
|
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
62
|
+
```sh
|
|
63
|
+
bun install --frozen-lockfile
|
|
64
|
+
bun run verify
|
|
176
65
|
```
|
|
177
66
|
|
|
178
|
-
|
|
179
|
-
|
|
67
|
+
`verify` checks the source, completion surface, build, and published package
|
|
68
|
+
shape.
|
|
180
69
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
name: "shared-server",
|
|
184
|
-
server: true,
|
|
185
|
-
client: false
|
|
186
|
-
})
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
The authoritative Core returns the existing Process only when its normalized
|
|
190
|
-
launch is equivalent; a conflicting launch rejects without changing it.
|
|
70
|
+
See the [Client and Server documentation](https://github.com/PhreshOS/docs/blob/main/content/docs/sdks/client-and-server.mdx)
|
|
71
|
+
for the shared model and authority boundary.
|
|
191
72
|
|
|
192
|
-
|
|
193
|
-
them through one authoritative request and produces one `geometry` event.
|
|
194
|
-
Calling `move()` and `resize()` sequentially or through `Promise.all()` remains
|
|
195
|
-
two independent operations and can expose an intermediate state remotely.
|
|
73
|
+
## Repository boundary
|
|
196
74
|
|
|
197
|
-
|
|
75
|
+
This repository owns the Client runtime adapter. Core owns the domain model, the
|
|
76
|
+
System owns enforcement and forwarding, React owns framework adaptation, and
|
|
77
|
+
React UI owns visual interpretation.
|
|
198
78
|
|
|
199
|
-
|
|
200
|
-
|
|
79
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the repository workflow and
|
|
80
|
+
[SECURITY.md](SECURITY.md) for private vulnerability reporting.
|
|
201
81
|
|
|
202
|
-
|
|
203
|
-
```
|
|
82
|
+
## License
|
|
204
83
|
|
|
205
|
-
|
|
206
|
-
representation. Reloading or destroying it resets the representation from
|
|
207
|
-
authoritative truth, while other desktops remain unaffected. Program code may
|
|
208
|
-
synchronize desired presence through its Server and explicitly apply it
|
|
209
|
-
again. `set()` and `remove()` accept only a required `VisibilityTransition`;
|
|
210
|
-
Programs cannot configure the System Surface's material, opacity, or radius.
|
|
211
|
-
The transition uses milliseconds and a stable named or cubic Bézier easing;
|
|
212
|
-
the desktop performs the motion, honors reduced motion, and removes the Surface
|
|
213
|
-
only after its exit transition completes. `wait: true` makes the request settle
|
|
214
|
-
with that transition. A new iframe representation restores nothing. The
|
|
215
|
-
container follows the iframe geometry while the independently rounded Surface
|
|
216
|
-
neither clips nor masks Client content. The ordinary `window` layer rejects
|
|
217
|
-
the capability.
|
|
218
|
-
|
|
219
|
-
`program.icon()` requests the current Program's guaranteed PNG `Blob` on
|
|
220
|
-
demand. The desktop derives the Program from the calling frame; no identity,
|
|
221
|
-
private asset address, or filesystem path crosses from Client code.
|
|
222
|
-
|
|
223
|
-
Persistent startup is deliberately absent. Only a Server endpoint may change
|
|
224
|
-
whether an installed Program creates a Process when the system starts.
|
|
84
|
+
Licensed under the [MIT License](LICENSE). Copyright © 2026 Zohayr SLILEH.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DesktopPointerSource, DesktopSurfaceSource, WritableDesktopPreferencesSource } from "@phreshos/core";
|
|
2
|
+
/** Every capability owned by the Desktop containing this Client. */
|
|
3
|
+
export interface SystemDesktop {
|
|
4
|
+
readonly surface: DesktopSurfaceSource;
|
|
5
|
+
readonly pointer: DesktopPointerSource;
|
|
6
|
+
readonly preferences: WritableDesktopPreferencesSource;
|
|
7
|
+
}
|
|
8
|
+
/** Desktop access bound to the current Client Process boundary. */
|
|
9
|
+
export default class ClientDesktop implements SystemDesktop {
|
|
10
|
+
readonly surface: DesktopSurfaceSource;
|
|
11
|
+
readonly pointer: DesktopPointerSource;
|
|
12
|
+
readonly preferences: WritableDesktopPreferencesSource;
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import ClientPointer from "./pointer.js";
|
|
2
|
+
import ClientPreferences from "./preferences.js";
|
|
3
|
+
import ClientSurface from "./surface.js";
|
|
4
|
+
/** Desktop access bound to the current Client Process boundary. */
|
|
5
|
+
export default class ClientDesktop {
|
|
6
|
+
surface = new ClientSurface();
|
|
7
|
+
pointer = new ClientPointer();
|
|
8
|
+
preferences = new ClientPreferences();
|
|
9
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import Events from "../events.js";
|
|
2
|
+
/** Permission-guarded access to the Desktop pointer visible to this Client. */
|
|
3
|
+
export default class ClientPointer extends Events {
|
|
4
|
+
private listeners;
|
|
5
|
+
private readonly sample;
|
|
6
|
+
constructor();
|
|
7
|
+
snapshot(): Promise<Readonly<{
|
|
8
|
+
position: import("@phreshos/core").DesktopPointerPosition | null;
|
|
9
|
+
}>>;
|
|
10
|
+
private withSampling;
|
|
11
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import Events from "../events.js";
|
|
2
|
+
import wire from "../wire.js";
|
|
3
|
+
/** Permission-guarded access to the Desktop pointer visible to this Client. */
|
|
4
|
+
export default class ClientPointer extends Events {
|
|
5
|
+
listeners = 0;
|
|
6
|
+
sample = (event) => wire.samplePointer(event.movementX, event.movementY);
|
|
7
|
+
constructor() {
|
|
8
|
+
super((event, listener, impossible) => this.withSampling(event === "move", wire.on("host-desktop-pointer", event, value => {
|
|
9
|
+
const snapshot = createSnapshot(value);
|
|
10
|
+
if (snapshot)
|
|
11
|
+
listener(snapshot);
|
|
12
|
+
}, null, impossible)), observer => this.withSampling(true, wire.onAll("host-desktop-pointer", (event, value) => {
|
|
13
|
+
const snapshot = createSnapshot(value);
|
|
14
|
+
if (event === "move" && snapshot)
|
|
15
|
+
observer(event, snapshot);
|
|
16
|
+
})));
|
|
17
|
+
}
|
|
18
|
+
async snapshot() {
|
|
19
|
+
const answer = await wire.request(["desktopPointer"]);
|
|
20
|
+
const snapshot = createSnapshot(answer[0]);
|
|
21
|
+
if (!snapshot)
|
|
22
|
+
throw new Error("The Desktop returned an invalid pointer");
|
|
23
|
+
return snapshot;
|
|
24
|
+
}
|
|
25
|
+
withSampling(sample, stop) {
|
|
26
|
+
if (!sample)
|
|
27
|
+
return stop;
|
|
28
|
+
if (this.listeners++ === 0)
|
|
29
|
+
window.addEventListener("pointermove", this.sample);
|
|
30
|
+
let active = true;
|
|
31
|
+
return () => {
|
|
32
|
+
if (!active)
|
|
33
|
+
return;
|
|
34
|
+
active = false;
|
|
35
|
+
stop();
|
|
36
|
+
this.listeners--;
|
|
37
|
+
if (this.listeners === 0)
|
|
38
|
+
window.removeEventListener("pointermove", this.sample);
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function createSnapshot(value) {
|
|
43
|
+
if (!record(value))
|
|
44
|
+
return null;
|
|
45
|
+
if (value.position === null)
|
|
46
|
+
return Object.freeze({ position: null });
|
|
47
|
+
if (!record(value.position))
|
|
48
|
+
return null;
|
|
49
|
+
const { x, y } = value.position;
|
|
50
|
+
if (!finite(x) || !finite(y))
|
|
51
|
+
return null;
|
|
52
|
+
return Object.freeze({ position: Object.freeze({ x, y }) });
|
|
53
|
+
}
|
|
54
|
+
function record(value) {
|
|
55
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
56
|
+
}
|
|
57
|
+
function finite(value) {
|
|
58
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
59
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DesktopPreferencesUpdate } from "@phreshos/core";
|
|
2
|
+
import Events from "../events.js";
|
|
3
|
+
/** Effective preferences owned by the Desktop containing this Client. */
|
|
4
|
+
export default class ClientPreferences extends Events {
|
|
5
|
+
constructor();
|
|
6
|
+
snapshot(): Promise<Readonly<{
|
|
7
|
+
theme: import("@phreshos/core").Theme;
|
|
8
|
+
animations: boolean;
|
|
9
|
+
}>>;
|
|
10
|
+
update(preferences: DesktopPreferencesUpdate): Promise<void>;
|
|
11
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export default class ClientDesktopPreferences extends Events {
|
|
1
|
+
import Events from "../events.js";
|
|
2
|
+
import wire from "../wire.js";
|
|
3
|
+
/** Effective preferences owned by the Desktop containing this Client. */
|
|
4
|
+
export default class ClientPreferences extends Events {
|
|
6
5
|
constructor() {
|
|
7
6
|
super((event, listener, impossible) => wire.on("host-desktop-preferences", event, value => {
|
|
8
7
|
listener(value);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import Events from "../events.js";
|
|
2
|
+
/** Read-only access to the Desktop surface containing this Client. */
|
|
3
|
+
export default class ClientSurface extends Events {
|
|
4
|
+
constructor();
|
|
5
|
+
snapshot(): Promise<Readonly<{
|
|
6
|
+
size: import("@phreshos/core").DesktopSize;
|
|
7
|
+
}>>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Events from "../events.js";
|
|
2
|
+
import wire from "../wire.js";
|
|
3
|
+
/** Read-only access to the Desktop surface containing this Client. */
|
|
4
|
+
export default class ClientSurface extends Events {
|
|
5
|
+
constructor() {
|
|
6
|
+
super((event, listener, impossible) => wire.on("host-desktop-surface", event, value => {
|
|
7
|
+
const snapshot = createSnapshot(value);
|
|
8
|
+
if (snapshot)
|
|
9
|
+
listener(snapshot);
|
|
10
|
+
}, null, impossible), observer => wire.onAll("host-desktop-surface", (event, value) => {
|
|
11
|
+
const snapshot = createSnapshot(value);
|
|
12
|
+
if (typeof event === "string" && snapshot)
|
|
13
|
+
observer(event, snapshot);
|
|
14
|
+
}));
|
|
15
|
+
}
|
|
16
|
+
async snapshot() {
|
|
17
|
+
const answer = await wire.request(["desktopSurface"]);
|
|
18
|
+
const snapshot = createSnapshot(answer[0]);
|
|
19
|
+
if (!snapshot)
|
|
20
|
+
throw new Error("The System returned an invalid Desktop surface");
|
|
21
|
+
return snapshot;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function createSnapshot(value) {
|
|
25
|
+
if (!record(value) || !record(value.size))
|
|
26
|
+
return null;
|
|
27
|
+
const { width, height } = value.size;
|
|
28
|
+
if (!finite(width) || !finite(height))
|
|
29
|
+
return null;
|
|
30
|
+
return Object.freeze({ size: Object.freeze({ width, height }) });
|
|
31
|
+
}
|
|
32
|
+
function record(value) {
|
|
33
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
function finite(value) {
|
|
36
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
37
|
+
}
|
package/dist/main.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export { system, type System } from "./system.js";
|
|
2
|
-
export { type
|
|
3
|
-
export { type DesktopEvents, type DesktopSize, type SystemDesktop } from "./desktop.js";
|
|
2
|
+
export { type SystemDesktop } from "./desktop/desktop.js";
|
|
4
3
|
export { context, type Context, type ContextCapture, type ContextEvents, type ContextMessage, type ContextServer } from "./context.js";
|
|
5
4
|
export { ClientService, ServerService, Service, type ServiceKey, } from "@phreshos/core";
|
|
6
5
|
export { Client, Endpoint, Process, Program, Server, type Window, type ProgramProcess, type AnswerCapture, type AnswerMessage, type AnswerSubscriber, type AskCapture, type AskMessage, type AskSubscriber, type ClientTraffic, type EndpointTraffic, type ServerTraffic, type TrafficCapture, type TrafficEvents, type TrafficMessage } from "./domain.js";
|
|
7
|
-
export type { Askable, Capture, Captures, CaptureSubscriber, ClientLaunch, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointLifecycle, EndpointLifecycleEvents, EntryStat, EventMessage, EventName, EventOptions, EventSubscriber, Exit, FileStat, Launch, ServerLaunch, 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, AnimationsPreference, DesktopPreferences, DesktopPreferencesEvents, DesktopPreferencesUpdate,
|
|
6
|
+
export type { Askable, Capture, Captures, CaptureSubscriber, ClientLaunch, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointLifecycle, EndpointLifecycleEvents, EntryStat, EventMessage, EventName, EventOptions, EventSubscriber, Exit, FileStat, Launch, ServerLaunch, 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, AnimationsPreference, DesktopPreferences, DesktopPreferencesEvents, DesktopPreferencesSource, DesktopPreferencesUpdate, DesktopPointerEvents, DesktopPointerPosition, DesktopPointerSnapshot, DesktopPointerSource, DesktopSize, DesktopSurfaceEvents, DesktopSurfaceSnapshot, DesktopSurfaceSource, WritableDesktopPreferencesSource, ThemedValue, Theme, ThemePreference, Value, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowSurface, VisibilityTransition, Transaction } from "@phreshos/core";
|
package/dist/main.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { system } from "./system.js";
|
|
2
|
-
export {} from "./
|
|
3
|
-
export {} from "./desktop.js";
|
|
2
|
+
export {} from "./desktop/desktop.js";
|
|
4
3
|
export { context } from "./context.js";
|
|
5
4
|
export { ClientService, ServerService, Service, } from "@phreshos/core";
|
|
6
5
|
export { Client, Endpoint, Process, Program, Server } from "./domain.js";
|
package/dist/system.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { AppearanceSource, ClientService,
|
|
2
|
-
import { type
|
|
3
|
-
import { type SystemDesktop } from "./desktop.js";
|
|
1
|
+
import type { AppearanceSource, ClientService, ServerService, ServiceKey, SystemUploads } from "@phreshos/core";
|
|
2
|
+
import { type SystemDesktop } from "./desktop/desktop.js";
|
|
4
3
|
type ServiceEndpoint = ServiceKey["endpoint"];
|
|
5
4
|
type ServiceAddress<Endpoint extends ServiceEndpoint> = Omit<ServiceKey, "endpoint"> & Readonly<{
|
|
6
5
|
endpoint: Endpoint;
|
|
@@ -10,12 +9,8 @@ type ServiceHandle<Endpoint extends ServiceEndpoint, Events extends object> = En
|
|
|
10
9
|
export interface System {
|
|
11
10
|
/** Complete unresolved Appearance read from the System authority. */
|
|
12
11
|
readonly appearance: AppearanceSource;
|
|
13
|
-
/**
|
|
14
|
-
readonly desktopPreferences: SystemDesktopPreferences;
|
|
15
|
-
/** Layer-independent desktop size reads and live updates. */
|
|
12
|
+
/** Capabilities owned by the Desktop containing this Client. */
|
|
16
13
|
readonly desktop: SystemDesktop;
|
|
17
|
-
/** Permission-guarded desktop pointer reads and live movement. */
|
|
18
|
-
readonly pointer: SystemPointer;
|
|
19
14
|
/** Flat System-owned public uploads capability. */
|
|
20
15
|
readonly uploads: SystemUploads;
|
|
21
16
|
/** Performs an unrestricted server-side fetch on behalf of this Client. */
|
package/dist/system.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import ClientAppearance from "./appearance.js";
|
|
2
|
-
import ClientDesktopPreferences from "./desktop-preferences.js";
|
|
3
2
|
import wire from "./wire.js";
|
|
4
|
-
import
|
|
5
|
-
import ClientDesktop, {} from "./desktop.js";
|
|
3
|
+
import ClientDesktop, {} from "./desktop/desktop.js";
|
|
6
4
|
import { prepareService } from "./service.js";
|
|
7
5
|
import { uploads } from "./uploads.js";
|
|
8
6
|
import controlledStream from "./controlled-stream.js";
|
|
9
7
|
class ClientSystem {
|
|
10
8
|
appearance = new ClientAppearance();
|
|
11
|
-
desktopPreferences = new ClientDesktopPreferences();
|
|
12
9
|
desktop = new ClientDesktop();
|
|
13
|
-
pointer = new ClientPointer();
|
|
14
10
|
uploads = uploads;
|
|
15
11
|
service(key) { return prepareService(key); }
|
|
16
12
|
async fetch(input, init) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phreshos/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
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.31"
|
|
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.31",
|
|
57
57
|
"typescript": "^6.0.3"
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type DesktopPreferencesUpdate } from "@phreshos/core";
|
|
2
|
-
import Events from "./events.js";
|
|
3
|
-
/** Mutable effective preferences local to the current Desktop. */
|
|
4
|
-
export default class ClientDesktopPreferences extends Events {
|
|
5
|
-
constructor();
|
|
6
|
-
snapshot(): Promise<Readonly<{
|
|
7
|
-
theme: import("@phreshos/core").Theme;
|
|
8
|
-
animations: boolean;
|
|
9
|
-
}>>;
|
|
10
|
-
update(preferences: DesktopPreferencesUpdate): Promise<void>;
|
|
11
|
-
}
|
package/dist/desktop.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { Subscribable } from "@phreshos/core";
|
|
2
|
-
import Events from "./events.js";
|
|
3
|
-
/** The complete measured desktop area in CSS pixels. */
|
|
4
|
-
export type DesktopSize = Readonly<{
|
|
5
|
-
width: number;
|
|
6
|
-
height: number;
|
|
7
|
-
}>;
|
|
8
|
-
/** Live changes to the desktop area containing this Client. */
|
|
9
|
-
export type DesktopEvents = {
|
|
10
|
-
/** The desktop area resized. */
|
|
11
|
-
resize: DesktopSize;
|
|
12
|
-
};
|
|
13
|
-
/** Explicit desktop size reads and future resizes. */
|
|
14
|
-
export interface SystemDesktop extends Subscribable<DesktopEvents, never> {
|
|
15
|
-
/** Reads the complete current desktop area in CSS pixels. */
|
|
16
|
-
size(): Promise<DesktopSize>;
|
|
17
|
-
}
|
|
18
|
-
/** Desktop access bound to the current Client Process boundary. */
|
|
19
|
-
export default class ClientDesktop extends Events {
|
|
20
|
-
constructor();
|
|
21
|
-
size(): Promise<Readonly<{
|
|
22
|
-
width: number;
|
|
23
|
-
height: number;
|
|
24
|
-
}>>;
|
|
25
|
-
}
|
package/dist/desktop.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import Events from "./events.js";
|
|
2
|
-
import wire from "./wire.js";
|
|
3
|
-
/** Desktop access bound to the current Client Process boundary. */
|
|
4
|
-
export default class ClientDesktop extends Events {
|
|
5
|
-
constructor() {
|
|
6
|
-
super((event, listener, impossible) => wire.on("host-desktop", event, value => {
|
|
7
|
-
const size = createSize(value);
|
|
8
|
-
if (size)
|
|
9
|
-
listener(size);
|
|
10
|
-
}, null, impossible), observer => wire.onAll("host-desktop", (event, value) => {
|
|
11
|
-
const size = createSize(value);
|
|
12
|
-
if (typeof event === "string" && size)
|
|
13
|
-
observer(event, size);
|
|
14
|
-
}));
|
|
15
|
-
}
|
|
16
|
-
async size() {
|
|
17
|
-
const answer = await wire.request(["desktop"]);
|
|
18
|
-
const size = createSize(answer[0]);
|
|
19
|
-
if (!size)
|
|
20
|
-
throw new Error("The system returned an invalid desktop size");
|
|
21
|
-
return size;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function createSize(value) {
|
|
25
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
26
|
-
return null;
|
|
27
|
-
const size = value;
|
|
28
|
-
if (!finite(size.width) || !finite(size.height))
|
|
29
|
-
return null;
|
|
30
|
-
return Object.freeze({ width: size.width, height: size.height });
|
|
31
|
-
}
|
|
32
|
-
function finite(value) {
|
|
33
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
34
|
-
}
|
package/dist/pointer.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import type { Subscribable } from "@phreshos/core";
|
|
2
|
-
import Events from "./events.js";
|
|
3
|
-
/** Pointer coordinates relative to the desktop display core. */
|
|
4
|
-
export type PointerPosition = Readonly<{
|
|
5
|
-
/** Horizontal coordinate in CSS pixels. */
|
|
6
|
-
x: number;
|
|
7
|
-
/** Vertical coordinate in CSS pixels. */
|
|
8
|
-
y: number;
|
|
9
|
-
}>;
|
|
10
|
-
/** Live pointer events visible to this Client. */
|
|
11
|
-
export type PointerEvents = {
|
|
12
|
-
/** The pointer moved over the desktop display core. */
|
|
13
|
-
move: PointerPosition;
|
|
14
|
-
};
|
|
15
|
-
/** Permission-guarded pointer positions and future movement. */
|
|
16
|
-
export interface SystemPointer extends Subscribable<PointerEvents, never> {
|
|
17
|
-
/**
|
|
18
|
-
* Reads the current desktop pointer position, or `null` before one is known.
|
|
19
|
-
* Rejects unless the `pointer` permission is currently granted.
|
|
20
|
-
*/
|
|
21
|
-
position(): Promise<PointerPosition | null>;
|
|
22
|
-
}
|
|
23
|
-
/** Client pointer access bound to the current Process boundary. */
|
|
24
|
-
export default class ClientPointer extends Events {
|
|
25
|
-
private listeners;
|
|
26
|
-
private readonly sample;
|
|
27
|
-
constructor();
|
|
28
|
-
position(): Promise<Readonly<{
|
|
29
|
-
/** Horizontal coordinate in CSS pixels. */
|
|
30
|
-
x: number;
|
|
31
|
-
/** Vertical coordinate in CSS pixels. */
|
|
32
|
-
y: number;
|
|
33
|
-
}> | null>;
|
|
34
|
-
private withSampling;
|
|
35
|
-
}
|
package/dist/pointer.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import Events from "./events.js";
|
|
2
|
-
import wire from "./wire.js";
|
|
3
|
-
/** Client pointer access bound to the current Process boundary. */
|
|
4
|
-
export default class ClientPointer extends Events {
|
|
5
|
-
listeners = 0;
|
|
6
|
-
sample = (event) => wire.samplePointer(event.movementX, event.movementY);
|
|
7
|
-
constructor() {
|
|
8
|
-
super((event, listener, impossible) => this.withSampling(event === "move", wire.on("host-pointer", event, value => {
|
|
9
|
-
const position = createPosition(value);
|
|
10
|
-
if (position)
|
|
11
|
-
listener(position);
|
|
12
|
-
}, null, impossible)), observer => this.withSampling(true, wire.onAll("host-pointer", (event, value) => {
|
|
13
|
-
const position = createPosition(value);
|
|
14
|
-
if (event === "move" && position)
|
|
15
|
-
observer(event, position);
|
|
16
|
-
})));
|
|
17
|
-
}
|
|
18
|
-
async position() {
|
|
19
|
-
const answer = await wire.request(["pointer"]);
|
|
20
|
-
if (answer[0] === null)
|
|
21
|
-
return null;
|
|
22
|
-
const position = createPosition(answer[0]);
|
|
23
|
-
if (!position)
|
|
24
|
-
throw new Error("The desktop returned an invalid Pointer position");
|
|
25
|
-
return position;
|
|
26
|
-
}
|
|
27
|
-
withSampling(sample, stop) {
|
|
28
|
-
if (!sample)
|
|
29
|
-
return stop;
|
|
30
|
-
if (this.listeners++ === 0)
|
|
31
|
-
window.addEventListener("pointermove", this.sample);
|
|
32
|
-
let active = true;
|
|
33
|
-
return () => {
|
|
34
|
-
if (!active)
|
|
35
|
-
return;
|
|
36
|
-
active = false;
|
|
37
|
-
stop();
|
|
38
|
-
this.listeners--;
|
|
39
|
-
if (this.listeners === 0)
|
|
40
|
-
window.removeEventListener("pointermove", this.sample);
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function createPosition(value) {
|
|
45
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
46
|
-
return null;
|
|
47
|
-
const position = value;
|
|
48
|
-
if (!finite(position.x) || !finite(position.y))
|
|
49
|
-
return null;
|
|
50
|
-
return Object.freeze({ x: position.x, y: position.y });
|
|
51
|
-
}
|
|
52
|
-
function finite(value) {
|
|
53
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
54
|
-
}
|