@phreshos/client 0.1.52 → 0.1.54

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.
@@ -1,9 +1,15 @@
1
- import type { DesktopViewportEvents, DesktopViewportSource } from "@phreshos/core";
1
+ import type { DesktopOffset, DesktopViewportEvents, WritableDesktopViewportSource } from "@phreshos/core";
2
2
  import Events from "../events.js";
3
- /** Read-only access to the Desktop viewport containing this Client Endpoint. */
4
- export default class ClientViewport extends Events<DesktopViewportEvents, never> implements DesktopViewportSource {
3
+ /** The Desktop viewport containing this Client Endpoint: its size, where it looks on the plane, and moving that view. */
4
+ export default class ClientViewport extends Events<DesktopViewportEvents, never> implements WritableDesktopViewportSource {
5
5
  constructor();
6
- snapshot(): Promise<Readonly<{
7
- size: import("@phreshos/core").DesktopSize;
6
+ size(): Promise<Readonly<{
7
+ width: number;
8
+ height: number;
8
9
  }>>;
10
+ offset(): Promise<Readonly<{
11
+ x: number;
12
+ y: number;
13
+ }>>;
14
+ move(offset: DesktopOffset): Promise<void>;
9
15
  }
@@ -1,33 +1,57 @@
1
1
  import Events from "../events.js";
2
2
  import wire from "../wire.js";
3
- /** Read-only access to the Desktop viewport containing this Client Endpoint. */
3
+ /** The Desktop viewport containing this Client Endpoint: its size, where it looks on the plane, and moving that view. */
4
4
  export default class ClientViewport extends Events {
5
5
  constructor() {
6
6
  super((event, listener, impossible) => wire.on("host-desktop-viewport", event, value => {
7
- const snapshot = createSnapshot(value);
8
- if (snapshot)
9
- listener(snapshot);
7
+ const parsed = parse(event, value);
8
+ if (parsed)
9
+ listener(parsed);
10
10
  }, null, impossible), observer => wire.onAll("host-desktop-viewport", (event, value) => {
11
- const snapshot = createSnapshot(value);
12
- if (typeof event === "string" && snapshot)
13
- observer(event, snapshot);
11
+ const parsed = typeof event === "string" ? parse(event, value) : null;
12
+ if (typeof event === "string" && parsed)
13
+ observer(event, parsed);
14
14
  }));
15
15
  }
16
- async snapshot() {
17
- const answer = await wire.request(["desktopViewport"]);
18
- const snapshot = createSnapshot(answer[0]);
19
- if (!snapshot)
20
- throw new Error("The System returned an invalid Desktop viewport");
21
- return snapshot;
16
+ async size() {
17
+ const size = createSize((await state()).size);
18
+ if (!size)
19
+ throw new Error("The System returned an invalid Desktop size");
20
+ return size;
22
21
  }
22
+ async offset() {
23
+ const offset = createOffset((await state()).offset);
24
+ if (!offset)
25
+ throw new Error("The System returned an invalid Desktop offset");
26
+ return offset;
27
+ }
28
+ async move(offset) {
29
+ const target = createOffset(offset);
30
+ if (!target)
31
+ throw new Error("A Desktop offset needs finite x and y");
32
+ await wire.request(["moveDesktopViewport", target]);
33
+ }
34
+ }
35
+ async function state() {
36
+ const answer = await wire.request(["desktopViewport"]);
37
+ return record(answer[0]) ? answer[0] : {};
23
38
  }
24
- function createSnapshot(value) {
25
- if (!record(value) || !record(value.size))
39
+ function parse(event, value) {
40
+ if (event === "resize")
41
+ return createSize(value);
42
+ if (event === "move")
43
+ return createOffset(value);
44
+ return null;
45
+ }
46
+ function createSize(value) {
47
+ if (!record(value) || !finite(value.width) || !finite(value.height))
26
48
  return null;
27
- const { width, height } = value.size;
28
- if (!finite(width) || !finite(height))
49
+ return Object.freeze({ width: value.width, height: value.height });
50
+ }
51
+ function createOffset(value) {
52
+ if (!record(value) || !finite(value.x) || !finite(value.y))
29
53
  return null;
30
- return Object.freeze({ size: Object.freeze({ width, height }) });
54
+ return Object.freeze({ x: value.x, y: value.y });
31
55
  }
32
56
  function record(value) {
33
57
  return typeof value === "object" && value !== null && !Array.isArray(value);
package/dist/storage.js CHANGED
@@ -200,16 +200,67 @@ function parseEntry(value) {
200
200
  return { kind: entry.kind, path: entry.path };
201
201
  }
202
202
  export function store(program) {
203
+ const snapshots = new Map();
204
+ const local = new Set();
205
+ const notify = (key, snapshot) => { for (const listener of local)
206
+ listener(key, snapshot); };
203
207
  async function ask(operation, ...values) {
204
208
  const answer = await wire.request(["store", program, operation, ...values]);
205
209
  return answer[0];
206
210
  }
207
211
  return {
208
212
  get: (key) => ask("get", key),
209
- set: (key, value, ttl) => ask("set", key, value, ttl),
210
- delete: (key) => ask("delete", key),
213
+ async set(key, value, ttl) {
214
+ const result = await ask("set", key, value, ttl);
215
+ snapshots.delete(key);
216
+ return result;
217
+ },
218
+ async getOrSet(key, initial) {
219
+ // The response carries the authoritative revision so an older initial event cannot win hydration.
220
+ const snapshot = await ask("getOrSet", key, initial);
221
+ notify(key, snapshot);
222
+ return snapshot.value;
223
+ },
224
+ async update(key, updater) {
225
+ let snapshot = snapshots.get(key) ?? await ask("snapshot", key);
226
+ for (;;) {
227
+ const next = updater(snapshot.value);
228
+ const result = await ask("compareAndSet", key, next, snapshot);
229
+ snapshots.set(key, result.snapshot);
230
+ notify(key, result.snapshot);
231
+ if (result.changed)
232
+ return result.snapshot.value;
233
+ snapshot = result.snapshot;
234
+ }
235
+ },
236
+ async delete(key) {
237
+ const result = await ask("delete", key);
238
+ for (const name of Array.isArray(key) ? key : [key])
239
+ snapshots.delete(name);
240
+ return result;
241
+ },
211
242
  has: (key) => ask("has", key),
212
- clear: () => ask("clear")
243
+ async clear() { await ask("clear"); snapshots.clear(); },
244
+ subscribe(key, subscriber) {
245
+ let active = true;
246
+ let latest;
247
+ const deliver = (snapshot) => {
248
+ if (!active || (latest?.run === snapshot.run && latest.revision >= snapshot.revision))
249
+ return;
250
+ latest = snapshot;
251
+ snapshots.set(key, snapshot);
252
+ subscriber(snapshot.value);
253
+ };
254
+ const stop = wire.on("program-host", "storeChange", (reference, changedKey, snapshot) => {
255
+ if (reference === program.reference && changedKey === key)
256
+ deliver(snapshot);
257
+ }, program.reference);
258
+ const localListener = (changedKey, snapshot) => { if (changedKey === key)
259
+ deliver(snapshot); };
260
+ local.add(localListener);
261
+ void ask("snapshot", key).then(deliver).catch(() => undefined);
262
+ return () => { active = false; local.delete(localListener); stop(); };
263
+ }
213
264
  };
214
265
  }
215
266
  export function sql(kind, program) {
package/dist/system.js CHANGED
@@ -1,4 +1,4 @@
1
- import { parseShellEvent, parseAuthenticationRequirements, parseAuthenticationState, parsePermissions, parseSessionEndSnapshot, execute as executeRequest } from "@phreshos/core";
1
+ import { parseShellEvent, parseAuthenticationRequirements, parseAuthenticationState, parsePermissions, parseSessionEndSnapshot, parseSystemProgramListOptions, parseSystemServiceListOptions, execute as executeRequest } from "@phreshos/core";
2
2
  import ClientAppearance from "./appearance.js";
3
3
  import wire from "./wire.js";
4
4
  import { prepareService } from "./service.js";
@@ -36,12 +36,8 @@ class SystemServiceHandle extends Events {
36
36
  observer(event, prepareService(values[1]));
37
37
  }));
38
38
  }
39
- async list() {
40
- const [addresses] = await wire.request(["host-service-list"]);
41
- return addresses.map(address => prepareService(address));
42
- }
43
- async search(name) {
44
- const [addresses] = await wire.request(["host-service-search", name]);
39
+ async list(options = {}) {
40
+ const [addresses] = await wire.request(["host-service-list", parseSystemServiceListOptions(options)]);
45
41
  return addresses.map(address => prepareService(address));
46
42
  }
47
43
  prepare(address) { return prepareService(address); }
@@ -53,8 +49,8 @@ class SystemProgramHandle extends Events {
53
49
  observer(event, systemProgramEvent(event, values));
54
50
  }));
55
51
  }
56
- async list(onlyInstalled = false) {
57
- const answer = await wire.request(["host-program-list", onlyInstalled]);
52
+ async list(options = {}) {
53
+ const answer = await wire.request(["host-program-list", parseSystemProgramListOptions(options)]);
58
54
  return answer[0].map(program);
59
55
  }
60
56
  async find(identity) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/client",
3
- "version": "0.1.52",
3
+ "version": "0.1.54",
4
4
  "description": "The SDK used by a Program's client endpoint.",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -46,13 +46,13 @@
46
46
  "test": "vitest run --project default"
47
47
  },
48
48
  "peerDependencies": {
49
- "@phreshos/core": "^0.1.62"
49
+ "@phreshos/core": "^0.1.64"
50
50
  },
51
51
  "dependencies": {
52
52
  "@the-link/messagepack": "^0.1.0"
53
53
  },
54
54
  "devDependencies": {
55
- "@phreshos/core": "^0.1.62",
55
+ "@phreshos/core": "^0.1.64",
56
56
  "typescript": "^6.0.3",
57
57
  "vitest": "^4.1.10"
58
58
  }