@phreshos/node 0.1.13 → 0.1.14

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,9 +1,22 @@
1
1
  # `@phreshos/node`
2
2
 
3
- Node.js access to a running PhreshOS System and local Program projects.
3
+ Node.js access to a PhreshOS System and local Program projects.
4
4
 
5
- The Node SDK exposes the same System model used inside Server Endpoints and adds
6
- only connection lifecycle and project tooling.
5
+ [Documentation](https://docs.phreshos.com/sdks/node) ·
6
+ [System](https://docs.phreshos.com/system) ·
7
+ [First Program](https://docs.phreshos.com/first-program) ·
8
+ [Source](https://github.com/PhreshOS/node)
9
+
10
+ ## Role
11
+
12
+ The Node SDK exposes the same complete System contract available inside Server
13
+ Endpoints. It adds only the lifecycle of an externally owned connection and the
14
+ `Project` authoring API for building, running, installing, and packaging local
15
+ Program projects.
16
+
17
+ The connection transport is not public API. Core owns the domain model, and the
18
+ System remains authoritative whether an operation can execute locally or must
19
+ cross the connection boundary.
7
20
 
8
21
  ## Installation
9
22
 
@@ -14,49 +27,21 @@ only connection lifecycle and project tooling.
14
27
  | Bun | `bun add @phreshos/node` |
15
28
  | Yarn | `yarn add @phreshos/node` |
16
29
 
17
- ## System
18
-
19
- ```ts
20
- import { System } from "@phreshos/node"
21
-
22
- const system = await System.connect()
23
- const programs = await system.program.list()
24
-
25
- await system.disconnect()
26
- ```
27
-
28
- The transport is not public API. Code receives the same System, Program,
29
- Process, Endpoint, Service, Window, storage, and upload handles used by the
30
- Server SDK. `disconnect()` exists because external Node code owns the
31
- connection.
32
-
33
- ## Project
34
-
35
30
  ```ts
36
31
  import { Project, System } from "@phreshos/node"
37
32
 
38
- const project = await Project.open()
39
33
  const system = await System.connect()
34
+ const project = await Project.open()
40
35
 
41
- for await (const event of await project.start(system, { signal })) {
36
+ for await (const event of await project.start(system)) {
42
37
  console.log(event)
43
38
  }
44
39
 
45
40
  await system.disconnect()
46
41
  ```
47
42
 
48
- `Project` owns authoring concerns: builds, development and production
49
- definitions, installation, execution shortcuts, and packaging. The System
50
- receives only the resulting Program definition.
51
-
52
- `Project.dev()` starts and supervises a declared Client development command. It
53
- assigns an available port when no URL is declared and provides the port and
54
- public Program asset base through `PHRESHOS_CLIENT_PORT` and
55
- `PHRESHOS_CLIENT_BASE`.
56
-
57
- `Project.open()` starts from the current working directory by default.
58
- `System.connect()` resolves an explicit System home, then `PHRESHOS_HOME`,
59
- then the current owner's default System home.
43
+ See [Node SDK](https://docs.phreshos.com/sdks/node) for System connection and
44
+ Project lifecycle details.
60
45
 
61
46
  ## Development
62
47
 
@@ -65,14 +50,19 @@ bun install --frozen-lockfile
65
50
  bun run verify
66
51
  ```
67
52
 
68
- See the [Node SDK documentation](https://github.com/PhreshOS/docs/blob/main/content/docs/sdks/node.mdx)
69
- for the public model.
53
+ `verify` checks the types, builds the package, and runs the connection and
54
+ Project tests.
70
55
 
71
- ## Repository boundary
56
+ ## Related repositories
72
57
 
73
- This repository owns the external Node connection and local-project API. Core
74
- owns the domain model, Server owns the Endpoint runtime adapter, and CLI owns
75
- terminal presentation and native service management.
58
+ - [`@phreshos/core`](https://github.com/PhreshOS/core) owns the shared System
59
+ and runtime contracts.
60
+ - [`@phreshos/server`](https://github.com/PhreshOS/server) exposes the same
61
+ System contract inside Server Endpoints.
62
+ - [`@phreshos/cli`](https://github.com/PhreshOS/cli) presents Project and System
63
+ operations as terminal commands.
64
+ - [PhreshOS System](https://github.com/PhreshOS/system) owns the connected
65
+ runtime.
76
66
 
77
67
  ## License
78
68
 
package/dist/events.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import type { Cleanup, EventOptions, Subscribable } from "@phreshos/core";
2
- type Wait = (event: string | null, signal: AbortSignal, timeout?: number) => Promise<unknown>;
3
2
  type Failure = (error: Error) => void;
4
3
  type Register<Message> = (subscriber: (message: Message) => unknown, impossible?: Failure) => Cleanup;
5
- /** Adapts authoritative one-event waits into the shared Subscribable contract. */
4
+ type Subscribe = (event: string | null, subscriber: (message: unknown) => unknown, impossible?: Failure) => Cleanup;
5
+ /** Adapts one live representation source into the shared Subscribable contract. */
6
6
  export default class Events<Definitions extends object, Fallback = never> {
7
7
  private readonly names;
8
- private readonly wait;
9
- constructor(names: readonly string[], wait: Wait);
8
+ private readonly register;
9
+ constructor(names: readonly string[], register: Subscribe);
10
10
  readonly subscribe: Subscribable<Definitions, Fallback>["subscribe"];
11
11
  readonly waitFor: Subscribable<Definitions, Fallback>["waitFor"];
12
12
  readonly events: Subscribable<Definitions, Fallback>["events"];
package/dist/events.js CHANGED
@@ -1,10 +1,10 @@
1
- /** Adapts authoritative one-event waits into the shared Subscribable contract. */
1
+ /** Adapts one live representation source into the shared Subscribable contract. */
2
2
  export default class Events {
3
3
  names;
4
- wait;
5
- constructor(names, wait) {
4
+ register;
5
+ constructor(names, register) {
6
6
  this.names = names;
7
- this.wait = wait;
7
+ this.register = register;
8
8
  }
9
9
  subscribe = ((eventOrSubscriber, subscriber) => {
10
10
  if (typeof eventOrSubscriber === "string")
@@ -19,9 +19,19 @@ export default class Events {
19
19
  eventOrSubscriber({ event: capture.event, message: capture.payload });
20
20
  });
21
21
  });
22
- waitFor = ((event, timeout) => {
23
- return this.wait(event, new AbortController().signal, timeout);
24
- });
22
+ waitFor = ((event, timeout = 10_000) => new Promise((resolve, reject) => {
23
+ let stop = () => undefined;
24
+ const timer = setTimeout(() => {
25
+ stop();
26
+ reject(new Error(`The "${event}" event did not occur before the timeout`));
27
+ }, timeout);
28
+ const finish = (work) => {
29
+ clearTimeout(timer);
30
+ stop();
31
+ work();
32
+ };
33
+ stop = this.listen(event, message => finish(() => resolve(message)), error => finish(() => reject(error)));
34
+ }));
25
35
  events = ((eventOrOptions = {}, namedOptions = {}) => {
26
36
  if (typeof eventOrOptions === "string") {
27
37
  return stream((subscriber, impossible) => this.listen(eventOrOptions, subscriber, impossible), namedOptions);
@@ -39,22 +49,7 @@ export default class Events {
39
49
  }, eventOrOptions);
40
50
  });
41
51
  listen(event, subscriber, impossible) {
42
- const controller = new AbortController();
43
- void (async () => {
44
- while (!controller.signal.aborted) {
45
- try {
46
- subscriber(await this.wait(event, controller.signal, 86_400_000));
47
- }
48
- catch (error) {
49
- if (!controller.signal.aborted && timeout(error))
50
- continue;
51
- if (!controller.signal.aborted)
52
- impossible?.(failure(error));
53
- controller.abort();
54
- }
55
- }
56
- })();
57
- return () => controller.abort();
52
+ return this.register(event, subscriber, impossible);
58
53
  }
59
54
  }
60
55
  export function stream(register, options = {}) {
@@ -109,9 +104,3 @@ export function stream(register, options = {}) {
109
104
  }
110
105
  })();
111
106
  }
112
- function timeout(error) {
113
- return error instanceof Error && /timeout|timed out/i.test(error.message);
114
- }
115
- function failure(error) {
116
- return error instanceof Error ? error : new Error(String(error));
117
- }
package/dist/main.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { gatewayAddress } from "./address.js";
2
2
  export { ClientEndpoint, ClientService, Endpoint, Process, Program, ServerEndpoint, ServerService, System, type ProgramProcessRunEvent, type ProgramProcessRunOptions } from "./system.js";
3
- export { Service, type ClientLaunch, type Launch, type Permission, type PermissionChange, type PermissionInput, type Permissions, type ProgramPermissions, type ProgramDefinition, type ServerLaunch, type ServiceKey, type ShellEvent, type ShellOptions, type ProgramStartup, type Storage, } from "@phreshos/core";
3
+ export { Service, clientPermissionCatalog, isPermissionName, type ClientLaunch, type Launch, type Permission, type PermissionChange, type PermissionDefinition, type PermissionDefinitions, type PermissionInput, type PermissionName, type PermissionValue, type PermissionValueDomain, type Permissions, type ProgramPermissions, type ProgramDefinition, type ServerLaunch, type ServiceKey, type ShellEvent, type ShellOptions, type ProgramStartup, type Storage, } from "@phreshos/core";
4
4
  export { resolveHome } from "./home.js";
5
5
  export { Project, type Manifest, type PackedProject, type ProjectMode, type ProjectOptions, type ProjectRunOptions } from "./project.js";
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { gatewayAddress } from "./address.js";
2
2
  export { ClientEndpoint, ClientService, Endpoint, Process, Program, ServerEndpoint, ServerService, System } from "./system.js";
3
- export { Service, } from "@phreshos/core";
3
+ export { Service, clientPermissionCatalog, isPermissionName, } from "@phreshos/core";
4
4
  export { resolveHome } from "./home.js";
5
5
  export { Project } from "./project.js";
@@ -1,13 +1,13 @@
1
1
  import { type ProgramPermissions, type ProgramSql, type ProgramStore } from "@phreshos/core";
2
- type Request = (value: object) => Promise<unknown>;
2
+ type Call = <Result = unknown>(event: string, ...values: unknown[]) => Promise<Result>;
3
3
  type ProgramAddress = Readonly<{
4
4
  identity: string;
5
5
  reference: string;
6
6
  }>;
7
7
  /** Program-owned key-value storage carried through the owner-local Gateway. */
8
- export declare function programStore(request: Request, handle: ProgramAddress): ProgramStore;
8
+ export declare function programStore(call: Call, handle: ProgramAddress): ProgramStore;
9
9
  /** Program-owned SQL capability carried through the owner-local Gateway. */
10
- export declare function programSql(request: Request, handle: ProgramAddress, database: "database" | "logs"): ProgramSql;
10
+ export declare function programSql(call: Call, handle: ProgramAddress, database: "database" | "logs"): ProgramSql;
11
11
  /** Program permission management carried through the owner-local Gateway. */
12
- export declare function programPermissions(request: Request, handle: ProgramAddress): ProgramPermissions;
12
+ export declare function programPermissions(call: Call, handle: ProgramAddress): ProgramPermissions;
13
13
  export {};
@@ -1,15 +1,7 @@
1
1
  import { parsePermission, parsePermissionChange, parsePermissions } from "@phreshos/core";
2
2
  /** Program-owned key-value storage carried through the owner-local Gateway. */
3
- export function programStore(request, handle) {
4
- const operate = (storeOperation, key, value, ttl) => request({
5
- capability: "program",
6
- operation: "store",
7
- handle,
8
- storeOperation,
9
- key,
10
- value,
11
- ttl
12
- });
3
+ export function programStore(call, handle) {
4
+ const operate = (storeOperation, key, value, ttl) => (call("/program/store", handle, storeOperation, key, value, ttl));
13
5
  return {
14
6
  get: (key) => operate("get", key),
15
7
  set: (key, value, ttl) => operate("set", key, value, ttl),
@@ -19,29 +11,22 @@ export function programStore(request, handle) {
19
11
  };
20
12
  }
21
13
  /** Program-owned SQL capability carried through the owner-local Gateway. */
22
- export function programSql(request, handle, database) {
14
+ export function programSql(call, handle, database) {
23
15
  return {
24
16
  query(statement, ...rest) {
25
17
  const [text, values] = written(statement, rest);
26
- return request({ capability: "program", operation: "query", handle, database, statement: text, values });
18
+ return call(`/program/${database}`, handle, text, values);
27
19
  }
28
20
  };
29
21
  }
30
22
  /** Program permission management carried through the owner-local Gateway. */
31
- export function programPermissions(request, handle) {
32
- const operate = (permissionOperation, name, permission) => request({
33
- capability: "program",
34
- operation: "permissions",
35
- handle,
36
- permissionOperation,
37
- name,
38
- permission
39
- });
23
+ export function programPermissions(call, handle) {
24
+ const operate = (permissionOperation, name, permission) => (call("/program/permissions", handle, permissionOperation, name, permission));
40
25
  return {
41
- async get(name) { return parsePermission(await operate("get", name)); },
26
+ async get(name) { return parsePermission(name, await operate("get", name)); },
42
27
  async all() { return parsePermissions(await operate("all")); },
43
- async set(name, permission) { return parsePermissionChange(await operate("set", name, permission)); },
44
- async delete(name) { return parsePermissionChange(await operate("delete", name)); }
28
+ async set(name, permission) { return parsePermissionChange(name, await operate("set", name, permission)); },
29
+ async delete(name) { return parsePermissionChange(name, await operate("delete", name)); }
45
30
  };
46
31
  }
47
32
  function written(statement, rest) {
@@ -0,0 +1,100 @@
1
+ import type { Appearance, ClientDeclaration, EndpointDeclaration, ServiceKey, WindowGeometry, WindowLayer } from "@phreshos/core";
2
+ import type { GatewayConnection } from "./transport.js";
3
+ export interface ProgramState {
4
+ reference: string;
5
+ identity: string;
6
+ assetId: string;
7
+ installed: boolean;
8
+ name: string;
9
+ version: string | null;
10
+ description: string | null;
11
+ hasAgent: boolean;
12
+ server: EndpointDeclaration | null;
13
+ client: ClientDeclaration | null;
14
+ }
15
+ export interface WindowState {
16
+ title: string;
17
+ position: WindowGeometry["position"];
18
+ size: WindowGeometry["size"];
19
+ depth: number;
20
+ minimized: boolean;
21
+ layer: WindowLayer;
22
+ location: string;
23
+ }
24
+ export interface ProcessState {
25
+ reference: string;
26
+ identity: string;
27
+ name: string | null;
28
+ program: string;
29
+ parent: ProcessAddress | null;
30
+ options: Record<string, string>;
31
+ startedAt: Date;
32
+ server: {
33
+ ready: boolean;
34
+ service: boolean;
35
+ } | null;
36
+ client: {
37
+ service: boolean;
38
+ sameOrigin: boolean;
39
+ window: WindowState;
40
+ } | null;
41
+ }
42
+ export interface ProcessAddress {
43
+ reference: string;
44
+ identity: string;
45
+ }
46
+ export type Observation = Readonly<{
47
+ scope: "endpoint";
48
+ process: string;
49
+ endpoint: "server" | "client";
50
+ event: string | null;
51
+ }> | Readonly<{
52
+ scope: "traffic";
53
+ process: string;
54
+ endpoint: "server" | "client";
55
+ kind: "publish" | "ask" | "answer";
56
+ event: string | null;
57
+ }> | Readonly<{
58
+ scope: "service";
59
+ key: ServiceKey;
60
+ kind: "events" | "lifecycle";
61
+ event: string | null;
62
+ }>;
63
+ type Listener = (...values: unknown[]) => unknown;
64
+ interface CommandEvent {
65
+ event?: string;
66
+ [key: string]: unknown;
67
+ }
68
+ /** A connection-owned, live representation of the authoritative System model. */
69
+ export default class SystemRepresentation {
70
+ private readonly connection;
71
+ readonly authorization: string;
72
+ readonly programs: Map<string, ProgramState>;
73
+ readonly processes: Map<string, ProcessState>;
74
+ appearance: Appearance;
75
+ private readonly listeners;
76
+ private readonly release;
77
+ constructor(connection: GatewayConnection);
78
+ activate(): void;
79
+ close(): void;
80
+ call<Result = unknown>(event: string, ...values: unknown[]): Promise<Result>;
81
+ on(event: string, listener: Listener): () => void;
82
+ follow(observation: Observation, listener: (event: string, value: unknown) => unknown, impossible?: (error: Error) => void): () => void;
83
+ /** Stream one Program command through the live LinkManager connection. */
84
+ command(operation: "install" | "uninstall" | "run", subject: ProgramAddress, value: unknown, signal?: AbortSignal): AsyncGenerator<CommandEvent, void, unknown>;
85
+ private followModel;
86
+ private arriveProgram;
87
+ private uninstallProgram;
88
+ private forgetProgram;
89
+ private createProcess;
90
+ private serverReady;
91
+ private changeEndpoint;
92
+ private exitProcess;
93
+ private changeWindow;
94
+ private emit;
95
+ }
96
+ export type ProgramAddress = Readonly<{
97
+ identity: string;
98
+ reference: string;
99
+ }>;
100
+ export {};