@phreshos/client 0.1.31 → 0.1.33

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/dist/domain.js CHANGED
@@ -1,35 +1,45 @@
1
- import { Client as CoreClient, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, Server as CoreServer } from "@phreshos/core";
1
+ import { ClientEndpoint as CoreClientEndpoint, Endpoint as CoreEndpoint, Process as CoreProcess, Program as CoreProgram, ServerEndpoint as CoreServerEndpoint } 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";
5
5
  import { area, sql, store } from "./storage.js";
6
+ import startup from "./startup.js";
7
+ import { programPermissions } from "./permissions.js";
6
8
  import wire from "./wire.js";
7
- import { currentProgramPermission } from "./permissions.js";
8
9
  const handles = new HandleRegistry();
9
10
  const ProgramBase = CoreProgram;
10
11
  const ProcessBase = CoreProcess;
11
- const ServerBase = CoreServer;
12
- const ClientBase = CoreClient;
12
+ const ServerEndpointBase = CoreServerEndpoint;
13
+ const ClientEndpointBase = CoreClientEndpoint;
13
14
  class ProgramHandle extends ProgramBase {
14
15
  identity;
15
16
  reference;
16
- data = area("data");
17
- cache = area("cache");
18
- store = store();
19
- logs = sql("logs");
20
- database = sql("database");
21
- permission = currentProgramPermission;
17
+ data;
18
+ cache;
19
+ store;
20
+ logs;
21
+ database;
22
+ startup;
22
23
  process;
24
+ permissions;
23
25
  record;
24
26
  constructor(record) {
25
27
  super();
26
28
  this.identity = record.identity;
27
29
  this.reference = record.reference;
28
30
  this.record = record;
29
- this.process = new ProgramProcessHandle(record.reference);
31
+ this.data = area(this.address, "data");
32
+ this.cache = area(this.address, "cache");
33
+ this.store = store(this.address);
34
+ this.logs = sql("logs", this.address);
35
+ this.database = sql("database", this.address);
36
+ this.startup = startup(this.address);
37
+ this.permissions = programPermissions(this.address);
38
+ this.process = new ProgramProcessHandle(this.address, record.reference);
30
39
  bindEvents(this, scoped("program-host", record.reference, programEvent));
31
40
  }
32
41
  get name() { return this.record.name; }
42
+ get assetId() { return this.record.assetId; }
33
43
  get version() { return this.record.version; }
34
44
  get description() { return this.record.description; }
35
45
  get hasAgent() { return this.record.hasAgent; }
@@ -39,31 +49,41 @@ class ProgramHandle extends ProgramBase {
39
49
  get client() {
40
50
  return this.record.client ? clientDeclaration(this.record.client) : null;
41
51
  }
52
+ get address() { return { identity: this.identity, reference: this.reference }; }
42
53
  update(record) {
43
54
  if (record.reference !== this.reference)
44
55
  throw new Error("A Program handle cannot become another Program");
45
56
  this.record = record;
46
57
  }
47
58
  async icon(size = "medium") {
48
- const answer = await wire.request(["icon", size]);
59
+ const answer = await wire.request(["icon", this.address, size]);
49
60
  return new Blob([Uint8Array.from(answer[0])], { type: "image/png" });
50
61
  }
51
62
  async agent() {
52
63
  if (!this.hasAgent)
53
64
  return null;
54
- const answer = await wire.request(["program-agent"]);
65
+ const answer = await wire.request(["program-agent", this.address]);
55
66
  return answer[0];
56
67
  }
57
68
  async installed() {
58
- const answer = await wire.request(["installed"]);
69
+ const answer = await wire.request(["installed", this.address]);
59
70
  return answer[0];
60
71
  }
72
+ async *install() {
73
+ for await (const value of wire.stream(["install", this.address])) {
74
+ yield programCommandChunk(value);
75
+ }
76
+ }
77
+ async fork(identity) {
78
+ const answer = await wire.request(["fork", this.address, identity]);
79
+ return program(answer[0]);
80
+ }
61
81
  async *uninstall(everything = false) {
62
- for await (const value of wire.stream(["uninstall", undefined, everything])) {
82
+ for await (const value of wire.stream(["uninstall", this.address, everything])) {
63
83
  yield programCommandChunk(value);
64
84
  }
65
85
  }
66
- async forget() { await wire.request(["forget"]); }
86
+ async forget() { await wire.request(["forget", this.address]); }
67
87
  }
68
88
  function programCommandChunk(value) {
69
89
  const chunk = value;
@@ -85,15 +105,18 @@ function clientDeclaration(record) {
85
105
  size: record.size,
86
106
  position: record.position,
87
107
  layer: record.layer,
88
- minimize: record.minimize
108
+ minimize: record.minimize,
109
+ permissions: Object.freeze(Object.fromEntries(Object.entries(record.permissions).map(([name, values]) => [name, Object.freeze([...values])])))
89
110
  });
90
111
  }
91
112
  class ProgramProcessHandle {
92
- constructor(program) {
93
- bindEvents(this, scoped("program-process", program, programProcessEvent));
113
+ address;
114
+ constructor(address, reference) {
115
+ this.address = address;
116
+ bindEvents(this, scoped("program-process", reference, programProcessEvent));
94
117
  }
95
118
  async list() {
96
- const answer = await wire.request(["program-process-list"]);
119
+ const answer = await wire.request(["program-process-list", this.address]);
97
120
  return answer[0].map(record => process(record));
98
121
  }
99
122
  async first() {
@@ -103,22 +126,42 @@ class ProgramProcessHandle {
103
126
  return chronological(await this.list()).at(-1) ?? null;
104
127
  }
105
128
  async find(identityOrName) {
106
- const answer = await wire.request(["program-process-find", identityOrName]);
129
+ const answer = await wire.request(["program-process-find", this.address, identityOrName]);
107
130
  return answer[0] ? process(answer[0]) : null;
108
131
  }
109
132
  async create(launch = {}) {
110
- const answer = await wire.request(["program-process-create", launch]);
133
+ const answer = await wire.request(["program-process-create", this.address, launch]);
111
134
  return process(answer[0]);
112
135
  }
136
+ async *run(launch = {}, options = {}) {
137
+ for await (const value of wire.stream(["run", this.address, launch], undefined, options.signal)) {
138
+ yield processRunEvent(value);
139
+ }
140
+ }
113
141
  async findOrCreate(launch) {
114
- const answer = await wire.request(["program-process-find-or-create", launch]);
142
+ const answer = await wire.request(["program-process-find-or-create", this.address, launch]);
115
143
  return process(answer[0]);
116
144
  }
117
145
  async exitAll() {
118
- const answer = await wire.request(["program-process-exit-all"]);
146
+ const answer = await wire.request(["program-process-exit-all", this.address]);
119
147
  return answer[0];
120
148
  }
121
149
  }
150
+ function processRunEvent(value) {
151
+ const event = value;
152
+ if (event?.event === "started" && event.process) {
153
+ return Object.freeze({ event: "started", process: process(event.process) });
154
+ }
155
+ if (event?.event === "output" && (event.stream === "stdout" || event.stream === "stderr") && typeof event.text === "string") {
156
+ return Object.freeze({ event: "output", stream: event.stream, text: event.text });
157
+ }
158
+ if (event?.event === "exited" && event.process) {
159
+ const code = typeof event.exit?.code === "number" ? event.exit.code : null;
160
+ const signal = typeof event.exit?.signal === "string" ? event.exit.signal : null;
161
+ return Object.freeze({ event: "exited", process: process(event.process), exit: exit(code, signal) });
162
+ }
163
+ throw new Error("The System returned an invalid Process run event");
164
+ }
122
165
  function chronological(processes) {
123
166
  return processes.sort((left, right) => left.startedAt.getTime() - right.startedAt.getTime());
124
167
  }
@@ -202,7 +245,7 @@ export class ServerTrafficHandle extends TrafficHandle {
202
245
  }, impossible);
203
246
  }
204
247
  }
205
- class ServerHandle extends ServerBase {
248
+ class ServerEndpointHandle extends ServerEndpointBase {
206
249
  owner;
207
250
  traffic;
208
251
  lifecycle;
@@ -222,7 +265,7 @@ class ServerHandle extends ServerBase {
222
265
  async start(launch = {}) { await wire.request(["start-endpoint", this.owner.address, "server", launch]); }
223
266
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "server"]); }
224
267
  async isService() { return (await wire.request(["is-service", "server", this.owner.address]))[0]; }
225
- async waitReady(timeout) { await wire.request(["wait-ready", this.owner.address], timeout); }
268
+ async waitReady(timeout) { await wire.request(["wait-ready", this.owner.address, "server"], timeout); }
226
269
  async ask(event, payload = undefined) {
227
270
  return this.askWithin(undefined, event, payload);
228
271
  }
@@ -231,7 +274,7 @@ class ServerHandle extends ServerBase {
231
274
  }
232
275
  async askWithin(timeout, event, payload) {
233
276
  const deadline = new Deadline(timeout);
234
- await wire.requestWithin(["wait-ready", this.owner.address, true], deadline);
277
+ await wire.requestWithin(["wait-ready", this.owner.address, "server", true], deadline);
235
278
  const source = await wire.identity();
236
279
  const address = `client:${source.process}:${crypto.randomUUID()}`;
237
280
  const questionId = crypto.randomUUID();
@@ -245,7 +288,7 @@ class ServerHandle extends ServerBase {
245
288
  }
246
289
  }
247
290
  }
248
- class ClientHandle extends ClientBase {
291
+ class ClientEndpointHandle extends ClientEndpointBase {
249
292
  owner;
250
293
  traffic;
251
294
  lifecycle;
@@ -267,14 +310,13 @@ class ClientHandle extends ClientBase {
267
310
  async start(launch = {}) { await wire.request(["start-endpoint", this.owner.address, "client", launch]); }
268
311
  async stop() { await wire.request(["stop-endpoint", this.owner.address, "client"]); }
269
312
  async isService() { return (await wire.request(["is-service", "client", this.owner.address]))[0]; }
313
+ async waitReady(timeout) { await wire.request(["wait-ready", this.owner.address, "client"], timeout); }
270
314
  }
271
315
  class WindowHandle extends Events {
272
316
  target;
273
- local;
274
317
  constructor(target) {
275
318
  super(...deferredScoped("host-end", target, (_event, values) => values[0]));
276
319
  this.target = target;
277
- this.local = new LocalWindowHandle(target);
278
320
  }
279
321
  async state() {
280
322
  const answer = await wire.request(["window", await this.target()]);
@@ -296,39 +338,21 @@ class WindowHandle extends Events {
296
338
  }
297
339
  class LocalWindowHandle {
298
340
  target;
299
- surface;
300
- constructor(target) {
341
+ selected;
342
+ constructor(target, selected) {
301
343
  this.target = target;
302
- this.surface = new LocalWindowSurfaceHandle(target);
344
+ this.selected = selected;
303
345
  }
304
- async state() {
305
- const answer = await wire.request(["windowLocal", await this.target()]);
306
- return answer[0];
346
+ transaction(transaction) {
347
+ return new LocalWindowHandle(this.target, transaction);
307
348
  }
308
- async title() { return (await this.state()).title; }
309
- async position() { return (await this.state()).position; }
310
- async size() { return (await this.state()).size; }
311
- async minimized() { return (await this.state()).minimized; }
312
- async front() { return (await this.state()).front; }
313
- async layer() { return (await this.state()).layer; }
314
- async location() { return (await this.state()).location; }
315
- async move(position, transaction) { await wire.request(["windowLocalMove", await this.target(), position, transaction]); }
316
- async resize(size, transaction) { await wire.request(["windowLocalResize", await this.target(), size, transaction]); }
317
- async setGeometry(geometry, transaction) { await wire.request(["windowLocalGeometry", await this.target(), geometry, transaction]); }
318
- async minimize(minimized = true) { await wire.request(["windowLocalMinimize", await this.target(), minimized]); }
319
- async changeTitle(title) { await wire.request(["windowLocalTitle", await this.target(), title]); }
320
- async raise() { await wire.request(["windowLocalRaise", await this.target()]); }
321
- }
322
- class LocalWindowSurfaceHandle {
323
- target;
324
- constructor(target) {
325
- this.target = target;
326
- }
327
- async set(transition) {
328
- await wire.request(["windowLocalSurfaceSet", await this.target(), transition]);
329
- }
330
- async remove(transition) {
331
- await wire.request(["windowLocalSurfaceRemove", await this.target(), transition]);
349
+ async addSurface() { await this.change("windowLocalSurfaceAdd"); }
350
+ async removeSurface() { await this.change("windowLocalSurfaceRemove"); }
351
+ async move(position) { await this.change("windowLocalMove", position); }
352
+ async resize(size) { await this.change("windowLocalResize", size); }
353
+ async setGeometry(geometry) { await this.change("windowLocalGeometry", geometry); }
354
+ async change(operation, value) {
355
+ await wire.request([operation, await this.target(), value, this.selected]);
332
356
  }
333
357
  }
334
358
  function deferredScoped(route, target, convert) {
@@ -472,12 +496,15 @@ export function claimEndpoint(reference, kind, endpoint) {
472
496
  return handles.adopt(`endpoint:${reference}:${kind}`, endpoint);
473
497
  }
474
498
  function endpointHandle(owner, kind, preferred) {
475
- return handles.obtain(`endpoint:${owner.reference}:${kind}`, () => preferred ?? (kind === "server" ? new ServerHandle(owner) : new ClientHandle(owner)));
499
+ return handles.obtain(`endpoint:${owner.reference}:${kind}`, () => preferred ?? (kind === "server" ? new ServerEndpointHandle(owner) : new ClientEndpointHandle(owner)));
476
500
  }
477
501
  export function window(target) {
478
502
  return new WindowHandle(target);
479
503
  }
480
- /** Resolves only endpoint identities intentionally visible to this Client. */
504
+ export function localWindow(target) {
505
+ return new LocalWindowHandle(target);
506
+ }
507
+ /** Resolves an Endpoint reference through the Client Endpoint's global handle registry. */
481
508
  export function visibleEndpoint(reference) {
482
509
  if (reference === null)
483
510
  return null;
@@ -489,7 +516,7 @@ export const Program = CoreProgram;
489
516
  export const Process = CoreProcess;
490
517
  /** Runtime constructor shared by all client-visible Endpoint handles. */
491
518
  export const Endpoint = CoreEndpoint;
492
- /** Runtime constructor shared by all client-visible Server handles. */
493
- export const Server = CoreServer;
494
- /** Runtime constructor shared by all client-visible Client handles. */
495
- export const Client = CoreClient;
519
+ /** Runtime constructor shared by all client-visible Server Endpoint handles. */
520
+ export const ServerEndpoint = CoreServerEndpoint;
521
+ /** Runtime constructor shared by all client-visible Client Endpoint handles. */
522
+ export const ClientEndpoint = CoreClientEndpoint;
package/dist/events.d.ts CHANGED
@@ -1,19 +1,17 @@
1
- import type { Capture, Cleanup, EventOptions } from "@phreshos/core";
1
+ import type { Cleanup, EventOptions, Subscribable } from "@phreshos/core";
2
2
  export declare const defaultTimeout = 10000;
3
3
  type Failure = (error: Error) => void;
4
4
  type Listener = (message: unknown) => unknown;
5
5
  type EveryListener = (event: string, message: unknown) => unknown;
6
6
  type Register<Message> = (listener: (message: Message) => unknown, impossible?: Failure) => Cleanup;
7
7
  /** SDK-owned waits, queues and callbacks over boundary forwarding state. */
8
- export default class Events {
8
+ export default class Events<Vocabulary extends object = {}, Fallback = unknown> implements Subscribable<Vocabulary, Fallback> {
9
9
  private readonly listen;
10
10
  private readonly listenAll;
11
11
  constructor(listen: (event: string, listener: Listener, impossible?: Failure) => Cleanup, listenAll: (listener: EveryListener, impossible?: Failure) => Cleanup);
12
- subscribe(event: string, subscriber: Listener): Cleanup;
13
- subscribe(subscriber: (capture: Capture<string, unknown>) => unknown): Cleanup;
14
- waitFor(event: string, timeout?: number): Promise<unknown>;
15
- events(event: string, options?: EventOptions): AsyncIterableIterator<unknown>;
16
- events(options?: EventOptions): AsyncIterableIterator<Capture<string, unknown>>;
12
+ readonly subscribe: Subscribable<Vocabulary, Fallback>["subscribe"];
13
+ readonly waitFor: Subscribable<Vocabulary, Fallback>["waitFor"];
14
+ readonly events: Subscribable<Vocabulary, Fallback>["events"];
17
15
  }
18
16
  /** Converts one persistent registration into a bounded asynchronous iterator. */
19
17
  export declare function stream<Message>(register: Register<Message>, options?: EventOptions): AsyncIterableIterator<Message>;
package/dist/events.js CHANGED
@@ -7,12 +7,12 @@ export default class Events {
7
7
  this.listen = listen;
8
8
  this.listenAll = listenAll;
9
9
  }
10
- subscribe(eventOrSubscriber, subscriber) {
10
+ subscribe = ((eventOrSubscriber, subscriber) => {
11
11
  if (typeof eventOrSubscriber === "string")
12
12
  return this.listen(eventOrSubscriber, subscriber);
13
13
  return this.listenAll((event, message) => eventOrSubscriber({ event, message }));
14
- }
15
- waitFor(event, timeout = defaultTimeout) {
14
+ });
15
+ waitFor = ((event, timeout = defaultTimeout) => {
16
16
  return new Promise((resolve, reject) => {
17
17
  let settled = false;
18
18
  let stop = () => undefined;
@@ -27,13 +27,13 @@ export default class Events {
27
27
  const timer = setTimeout(() => finish(() => reject(new Error(`Event promise timeout ${timeout}ms`))), timeout);
28
28
  stop = this.listen(event, message => finish(() => resolve(message)), error => finish(() => reject(error)));
29
29
  });
30
- }
31
- events(eventOrOptions = {}, namedOptions = {}) {
30
+ });
31
+ events = ((eventOrOptions = {}, namedOptions = {}) => {
32
32
  if (typeof eventOrOptions === "string") {
33
33
  return stream((listener, impossible) => this.listen(eventOrOptions, listener, impossible), namedOptions);
34
34
  }
35
35
  return stream((listener, impossible) => this.listenAll((event, message) => listener({ event, message }), impossible), eventOrOptions);
36
- }
36
+ });
37
37
  }
38
38
  /** Converts one persistent registration into a bounded asynchronous iterator. */
39
39
  export function stream(register, options = {}) {
package/dist/main.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { system, type System } from "./system.js";
2
- export { type SystemDesktop } from "./desktop/desktop.js";
1
+ export { system } from "./system.js";
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
4
  export { ClientService, ServerService, Service, type ServiceKey, } from "@phreshos/core";
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";
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";
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";
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { system } from "./system.js";
2
- export {} from "./desktop/desktop.js";
2
+ export { desktop } from "./desktop/desktop.js";
3
3
  export { context } from "./context.js";
4
4
  export { ClientService, ServerService, Service, } from "@phreshos/core";
5
- export { Client, Endpoint, Process, Program, Server } from "./domain.js";
5
+ export { ClientEndpoint, Endpoint, Process, Program, ServerEndpoint } from "./domain.js";
@@ -1,10 +1,7 @@
1
- import type { Permission, PermissionDecision, PermissionName, TimedPermission } from "@phreshos/core";
2
- /** Client access to the current Program's effective permission decisions. */
3
- export default class ClientPermission implements Permission {
4
- granted(name: PermissionName): Promise<PermissionDecision>;
5
- request(name: PermissionName): Promise<PermissionDecision>;
6
- timeout(milliseconds: number): TimedPermission;
7
- private requestWithin;
8
- }
9
- /** The one Program-owned permission capability visible in this isolated Client. */
10
- export declare const currentProgramPermission: ClientPermission;
1
+ import type { ContextPermissions, ProgramPermissions } from "@phreshos/core";
2
+ import type { HandleAddress } from "./domain.js";
3
+ export declare const defaultPermissionTimeout = 120000;
4
+ /** Bind authoritative stored grants to one exact Program handle. */
5
+ export declare function programPermissions(program: HandleAddress): ProgramPermissions;
6
+ /** Stored grants and owner requests belonging to the current Client Endpoint. */
7
+ export declare function contextPermissions(): ContextPermissions;
@@ -1,23 +1,36 @@
1
+ import { parsePermission, parsePermissionChange, parsePermissions } from "@phreshos/core";
1
2
  import wire from "./wire.js";
2
- const defaultPermissionTimeout = 30_000;
3
- /** Client access to the current Program's effective permission decisions. */
4
- export default class ClientPermission {
5
- async granted(name) {
6
- const answer = await wire.request(["permission-granted", name]);
7
- return answer[0];
8
- }
9
- request(name) {
10
- return this.requestWithin(name, defaultPermissionTimeout);
11
- }
12
- timeout(milliseconds) {
13
- if (!Number.isFinite(milliseconds) || milliseconds < 0)
14
- throw new Error("A permission timeout must be a non-negative finite number");
15
- return Object.freeze({ request: (name) => this.requestWithin(name, milliseconds) });
16
- }
17
- async requestWithin(name, milliseconds) {
18
- const answer = await wire.requestOrNull(["permission-request", name], milliseconds);
19
- return answer?.[0] ?? null;
20
- }
3
+ export const defaultPermissionTimeout = 120_000;
4
+ /** Bind authoritative stored grants to one exact Program handle. */
5
+ export function programPermissions(program) {
6
+ const operate = (operation, name, permission) => (wire.request(["program-permissions", program, operation, name, permission]));
7
+ return {
8
+ async get(name) { return parsePermission((await operate("get", name))[0]); },
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]); }
12
+ };
13
+ }
14
+ /** Stored grants and owner requests belonging to the current Client Endpoint. */
15
+ export function contextPermissions() {
16
+ const timed = (timeout) => ({
17
+ async request(name, permission = true) {
18
+ const identity = crypto.randomUUID();
19
+ const result = await wire.requestOrNull(["context-permission-request", identity, name, permission], timeout);
20
+ return result === null
21
+ ? Object.freeze({ permission: null, needReload: false })
22
+ : parsePermissionChange(result[0]);
23
+ }
24
+ });
25
+ return {
26
+ async get(name) {
27
+ return parsePermission((await wire.request(["context-permission-get", name]))[0]);
28
+ },
29
+ request: timed(defaultPermissionTimeout).request,
30
+ timeout(milliseconds) {
31
+ if (!Number.isFinite(milliseconds) || milliseconds < 0)
32
+ throw new Error("A permission timeout must be a non-negative finite number");
33
+ return timed(milliseconds);
34
+ }
35
+ };
21
36
  }
22
- /** The one Program-owned permission capability visible in this isolated Client. */
23
- export const currentProgramPermission = new ClientPermission();
package/dist/service.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { type ClientService, type ServerService, type Service, type ServiceKey } from "@phreshos/core";
2
- export declare function prepareService<EventsMap extends object = {}, Fallback = never>(key: ServiceKey & {
2
+ export declare function prepareService<EventsMap extends object = {}, Fallback = unknown>(key: ServiceKey & {
3
3
  endpoint: "server";
4
4
  }): ServerService<EventsMap, Fallback>;
5
- export declare function prepareService<EventsMap extends object = {}, Fallback = never>(key: ServiceKey & {
5
+ export declare function prepareService<EventsMap extends object = {}, Fallback = unknown>(key: ServiceKey & {
6
6
  endpoint: "client";
7
7
  }): ClientService<EventsMap, Fallback>;
8
8
  export declare function prepareService(key: ServiceKey): Service;
package/dist/service.js CHANGED
@@ -20,6 +20,9 @@ class ServiceHandle {
20
20
  const answer = await wire.request(["service-exists", this.key]);
21
21
  return answer[0];
22
22
  }
23
+ async waitReady(timeout) {
24
+ await wire.request(["service-wait-ready", this.key, timeout], timeout);
25
+ }
23
26
  }
24
27
  class ServerHandler extends ServerServiceBase {
25
28
  key;
@@ -34,9 +37,7 @@ class ServerHandler extends ServerServiceBase {
34
37
  }
35
38
  publish(event, payload = undefined) { this.service.publish(event, payload); }
36
39
  exists() { return this.service.exists(); }
37
- async waitReady(timeout) {
38
- await wire.request(["service-wait-ready", this.key, timeout], timeout);
39
- }
40
+ waitReady(timeout) { return this.service.waitReady(timeout); }
40
41
  async ask(event, payload = undefined) {
41
42
  return await this.askWithin(new Deadline(), event, payload);
42
43
  }
@@ -68,6 +69,7 @@ class ClientHandler extends ClientServiceBase {
68
69
  }
69
70
  publish(event, payload = undefined) { this.service.publish(event, payload); }
70
71
  exists() { return this.service.exists(); }
72
+ waitReady(timeout) { return this.service.waitReady(timeout); }
71
73
  }
72
74
  export function prepareService(key) {
73
75
  if (!isServiceKey(key))
@@ -84,11 +86,11 @@ export function prepareService(key) {
84
86
  }
85
87
  function serviceEvents(key, scope) {
86
88
  return [
87
- (event, listener) => wire.followService(key, scope, event, listener),
88
- (listener) => wire.followService(key, scope, null, (event, payload) => {
89
+ (event, listener, impossible) => wire.followService(key, scope, event, listener, impossible),
90
+ (listener, impossible) => wire.followService(key, scope, null, (event, payload) => {
89
91
  if (typeof event === "string")
90
92
  listener(event, payload);
91
- })
93
+ }, impossible)
92
94
  ];
93
95
  }
94
96
  function bindEvents(target, events) {
@@ -0,0 +1,4 @@
1
+ import type { ProgramStartup } from "@phreshos/core";
2
+ import type { HandleAddress } from "./domain.js";
3
+ /** Bind persistent startup operations to one Program handle. */
4
+ export default function startup(program: HandleAddress): ProgramStartup;
@@ -0,0 +1,16 @@
1
+ import wire from "./wire.js";
2
+ /** Bind persistent startup operations to one Program handle. */
3
+ export default function startup(program) {
4
+ return {
5
+ async get() {
6
+ const answer = await wire.request(["startup", program, "get"]);
7
+ return answer[0];
8
+ },
9
+ async enable(launch = {}) {
10
+ await wire.request(["startup", program, "enable", launch]);
11
+ },
12
+ async disable() {
13
+ await wire.request(["startup", program, "disable"]);
14
+ }
15
+ };
16
+ }
package/dist/storage.d.ts CHANGED
@@ -1,5 +1,9 @@
1
- import type { ProgramSql, ProgramStore, Storage } from "@phreshos/core";
2
- /** Client-side Program storage, structurally scoped by the iframe boundary. */
3
- export declare function area(which: "data" | "cache"): Storage;
4
- export declare function store(): ProgramStore;
5
- export declare function sql(kind: "database" | "logs"): ProgramSql;
1
+ import type { ProgramSql, ProgramStore, Storage as CoreStorage } from "@phreshos/core";
2
+ import type { HandleAddress } from "./domain.js";
3
+ export type Storage = CoreStorage;
4
+ /** Client-side access to one exact Program storage area. */
5
+ export declare function area(program: HandleAddress, which: "data" | "cache"): Storage;
6
+ export declare function store(program: HandleAddress): ProgramStore;
7
+ export declare function sql(kind: "database" | "logs", program: HandleAddress): ProgramSql;
8
+ /** Access to the System's native storage through the Client boundary. */
9
+ export declare function systemStorage(): Storage;