@phreshos/client 0.1.33 → 0.1.34

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 CHANGED
@@ -1,10 +1,21 @@
1
1
  # `@phreshos/client`
2
2
 
3
- The SDK for a PhreshOS Program's Client Endpoint.
3
+ The runtime adapter for a PhreshOS Program's Client Endpoint.
4
4
 
5
- The Client SDK adapts the Desktop boundary to the shared Core domain model. It
6
- exposes the same complete `system` contract as the Server and Node SDKs, the
7
- current Client `context`, and the Client's `desktop` environment.
5
+ [Documentation](https://docs.phreshos.com/sdks/client) ·
6
+ [Client Context](https://docs.phreshos.com/runtime/context) ·
7
+ [Desktop](https://docs.phreshos.com/system/desktop) ·
8
+ [Source](https://github.com/PhreshOS/client)
9
+
10
+ ## Role
11
+
12
+ The Client SDK exposes three separate Core contracts inside a Client Endpoint:
13
+ the global `system`, the current Client `context`, and the containing `desktop`.
14
+ It adapts the Desktop boundary without defining client-specific versions of the
15
+ shared Program, Process, Endpoint, or Service domains.
16
+
17
+ The System enforces Client authority. The SDK carries requests and preserves
18
+ canonical handles; it does not own authoritative state or visual components.
8
19
 
9
20
  ## Installation
10
21
 
@@ -17,55 +28,16 @@ current Client `context`, and the Client's `desktop` environment.
17
28
 
18
29
  `@phreshos/core` is a peer dependency.
19
30
 
20
- ## Context
21
-
22
31
  ```ts
23
- import { context } from "@phreshos/client"
24
-
25
- context.subscribe("changed", message => {
26
- console.log(message)
27
- })
28
-
29
- context.publish("changed", { value: 1 })
32
+ import { context, desktop, system } from "@phreshos/client"
30
33
 
31
34
  const program = await context.program()
32
- const process = await context.process()
33
- const server = context.server
34
- const position = await context.window.position()
35
- await context.localWindow.move({ x: 20, y: 20 })
36
- ```
37
-
38
- `context` belongs to the executing Client. It provides communication,
39
- navigation to its Program and Process, its paired Server, the authoritative
40
- current Window through `window`, and command-only control of the current
41
- Desktop representation through `localWindow`.
42
-
43
- ## System
44
-
45
- ```ts
46
- import { system } from "@phreshos/client"
47
-
48
- const appearance = await system.appearance.snapshot()
49
- const programs = await system.program.list()
50
- const processes = await system.process.list()
51
- ```
52
-
53
- `system` is the complete global System contract sourced from Core.
54
-
55
- ## Desktop
56
-
57
- ```ts
58
- import { desktop } from "@phreshos/client"
59
-
60
- const surface = await desktop.surface.snapshot()
61
35
  const preferences = await desktop.preferences.snapshot()
36
+ const appearance = await system.appearance.snapshot()
62
37
  ```
63
38
 
64
- Desktop capabilities remain separate from the global System and the current
65
- execution Context.
66
-
67
- Requests read current state. Subscriptions observe future publications and do
68
- not replay an initial value. Importing the SDK performs neither operation.
39
+ See [Client SDK](https://docs.phreshos.com/sdks/client) for the complete entry
40
+ points and authority boundary.
69
41
 
70
42
  ## Development
71
43
 
@@ -74,17 +46,21 @@ bun install --frozen-lockfile
74
46
  bun run verify
75
47
  ```
76
48
 
77
- `verify` checks the source, completion surface, build, and published package
78
- shape.
49
+ `verify` checks the types and completions, builds the package, and validates its
50
+ published shape.
79
51
 
80
- See the [Client and Server documentation](https://github.com/PhreshOS/docs/blob/main/content/docs/sdks/client-and-server.mdx)
81
- for the shared model and authority boundary.
52
+ ## Related repositories
82
53
 
83
- ## Repository boundary
54
+ - [`@phreshos/core`](https://github.com/PhreshOS/core) owns every shared
55
+ contract and domain class exposed here.
56
+ - [`@phreshos/server`](https://github.com/PhreshOS/server) adapts the paired
57
+ Server Endpoint boundary.
58
+ - [`@phreshos/react`](https://github.com/PhreshOS/react) adapts the Client
59
+ sources to React without depending on this package.
60
+ - [PhreshOS System](https://github.com/PhreshOS/system) owns enforcement,
61
+ routing, and the Desktop host.
84
62
 
85
- This repository owns the Client runtime adapter. Core owns the domain model, the
86
- System owns enforcement and forwarding, React owns framework adaptation, and
87
- React UI owns visual interpretation.
63
+ ## Contributing
88
64
 
89
65
  See [CONTRIBUTING.md](CONTRIBUTING.md) for the repository workflow and
90
66
  [SECURITY.md](SECURITY.md) for private vulnerability reporting.
package/dist/domain.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerSubscriber as CoreAnswerSubscriber, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskSubscriber as CoreAskSubscriber, type Cleanup, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointTraffic as CoreEndpointTraffic, type Exit, type EventOptions, type LocalWindow, type Outcome, type Position, type ProgramProcess as CoreProgramProcess, type ProgramProcessRunEvent as CoreProgramProcessRunEvent, type ProgramProcessRunOptions as CoreProgramProcessRunOptions, type ServerTraffic as CoreServerTraffic, type Size, type TrafficMessage as CoreTrafficMessage, type TrafficCapture as CoreTrafficCapture, type TrafficEvents as CoreTrafficEvents, type Window as CoreWindow, type WindowState } from "@phreshos/core";
1
+ import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint, type AnswerCapture as CoreAnswerCapture, type AnswerMessage as CoreAnswerMessage, type AnswerOutcome, type AnswerSubscriber as CoreAnswerSubscriber, type AskCapture as CoreAskCapture, type AskMessage as CoreAskMessage, type AskSubscriber as CoreAskSubscriber, type Cleanup, type ClientTraffic as CoreClientTraffic, type ClientDeclaration, type EndpointTraffic as CoreEndpointTraffic, type Exit, type EventOptions, type LocalWindow, type Position, type ProgramProcess as CoreProgramProcess, type ProgramProcessRunEvent as CoreProgramProcessRunEvent, type ProgramProcessRunOptions as CoreProgramProcessRunOptions, type ServerTraffic as CoreServerTraffic, type Size, type TrafficMessage as CoreTrafficMessage, type TrafficCapture as CoreTrafficCapture, type TrafficEvents as CoreTrafficEvents, type Window as CoreWindow, type WindowState } from "@phreshos/core";
2
2
  import Events from "./events.js";
3
3
  export interface HandleAddress {
4
4
  identity: string;
@@ -101,7 +101,7 @@ export declare class ServerTrafficHandle extends TrafficHandle {
101
101
  questionId: string;
102
102
  message: Readonly<{
103
103
  to: Endpoint<{}, unknown> | null;
104
- outcome: Outcome<unknown>;
104
+ outcome: AnswerOutcome<unknown>;
105
105
  }>;
106
106
  }>>;
107
107
  private followAnswers;
package/dist/domain.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint } from "@phreshos/core";
1
+ import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint, parseClientPermissions } from "@phreshos/core";
2
2
  import Events, { stream } from "./events.js";
3
3
  import Deadline from "./deadline.js";
4
4
  import HandleRegistry from "./handle-registry.js";
@@ -106,7 +106,7 @@ function clientDeclaration(record) {
106
106
  position: record.position,
107
107
  layer: record.layer,
108
108
  minimize: record.minimize,
109
- permissions: Object.freeze(Object.fromEntries(Object.entries(record.permissions).map(([name, values]) => [name, Object.freeze([...values])])))
109
+ permissions: parseClientPermissions(record.permissions)
110
110
  });
111
111
  }
112
112
  class ProgramProcessHandle {
package/dist/main.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { system } from "./system.js";
2
2
  export { desktop } from "./desktop/desktop.js";
3
3
  export { context, type Context, type ContextCapture, type ContextEvents, type ContextMessage, type ContextServer } from "./context.js";
4
- export { ClientService, ServerService, Service, type ServiceKey, } from "@phreshos/core";
4
+ export { ClientService, ServerService, Service, clientPermissionCatalog, isPermissionName, type ServiceKey, } from "@phreshos/core";
5
5
  export { ClientEndpoint, Endpoint, Process, Program, ServerEndpoint, type Window, type ProgramProcess, type ProgramProcessRunEvent, type ProgramProcessRunOptions, 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";
6
- export type { Askable, ClientContext, Capture, Captures, CaptureSubscriber, ClientLaunch, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointLifecycle, EndpointLifecycleEvents, EntryStat, EventMessage, EventName, EventOptions, EventSubscriber, Exit, FileStat, Launch, ServerLaunch, ShellEvent, ShellOptions, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Outcome, Permission, PermissionChange, PermissionInput, PermissionRequest, Permissions, ClientPermissionDeclarations, ClientPermissions, ContextPermissions, ProgramPermissions, TimedContextPermissions, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramStartup, ProgramSql, ProgramStore, ProcessEvents, Publishable, System, SystemUploads, Upload, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, Timeoutable, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, AnimationsPreference, DesktopPreferences, DesktopPreferencesEvents, DesktopPreferencesSource, DesktopPreferencesUpdate, Desktop, DesktopSize, DesktopSurfaceEvents, DesktopSurfaceSnapshot, DesktopSurfaceSource, WritableDesktopPreferencesSource, ThemedValue, Theme, ThemePreference, Value, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowOperations, Transaction } from "@phreshos/core";
6
+ export type { Askable, AnswerOutcome, ClientContext, Capture, Captures, CaptureSubscriber, ClientLaunch, ClientDeclaration, Cleanup, DirectoryStat, EndpointDeclaration, EndpointLifecycle, EndpointLifecycleEvents, EntryStat, EventMessage, EventName, EventOptions, EventSubscriber, Exit, FileStat, Launch, ServerLaunch, ShellEvent, ShellOptions, Layer, LogKind, LogRecord, LogSource, Message, OtherStat, Permission, PermissionChange, PermissionDefinition, PermissionDefinitions, PermissionInput, PermissionName, PermissionRequest, PermissionValue, PermissionValueDomain, Permissions, ClientPermissionDeclarations, ClientPermissions, ContextPermissions, ProgramPermissions, TimedContextPermissions, Position, Storage, ProgramEvents, ProgramProcessEvents, ProgramProcessExit, ProgramStartup, ProgramSql, ProgramStore, ProcessEvents, Publishable, System, SystemUploads, Upload, Size, Subscribable, SubscribableEvents, SubscribableFallback, TimedAskable, Timeoutable, Appearance, AppearanceEvents, AppearanceSource, AppearanceSurface, AnimationsPreference, DesktopPreferences, DesktopPreferencesEvents, DesktopPreferencesSource, DesktopPreferencesUpdate, Desktop, DesktopSize, DesktopSurfaceEvents, DesktopSurfaceSnapshot, DesktopSurfaceSource, WritableDesktopPreferencesSource, ThemedValue, Theme, ThemePreference, Value, WindowEvents, WindowGeometry, WindowLayer, WindowState, Easing, LocalWindow, LocalWindowOperations, Transaction } from "@phreshos/core";
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { system } from "./system.js";
2
2
  export { desktop } from "./desktop/desktop.js";
3
3
  export { context } from "./context.js";
4
- export { ClientService, ServerService, Service, } from "@phreshos/core";
4
+ export { ClientService, ServerService, Service, clientPermissionCatalog, isPermissionName, } from "@phreshos/core";
5
5
  export { ClientEndpoint, Endpoint, Process, Program, ServerEndpoint } from "./domain.js";
@@ -5,10 +5,10 @@ export const defaultPermissionTimeout = 120_000;
5
5
  export function programPermissions(program) {
6
6
  const operate = (operation, name, permission) => (wire.request(["program-permissions", program, operation, name, permission]));
7
7
  return {
8
- async get(name) { return parsePermission((await operate("get", name))[0]); },
8
+ async get(name) { return parsePermission(name, (await operate("get", name))[0]); },
9
9
  async all() { return parsePermissions((await operate("all"))[0]); },
10
- async set(name, permission) { return parsePermissionChange((await operate("set", name, permission))[0]); },
11
- async delete(name) { return parsePermissionChange((await operate("delete", name))[0]); }
10
+ async set(name, permission) { return parsePermissionChange(name, (await operate("set", name, permission))[0]); },
11
+ async delete(name) { return parsePermissionChange(name, (await operate("delete", name))[0]); }
12
12
  };
13
13
  }
14
14
  /** Stored grants and owner requests belonging to the current Client Endpoint. */
@@ -19,12 +19,12 @@ export function contextPermissions() {
19
19
  const result = await wire.requestOrNull(["context-permission-request", identity, name, permission], timeout);
20
20
  return result === null
21
21
  ? Object.freeze({ permission: null, needReload: false })
22
- : parsePermissionChange(result[0]);
22
+ : parsePermissionChange(name, result[0]);
23
23
  }
24
24
  });
25
25
  return {
26
26
  async get(name) {
27
- return parsePermission((await wire.request(["context-permission-get", name]))[0]);
27
+ return parsePermission(name, (await wire.request(["context-permission-get", name]))[0]);
28
28
  },
29
29
  request: timed(defaultPermissionTimeout).request,
30
30
  timeout(milliseconds) {
package/dist/wire.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import Deadline from "./deadline.js";
2
2
  import { defaultTimeout } from "./events.js";
3
3
  import captureClientOutput from "./log.js";
4
- import { deserialize, serialize } from "./messagepack.js";
4
+ import { deserialize, serialize } from "@the-link/messagepack";
5
5
  /** The client endpoint's sole postMessage adapter. */
6
6
  class Wire {
7
7
  parent = window.parent === window ? null : window.parent;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phreshos/client",
3
- "version": "0.1.33",
3
+ "version": "0.1.34",
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.37"
50
+ "@phreshos/core": "^0.1.38"
51
51
  },
52
52
  "dependencies": {
53
- "@msgpack/msgpack": "^3.1.3"
53
+ "@the-link/messagepack": "^0.1.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@phreshos/core": "^0.1.37",
56
+ "@phreshos/core": "^0.1.38",
57
57
  "typescript": "^6.0.3"
58
58
  }
59
59
  }
@@ -1,3 +0,0 @@
1
- export type Bytes = Uint8Array<ArrayBuffer>;
2
- export declare const serialize: (value: unknown, attachments?: readonly object[]) => Bytes;
3
- export declare const deserialize: (bytes: Uint8Array, attachments?: readonly unknown[]) => unknown;
@@ -1,130 +0,0 @@
1
- import { decode, encode, ExtensionCodec } from "@msgpack/msgpack";
2
- class Attachment {
3
- index;
4
- constructor(index) {
5
- this.index = index;
6
- }
7
- }
8
- class BigInteger {
9
- value;
10
- constructor(value) {
11
- this.value = value;
12
- }
13
- }
14
- class Undefined {
15
- }
16
- const textEncoder = new TextEncoder();
17
- const textDecoder = new TextDecoder("utf-8", { fatal: true });
18
- const extensions = new ExtensionCodec();
19
- const serializePrepared = (value, context) => encode(value, { context, extensionCodec: extensions });
20
- const deserializePrepared = (bytes, context) => decode(bytes, { context, extensionCodec: extensions });
21
- extensions.register({
22
- type: 0,
23
- encode: value => value instanceof Attachment ? uint32(value.index) : null,
24
- decode: (bytes, _type, context) => {
25
- const attachment = context.incoming?.[readUint32(bytes)];
26
- if (attachment === undefined)
27
- throw new Error("The MessagePack attachment is absent");
28
- return attachment;
29
- }
30
- });
31
- extensions.register({
32
- type: 1,
33
- encode: (value, context) => value instanceof Map ? serializePrepared([...value], context) : null,
34
- decode: (bytes, _type, context) => new Map(deserializePrepared(bytes, context))
35
- });
36
- extensions.register({
37
- type: 2,
38
- encode: (value, context) => value instanceof Set ? serializePrepared([...value], context) : null,
39
- decode: (bytes, _type, context) => new Set(deserializePrepared(bytes, context))
40
- });
41
- extensions.register({
42
- type: 3,
43
- encode: (value, context) => value instanceof RegExp ? serializePrepared([value.source, value.flags, value.lastIndex], context) : null,
44
- decode: (bytes, _type, context) => {
45
- const [source, flags, lastIndex] = deserializePrepared(bytes, context);
46
- const expression = new RegExp(source, flags);
47
- expression.lastIndex = lastIndex;
48
- return expression;
49
- }
50
- });
51
- extensions.register({
52
- type: 4,
53
- encode: value => value instanceof URL ? textEncoder.encode(value.href) : null,
54
- decode: bytes => new URL(textDecoder.decode(bytes))
55
- });
56
- extensions.register({
57
- type: 5,
58
- encode: (value, context) => value instanceof Error ? serializePrepared(prepare({
59
- cause: value.cause,
60
- message: value.message,
61
- name: value.name,
62
- stack: value.stack
63
- }, context), context) : null,
64
- decode: (bytes, _type, context) => {
65
- const value = deserializePrepared(bytes, context);
66
- const error = new Error(value.message, { cause: value.cause });
67
- error.name = value.name;
68
- if (value.stack !== undefined)
69
- error.stack = value.stack;
70
- return error;
71
- }
72
- });
73
- extensions.register({
74
- type: 6,
75
- encode: value => value instanceof BigInteger ? textEncoder.encode(value.value.toString()) : null,
76
- decode: bytes => BigInt(textDecoder.decode(bytes))
77
- });
78
- extensions.register({
79
- type: 7,
80
- encode: value => value instanceof Undefined ? new Uint8Array() : null,
81
- decode: () => undefined
82
- });
83
- export const serialize = (value, attachments = []) => {
84
- const outgoing = new Map(attachments.map((attachment, index) => [attachment, index]));
85
- const context = { outgoing };
86
- return serializePrepared(prepare(value, context), context);
87
- };
88
- export const deserialize = (bytes, attachments = []) => {
89
- return deserializePrepared(bytes, { incoming: attachments });
90
- };
91
- const prepare = (value, context) => {
92
- if (value === undefined)
93
- return new Undefined();
94
- if (typeof value === "bigint")
95
- return new BigInteger(value);
96
- if (typeof value === "function" || typeof value === "symbol")
97
- return null;
98
- if (value === null || typeof value !== "object")
99
- return value;
100
- const attachment = context.outgoing?.get(value);
101
- if (attachment !== undefined)
102
- return new Attachment(attachment);
103
- if (value instanceof Date || value instanceof RegExp || value instanceof URL || value instanceof Error)
104
- return value;
105
- if (value instanceof ArrayBuffer)
106
- return new Uint8Array(value);
107
- if (ArrayBuffer.isView(value))
108
- return Uint8Array.from(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
109
- if (value instanceof Map)
110
- return new Map([...value].map(([key, entry]) => [prepare(key, context), prepare(entry, context)]));
111
- if (value instanceof Set)
112
- return new Set([...value].map(entry => prepare(entry, context)));
113
- if (Array.isArray(value))
114
- return value.map(entry => prepare(entry, context));
115
- if ("toJSON" in value && typeof value.toJSON === "function")
116
- return prepare(value.toJSON(), context);
117
- return Object.fromEntries(Object.entries(value)
118
- .filter(([, entry]) => typeof entry !== "function")
119
- .map(([key, entry]) => [key, prepare(entry, context)]));
120
- };
121
- const uint32 = (value) => {
122
- const bytes = new Uint8Array(4);
123
- new DataView(bytes.buffer).setUint32(0, value);
124
- return bytes;
125
- };
126
- const readUint32 = (bytes) => {
127
- if (bytes.byteLength !== 4)
128
- throw new Error("The MessagePack attachment reference is invalid");
129
- return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0);
130
- };