@phreshos/node 0.1.12 → 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,44 +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.open()` starts from the current working directory by default.
53
- `System.connect()` resolves an explicit System home, then `PHRESHOS_HOME`,
54
- 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.
55
45
 
56
46
  ## Development
57
47
 
@@ -60,14 +50,19 @@ bun install --frozen-lockfile
60
50
  bun run verify
61
51
  ```
62
52
 
63
- See the [Node SDK documentation](https://github.com/PhreshOS/docs/blob/main/content/docs/sdks/node.mdx)
64
- for the public model.
53
+ `verify` checks the types, builds the package, and runs the connection and
54
+ Project tests.
65
55
 
66
- ## Repository boundary
56
+ ## Related repositories
67
57
 
68
- This repository owns the external Node connection and local-project API. Core
69
- owns the domain model, Server owns the Endpoint runtime adapter, and CLI owns
70
- 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.
71
66
 
72
67
  ## License
73
68
 
@@ -0,0 +1,20 @@
1
+ import type { ClientDevelopment, ProgramProcessRunEvent } from "@phreshos/core";
2
+ /** One prepared Client development source owned by a Project development run. */
3
+ export default class DevelopmentClient {
4
+ readonly url: string;
5
+ private readonly startCommand;
6
+ private readonly directory;
7
+ private command;
8
+ private controller;
9
+ private releaseSignal;
10
+ private constructor();
11
+ /** Select the development address without starting the authored command. */
12
+ static prepare(development: ClientDevelopment, directory: string): Promise<DevelopmentClient>;
13
+ /** Start and verify the Client beneath its System-created asset address. */
14
+ start(assetId: string, signal?: AbortSignal): Promise<void>;
15
+ processSignal(fallback?: AbortSignal): AbortSignal | undefined;
16
+ supervise(lifecycle: AsyncGenerator<ProgramProcessRunEvent, void, void>): AsyncGenerator<ProgramProcessRunEvent, void, void>;
17
+ dispose(reason: unknown): Promise<void>;
18
+ private waitUntilReady;
19
+ private supervisedLifecycle;
20
+ }
@@ -0,0 +1,259 @@
1
+ import { spawn } from "node:child_process";
2
+ import { connect, createServer } from "node:net";
3
+ import { delimiter, join } from "node:path";
4
+ const readinessTimeout = 15_000;
5
+ const pollingInterval = 200;
6
+ /** One prepared Client development source owned by a Project development run. */
7
+ export default class DevelopmentClient {
8
+ url;
9
+ startCommand;
10
+ directory;
11
+ command = null;
12
+ controller = null;
13
+ releaseSignal = () => undefined;
14
+ constructor(url, startCommand, directory) {
15
+ this.url = url;
16
+ this.startCommand = startCommand;
17
+ this.directory = directory;
18
+ }
19
+ /** Select the development address without starting the authored command. */
20
+ static async prepare(development, directory) {
21
+ const url = development.url ?? `http://localhost:${await availablePort()}/`;
22
+ if (development.startCommand)
23
+ await assertAvailable(url);
24
+ return new DevelopmentClient(url, development.startCommand ?? null, directory);
25
+ }
26
+ /** Start and verify the Client beneath its System-created asset address. */
27
+ async start(assetId, signal) {
28
+ if (this.controller)
29
+ throw new Error("The Client development source has already started");
30
+ const base = `/program/${assetId}/assets/`;
31
+ const controller = new AbortController();
32
+ this.controller = controller;
33
+ this.releaseSignal = forwardAbort(signal, controller);
34
+ try {
35
+ if (this.startCommand) {
36
+ this.command = new OwnedCommand(this.startCommand, this.directory, {
37
+ PHRESHOS_CLIENT_BASE: base,
38
+ PHRESHOS_CLIENT_PORT: String(portOf(this.url))
39
+ });
40
+ }
41
+ await this.waitUntilReady(new URL(base, this.url).href);
42
+ }
43
+ catch (error) {
44
+ await this.dispose(error);
45
+ throw error;
46
+ }
47
+ }
48
+ processSignal(fallback) {
49
+ return this.command ? this.controller?.signal : fallback;
50
+ }
51
+ supervise(lifecycle) {
52
+ if (!this.command) {
53
+ this.releaseSignal();
54
+ return lifecycle;
55
+ }
56
+ return this.supervisedLifecycle(lifecycle);
57
+ }
58
+ async dispose(reason) {
59
+ this.releaseSignal();
60
+ this.controller?.abort(reason);
61
+ await this.command?.stop();
62
+ }
63
+ async waitUntilReady(url) {
64
+ const deadline = Date.now() + readinessTimeout;
65
+ while (Date.now() < deadline) {
66
+ this.controller.signal.throwIfAborted();
67
+ const exit = this.command?.exitResult();
68
+ if (exit)
69
+ throw commandFailure(exit);
70
+ try {
71
+ const response = await fetch(url, {
72
+ signal: AbortSignal.timeout(Math.max(1, Math.min(500, deadline - Date.now())))
73
+ });
74
+ await response.body?.cancel();
75
+ if (response.ok)
76
+ return;
77
+ }
78
+ catch { /* The development server is still starting. */ }
79
+ await pause(Math.min(pollingInterval, Math.max(0, deadline - Date.now())), this.controller.signal);
80
+ }
81
+ throw new Error(`Client development URL did not respond within 15 seconds: ${url}`);
82
+ }
83
+ async *supervisedLifecycle(lifecycle) {
84
+ const iterator = lifecycle[Symbol.asyncIterator]();
85
+ const command = this.command;
86
+ try {
87
+ while (true) {
88
+ const next = iterator.next();
89
+ const outcome = await Promise.race([
90
+ next.then(result => ({ source: "system", result }), error => ({ source: "system-error", error })),
91
+ command.exited().then(result => ({ source: "client", result }))
92
+ ]);
93
+ if (outcome.source === "client") {
94
+ const error = commandFailure(outcome.result);
95
+ this.controller?.abort(error);
96
+ await next.catch(() => undefined);
97
+ throw error;
98
+ }
99
+ if (outcome.source === "system-error")
100
+ throw outcome.error;
101
+ if (outcome.result.done)
102
+ return;
103
+ yield outcome.result.value;
104
+ }
105
+ }
106
+ finally {
107
+ await this.dispose(new Error("The development lifecycle ended"));
108
+ await iterator.return?.();
109
+ }
110
+ }
111
+ }
112
+ /** One operating-system command supervised as a complete process tree. */
113
+ class OwnedCommand {
114
+ child;
115
+ completion;
116
+ result = null;
117
+ stopped = false;
118
+ constructor(command, directory, environment) {
119
+ this.child = spawn(command, {
120
+ cwd: directory,
121
+ env: commandEnvironment(directory, environment),
122
+ shell: true,
123
+ stdio: ["ignore", "pipe", "pipe"],
124
+ detached: true
125
+ });
126
+ this.child.stdout?.pipe(process.stdout, { end: false });
127
+ this.child.stderr?.pipe(process.stderr, { end: false });
128
+ this.completion = new Promise(resolve => {
129
+ const finish = (exit) => {
130
+ if (this.result)
131
+ return;
132
+ this.result = exit;
133
+ resolve(exit);
134
+ };
135
+ this.child.once("error", error => finish({ code: null, signal: null, error }));
136
+ this.child.once("exit", (code, signal) => finish({ code, signal, error: null }));
137
+ });
138
+ }
139
+ exited() { return this.completion; }
140
+ exitResult() { return this.result; }
141
+ async stop() {
142
+ if (this.stopped)
143
+ return;
144
+ this.stopped = true;
145
+ if (!running(this.child))
146
+ return;
147
+ terminate(this.child, "SIGTERM");
148
+ await waitUntilStopped(this.child, 1_000);
149
+ if (running(this.child))
150
+ terminate(this.child, "SIGKILL");
151
+ await waitUntilStopped(this.child, 1_000);
152
+ }
153
+ }
154
+ async function availablePort() {
155
+ const server = createServer();
156
+ return await new Promise((done, fail) => {
157
+ server.once("error", fail);
158
+ server.listen(0, "localhost", () => {
159
+ const address = server.address();
160
+ if (!address || typeof address === "string") {
161
+ server.close();
162
+ fail(new Error("An available Client development port could not be selected"));
163
+ return;
164
+ }
165
+ server.close(error => error ? fail(error) : done(address.port));
166
+ });
167
+ });
168
+ }
169
+ async function assertAvailable(url) {
170
+ const location = new URL(url);
171
+ const occupied = await new Promise(done => {
172
+ const socket = connect({ host: location.hostname, port: portOf(url) });
173
+ let finished = false;
174
+ const finish = (value) => {
175
+ if (finished)
176
+ return;
177
+ finished = true;
178
+ socket.destroy();
179
+ done(value);
180
+ };
181
+ socket.setTimeout(500);
182
+ socket.once("connect", () => finish(true));
183
+ socket.once("error", () => finish(false));
184
+ socket.once("timeout", () => finish(false));
185
+ });
186
+ if (occupied)
187
+ throw new Error(`Client development URL is already in use: ${url}`);
188
+ }
189
+ function portOf(url) {
190
+ const location = new URL(url);
191
+ return Number(location.port || (location.protocol === "https:" ? 443 : 80));
192
+ }
193
+ function commandEnvironment(directory, additions) {
194
+ const key = Object.keys(process.env).find(name => name.toLowerCase() === "path") ?? "PATH";
195
+ const inherited = process.env[key];
196
+ return { ...process.env, ...additions, [key]: [join(directory, "node_modules", ".bin"), inherited].filter(Boolean).join(delimiter) };
197
+ }
198
+ function forwardAbort(source, target) {
199
+ if (!source)
200
+ return () => undefined;
201
+ const abort = () => target.abort(source.reason);
202
+ if (source.aborted)
203
+ abort();
204
+ else
205
+ source.addEventListener("abort", abort, { once: true });
206
+ return () => source.removeEventListener("abort", abort);
207
+ }
208
+ function commandFailure(exit) {
209
+ if (exit.error)
210
+ return new Error(`Client development command failed: ${exit.error.message}`);
211
+ if (exit.signal)
212
+ return new Error(`Client development command ended on ${exit.signal}`);
213
+ return new Error(`Client development command exited with ${exit.code ?? 0}`);
214
+ }
215
+ function terminate(child, signal) {
216
+ if (!child.pid)
217
+ return;
218
+ try {
219
+ process.kill(-child.pid, signal);
220
+ }
221
+ catch {
222
+ child.kill(signal);
223
+ }
224
+ }
225
+ function running(child) {
226
+ if (!child.pid)
227
+ return false;
228
+ try {
229
+ process.kill(-child.pid, 0);
230
+ return true;
231
+ }
232
+ catch {
233
+ return child.exitCode === null && child.signalCode === null;
234
+ }
235
+ }
236
+ async function waitUntilStopped(child, milliseconds) {
237
+ const deadline = Date.now() + milliseconds;
238
+ while (running(child) && Date.now() < deadline)
239
+ await new Promise(resolve => setTimeout(resolve, 20));
240
+ }
241
+ function pause(milliseconds, signal) {
242
+ return new Promise((done, fail) => {
243
+ if (signal.aborted) {
244
+ fail(signal.reason);
245
+ return;
246
+ }
247
+ const timeout = setTimeout(finish, milliseconds);
248
+ const abort = () => finish(signal.reason);
249
+ signal.addEventListener("abort", abort, { once: true });
250
+ function finish(error) {
251
+ clearTimeout(timeout);
252
+ signal.removeEventListener("abort", abort);
253
+ if (error !== undefined)
254
+ fail(error);
255
+ else
256
+ done();
257
+ }
258
+ });
259
+ }
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
- export { Client, ClientService, Endpoint, Process, Program, Server, ServerService, System, type ProgramProcessRunEvent, type ProgramProcessRunOptions } from "./system.js";
3
- export { Service, type ClientLaunch, type Launch, type ProgramPermission, type ProgramDefinition, type ServerLaunch, type ServiceKey, type SystemProgramStartup, type SystemStorage, } from "@phreshos/core";
2
+ export { ClientEndpoint, ClientService, Endpoint, Process, Program, ServerEndpoint, ServerService, System, type ProgramProcessRunEvent, type ProgramProcessRunOptions } from "./system.js";
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
- export { Client, ClientService, Endpoint, Process, Program, Server, ServerService, System } from "./system.js";
3
- export { Service, } from "@phreshos/core";
2
+ export { ClientEndpoint, ClientService, Endpoint, Process, Program, ServerEndpoint, ServerService, System } from "./system.js";
3
+ export { Service, clientPermissionCatalog, isPermissionName, } from "@phreshos/core";
4
4
  export { resolveHome } from "./home.js";
5
5
  export { Project } from "./project.js";
@@ -0,0 +1,10 @@
1
+ import type { ChildProcess } from "node:child_process";
2
+ /** The complete operating-system process tree beneath one shell command. */
3
+ export default class ProcessTree {
4
+ private readonly child;
5
+ private readonly ended;
6
+ private forcing;
7
+ constructor(child: ChildProcess, ended: (code: number | null, signal: NodeJS.Signals | null) => void | Promise<void>);
8
+ stop(): void;
9
+ private finish;
10
+ }
@@ -0,0 +1,67 @@
1
+ const terminationGrace = 1_000;
2
+ /** The complete operating-system process tree beneath one shell command. */
3
+ export default class ProcessTree {
4
+ child;
5
+ ended;
6
+ forcing = null;
7
+ constructor(child, ended) {
8
+ this.child = child;
9
+ this.ended = ended;
10
+ child.on("exit", (code, signal) => { this.finish(code, signal).catch(() => undefined); });
11
+ }
12
+ stop() {
13
+ signalTree(this.child, "SIGTERM");
14
+ if (this.forcing)
15
+ return;
16
+ this.forcing = setTimeout(() => signalTree(this.child, "SIGKILL"), terminationGrace);
17
+ this.forcing.unref();
18
+ }
19
+ async finish(code, signal) {
20
+ if (this.forcing)
21
+ clearTimeout(this.forcing);
22
+ this.forcing = null;
23
+ await finishTree(this.child);
24
+ await this.ended(code, signal);
25
+ }
26
+ }
27
+ function signalTree(child, signal) {
28
+ if (!child.pid)
29
+ return;
30
+ try {
31
+ process.kill(-child.pid, signal);
32
+ }
33
+ catch (error) {
34
+ if (error.code === "ESRCH")
35
+ return;
36
+ if (child.exitCode === null && child.signalCode === null)
37
+ child.kill(signal);
38
+ }
39
+ }
40
+ async function finishTree(child) {
41
+ const pid = child.pid;
42
+ if (!pid || !treeExists(pid))
43
+ return;
44
+ signalTree(child, "SIGTERM");
45
+ if (await waitForTree(pid))
46
+ return;
47
+ signalTree(child, "SIGKILL");
48
+ await waitForTree(pid);
49
+ }
50
+ async function waitForTree(pid) {
51
+ const began = Date.now();
52
+ while (treeExists(pid)) {
53
+ if (Date.now() - began >= terminationGrace)
54
+ return false;
55
+ await new Promise(resolve => setTimeout(resolve, 20));
56
+ }
57
+ return true;
58
+ }
59
+ function treeExists(pid) {
60
+ try {
61
+ process.kill(-pid, 0);
62
+ return true;
63
+ }
64
+ catch (error) {
65
+ return error.code !== "ESRCH";
66
+ }
67
+ }
@@ -1,13 +1,13 @@
1
- import type { ProgramPermission, ProgramSql, ProgramStore } from "@phreshos/core";
2
- type Request = (value: object) => Promise<unknown>;
1
+ import { type ProgramPermissions, type ProgramSql, type ProgramStore } from "@phreshos/core";
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;
11
- /** Persistent Program permission decisions carried through the owner-local Gateway. */
12
- export declare function programPermission(request: Request, handle: ProgramAddress): ProgramPermission;
10
+ export declare function programSql(call: Call, handle: ProgramAddress, database: "database" | "logs"): ProgramSql;
11
+ /** Program permission management carried through the owner-local Gateway. */
12
+ export declare function programPermissions(call: Call, handle: ProgramAddress): ProgramPermissions;
13
13
  export {};
@@ -1,14 +1,7 @@
1
+ import { parsePermission, parsePermissionChange, parsePermissions } from "@phreshos/core";
1
2
  /** Program-owned key-value storage carried through the owner-local Gateway. */
2
- export function programStore(request, handle) {
3
- const operate = (storeOperation, key, value, ttl) => request({
4
- capability: "program",
5
- operation: "store",
6
- handle,
7
- storeOperation,
8
- key,
9
- value,
10
- ttl
11
- });
3
+ export function programStore(call, handle) {
4
+ const operate = (storeOperation, key, value, ttl) => (call("/program/store", handle, storeOperation, key, value, ttl));
12
5
  return {
13
6
  get: (key) => operate("get", key),
14
7
  set: (key, value, ttl) => operate("set", key, value, ttl),
@@ -18,29 +11,22 @@ export function programStore(request, handle) {
18
11
  };
19
12
  }
20
13
  /** Program-owned SQL capability carried through the owner-local Gateway. */
21
- export function programSql(request, handle, database) {
14
+ export function programSql(call, handle, database) {
22
15
  return {
23
16
  query(statement, ...rest) {
24
17
  const [text, values] = written(statement, rest);
25
- return request({ capability: "program", operation: "query", handle, database, statement: text, values });
18
+ return call(`/program/${database}`, handle, text, values);
26
19
  }
27
20
  };
28
21
  }
29
- /** Persistent Program permission decisions carried through the owner-local Gateway. */
30
- export function programPermission(request, handle) {
31
- const operate = (permissionOperation, name, value) => request({
32
- capability: "program",
33
- operation: "permission",
34
- handle,
35
- permissionOperation,
36
- name,
37
- value
38
- });
22
+ /** Program permission management carried through the owner-local Gateway. */
23
+ export function programPermissions(call, handle) {
24
+ const operate = (permissionOperation, name, permission) => (call("/program/permissions", handle, permissionOperation, name, permission));
39
25
  return {
40
- get: name => operate("get", name),
41
- getAll: () => operate("getAll"),
42
- set: (name, value) => operate("set", name, value),
43
- delete: name => operate("delete", name)
26
+ async get(name) { return parsePermission(name, await operate("get", name)); },
27
+ async all() { return parsePermissions(await operate("all")); },
28
+ async set(name, permission) { return parsePermissionChange(name, await operate("set", name, permission)); },
29
+ async delete(name) { return parsePermissionChange(name, await operate("delete", name)); }
44
30
  };
45
31
  }
46
32
  function written(statement, rest) {
package/dist/project.d.ts CHANGED
@@ -18,9 +18,11 @@ export declare class Project {
18
18
  /** Run the optional author-owned production build command. */
19
19
  build(): Promise<void>;
20
20
  /** Build this Project and return its production Process lifecycle generator. */
21
- start(system: SystemContract, options?: ProjectRunOptions): Promise<AsyncGenerator<import("@phreshos/core").SystemProcessRunEvent, void, void>>;
22
- /** Return this Project's development Process lifecycle generator. */
23
- dev(system: SystemContract, options?: ProjectRunOptions): Promise<AsyncGenerator<import("@phreshos/core").SystemProcessRunEvent, void, void>>;
21
+ start(system: SystemContract, options?: ProjectRunOptions): Promise<AsyncGenerator<import("@phreshos/core").ProgramProcessRunEvent, void, void>>;
22
+ /** Prepare this Project's Client development source and return its Process lifecycle. */
23
+ dev(system: SystemContract, options?: ProjectRunOptions): Promise<AsyncGenerator<import("@phreshos/core").ProgramProcessRunEvent, void, void>>;
24
+ private developmentRun;
25
+ private prepareDevelopment;
24
26
  /** Build this Project and return its Program installation generator. */
25
27
  install(system: SystemContract): Promise<AsyncGenerator<Readonly<{
26
28
  stream: "stdout" | "stderr";